Merge commit 'pre_3x_merge' into R4_1_maintenance

Conflicts:
	bundles/org.eclipse.ui.workbench/.classpath
	bundles/org.eclipse.ui.workbench/.options
	bundles/org.eclipse.ui.workbench/.settings/org.eclipse.jdt.core.prefs
	bundles/org.eclipse.ui.workbench/.settings/org.eclipse.jdt.ui.prefs
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IAggregateWorkingSet.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IEditorRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IMemento.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IPageLayout.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IPageService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IPartListener2.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IPartService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IPersistableEditor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/ISaveablePart2.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbench.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchCommandConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPartReference.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPreferenceConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPropertyPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkbenchPropertyPageMulti.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/IWorkingSetManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/Saveable.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/SubActionBars.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/XMLMemento.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/actions/ActionFactory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/actions/CompoundContributionItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/actions/ContributedAction.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/actions/ContributionItemFactory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/actions/NewWizardDropDownAction.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/actions/WorkingSetFilterActionGroup.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/application/ActionBarAdvisor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/application/IWorkbenchWindowConfigurer.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/application/WorkbenchAdvisor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/application/WorkbenchWindowAdvisor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/branding/package.html
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/commands/package.html
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/contexts/IContextService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/EditorSelectionDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/FilteredItemsSelectionDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/PropertyDialogAction.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/PropertyPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/SearchPattern.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/TypeFilteringDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/dialogs/WorkingSetConfigurationBlock.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/fieldassist/ContentAssistCommandAdapter.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/handlers/HandlerUtil.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/handlers/ShowPerspectiveHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/handlers/ShowViewHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/AbstractWorkingSetManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ActivateEditorHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/AnimatedTabFeedback.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ChangeToPerspectiveMenu.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/CloseAllHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/CycleBaseHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/CycleEditorHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/CycleViewHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/DragCursors.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorActionBars.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorHistory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorHistoryItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorMenuManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorReference.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorSite.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/EditorSiteDragAndDropServiceImpl.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ErrorEditorPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ErrorViewPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ExtensionEventHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/FaderAnimationFeedback.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/HeapStatus.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/IPreferenceConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/IWorkbenchConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/IWorkbenchHelpContextIds.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/JFaceUtil.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/KeyBindingService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/NavigationHistory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PartPane.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PartService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PartSite.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PerspectiveSwitcher.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PlatformUIPreferenceListener.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PluginActionSet.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/PopupMenuExtender.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ProductInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ProductProperties.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ReopenEditorMenu.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/SaveableHelper.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/SaveablesList.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowFastViewContribution.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowInHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowInMenu.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowPartPaneMenuHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowViewAction.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowViewMenu.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ShowViewMenuHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/SwitchToWindowMenu.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/UILockListener.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ViewActionBars.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ViewIntroAdapterPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ViewReference.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/ViewSite.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/Workbench.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchConfigurer.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchImages.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchIntroManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchLayoutSettingsTransfer.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchMessages.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchPartReference.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchPlugin.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchWindow.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbenchWindowConfigurer.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkbookEditorsHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/WorkingSetManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/about/AboutBundleData.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/about/AboutFeaturesPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/about/BundleSigningInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/activities/ExtensionActivityRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/activities/MutableActivityManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/activities/ws/messages.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/browser/DefaultWorkbenchBrowserSupport.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/commands/CommandImagePersistence.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/commands/CommandLegacyWrapper.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/commands/CommandPersistence.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/commands/CommandService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/commands/WorkbenchCommandSupport.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/contexts/ContextPersistence.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/contexts/ContextService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/contexts/WorkbenchContextSupport.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/decorators/DecorationScheduler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/decorators/DecoratorDefinition.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/decorators/DecoratorManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/AboutDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/EditorsPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/FileEditorsPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/FilteredPreferenceDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/ImportExportPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/NewWizard.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/NewWizardNewPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PerspectivesPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PreferenceBoldLabelProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyPageContributorManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/PropertyPageNode.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/RegistryPageContributor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/SelectPerspectiveDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/ShowViewDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/StartupPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/ViewComparator.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/ViewLabelProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/ViewsPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/WizardActivityFilter.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/WorkbenchEditorsDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/WorkbenchPreferenceDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/WorkbenchPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/WorkbenchWizardElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dialogs/WorkingSetSelectionDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/dnd/SwtUtil.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/expressions/LegacyActionSetExpression.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/ActionDelegateHandlerProxy.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/ClosePerspectiveHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/CyclePageHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/HandlerActivation.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/NewEditorHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/QuitHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/ReuseEditorTester.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/ShowPreferencePageHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/TraversePageHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/WidgetMethodHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/handlers/WizardHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/help/WorkbenchHelpSystem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/AbstractKeyFormatter.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/BindingPersistence.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/BindingService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/KeyAssistDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/KeysPreferenceFiltersDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/MacKeyFormatter.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/NativeKeyFormatter.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/NewKeysPreferenceMessages.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/NewKeysPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/NewKeysPreferencePage.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/WorkbenchKeyboard.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/model/ContextModel.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/keys/model/KeyController.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/layout/IWindowTrim.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/layout/TrimCommonUIHandle.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/layout/TrimToolBarBase.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/DynamicMenuContributionItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/DynamicToolBarContributionItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/InternalControlContribution.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/LegacyActionPersistence.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/MenuAdditionCacheEntry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/MenuPersistence.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/WorkbenchMenuService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/menus/messages.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/messages.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/misc/Policy.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/part/NullEditorInput.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/part/StatusPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/preferences/WorkbenchPreferenceExtensionNode.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/BlockedJobsDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/DetailedProgressViewer.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/ErrorInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/FinishedJobs.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/GroupInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/JobInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/JobTreeElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/ProgressAnimationItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/ProgressInfoItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/ProgressManagerUtil.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/ProgressMonitorFocusJobDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/ProgressView.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/SubTaskInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/progress/WorkbenchSiteProgressService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/ActionElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/ActionProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/CommandElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/CommandProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/EditorElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/EditorProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/PerspectiveProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/PreferenceElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/PreferenceProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/PropertiesProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/QuickAccessDialog.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/QuickAccessHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/QuickAccessMessages.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/QuickAccessProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/ViewElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/ViewProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/WizardElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/WizardProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/quickaccess/messages.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/ActionSetDescriptor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/EditorRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/IWorkbenchRegistryConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/PerspectiveDescriptor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/PerspectiveRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/PropertyPagesRegistryReader.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/UIExtensionTracker.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/ViewDescriptor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/registry/ViewRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/EvaluationReference.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/EvaluationService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/ServiceLocator.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/SourcePriorityNameMapping.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/WorkbenchServiceRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/services/WorkbenchSourceProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/statushandlers/DefaultDetailsArea.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/statushandlers/IStatusDialogConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/statushandlers/StackTraceSupportArea.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/testing/WorkbenchPartTestable.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/CascadingColorRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/CascadingFontRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/CascadingMap.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/CascadingTheme.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ColorsAndFontsPreferencePage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ColorsAndFontsPreferencePage.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/FontDefinition.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ICategorizedThemeElementDefinition.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/IEditable.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/IHierarchalThemeElementDefinition.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/IThemeDescriptor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/IThemeElementDefinition.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/IThemeRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/LightColorFactory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/RGBContrastFactory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/Theme.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/Theme.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ThemeDescriptor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ThemeElementCategory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ThemeElementHelper.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ThemeRegistry.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ThemeRegistryReader.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/ThemeRegistryReader.properties
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/WorkbenchPreview.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/themes/WorkbenchThemeManager.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/tweaklets/TabBehaviour.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/tweaklets/TabBehaviourMRU.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/tweaklets/TitlePathUpdater.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/util/PrefUtil.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/util/Util.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/wizards/preferences/WizardPreferencesExportPage1.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/wizards/preferences/WizardPreferencesImportPage1.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/wizards/preferences/WizardPreferencesPage.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/intro/IIntroPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/keys/SWTKeySupport.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/menus/AbstractContributionFactory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/menus/CommandContributionItem.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/menus/ExtensionContributionFactory.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/menus/MenuUtil.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/menus/UIElement.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/model/IWorkbenchAdapter3.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/model/WorkbenchAdapter.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/model/WorkbenchLabelProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/operations/RedoActionHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/operations/UndoActionHandler.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/AbstractMultiEditor.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/IShowInSource.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/IShowInTarget.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/IShowInTargetList.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/MultiPageEditorPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/MultiPageEditorSite.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/PageBook.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/PageBookView.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/PageSite.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/part/WorkbenchPart.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/plugin/AbstractUIPlugin.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/progress/IProgressConstants.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/progress/IProgressConstants2.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/progress/IWorkbenchSiteProgressService.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/services/package.html
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/statushandlers/AbstractStatusAreaProvider.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/testing/ContributionInfo.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/testing/IWorkbenchPartTestable.java
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/testing/package.html
	bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/themes/ColorUtil.java
	bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF
	bundles/org.eclipse.ui.workbench/build.properties
	bundles/org.eclipse.ui.workbench/plugin.properties
	bundles/org.eclipse.ui.workbench/plugin.xml
diff --git a/bundles/org.eclipse.core.databinding.beans/.classpath b/bundles/org.eclipse.core.databinding.beans/.classpath
new file mode 100644
index 0000000..ce73933
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.core.databinding.beans/.cvsignore b/bundles/org.eclipse.core.databinding.beans/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.core.databinding.beans/.project b/bundles/org.eclipse.core.databinding.beans/.project
new file mode 100644
index 0000000..85f5bb3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.core.databinding.beans</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..27f8f29
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,354 @@
+#Thu Feb 05 11:35:54 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..5a34f28
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:05:53 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.3\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.pde.prefs b/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..4ea08b3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:51:14 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=0
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=0
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.core.databinding.beans/.settings/org.moreunit.prefs b/bundles/org.eclipse.core.databinding.beans/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..8951bec
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/.settings/org.moreunit.prefs
@@ -0,0 +1,5 @@
+#Tue Feb 02 23:25:15 MST 2010
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=org.eclipse.core.databinding.beans\:src\:org.eclipse.jface.tests.databinding\:src
+org.moreunit.useprojectsettings=true
diff --git a/bundles/org.eclipse.core.databinding.beans/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.databinding.beans/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..da39f50
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.core.databinding.beans
+Bundle-Version: 1.2.100.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.core.databinding.beans,
+ org.eclipse.core.internal.databinding.beans;x-internal:=true
+Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.databinding.observable;bundle-version="[1.2.0,2.0.0)",
+ org.eclipse.core.databinding.property;bundle-version="[1.3.0,2.0.0)"
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/bundles/org.eclipse.core.databinding.beans/about.html b/bundles/org.eclipse.core.databinding.beans/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.beans/build.properties b/bundles/org.eclipse.core.databinding.beans/build.properties
new file mode 100644
index 0000000..6f0a513
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# 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
+###############################################################################
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+output.databinding.jar = bin/
+src.includes = about.html
+source.. = src/
diff --git a/bundles/org.eclipse.core.databinding.beans/plugin.properties b/bundles/org.eclipse.core.databinding.beans/plugin.properties
new file mode 100644
index 0000000..083e360
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# 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
+###############################################################################
+pluginName = JFace Data Binding for JavaBeans
+providerName = Eclipse.org
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/BeanProperties.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/BeanProperties.java
new file mode 100644
index 0000000..bbd9f8b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/BeanProperties.java
@@ -0,0 +1,400 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 247997, 261843, 264307
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousBeanListProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousBeanMapProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousBeanSetProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousBeanValueProperty;
+import org.eclipse.core.internal.databinding.beans.BeanListProperty;
+import org.eclipse.core.internal.databinding.beans.BeanListPropertyDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanMapProperty;
+import org.eclipse.core.internal.databinding.beans.BeanMapPropertyDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
+import org.eclipse.core.internal.databinding.beans.BeanSetProperty;
+import org.eclipse.core.internal.databinding.beans.BeanSetPropertyDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanValueProperty;
+import org.eclipse.core.internal.databinding.beans.BeanValuePropertyDecorator;
+
+/**
+ * A factory for creating properties for Java objects that conform to the <a
+ * href="http://java.sun.com/products/javabeans/docs/spec.html">JavaBean
+ * specification</a> for bound properties.
+ * 
+ * @since 1.2
+ */
+public class BeanProperties {
+	/**
+	 * Returns a value property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains null.
+	 * 
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @return a value property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(String propertyName) {
+		return value(null, propertyName, null);
+	}
+
+	/**
+	 * Returns a value property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains null.
+	 * 
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param valueType
+	 *            the value type of the returned value property
+	 * @return a value property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(String propertyName, Class valueType) {
+		return value(null, propertyName, valueType);
+	}
+
+	/**
+	 * Returns a value property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @return a value property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(Class beanClass, String propertyName) {
+		return value(beanClass, propertyName, null);
+	}
+
+	/**
+	 * Returns a value property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param valueType
+	 *            the value type of the returned value property
+	 * @return a value property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(Class beanClass,
+			String propertyName, Class valueType) {
+		String[] propertyNames = split(propertyName);
+		if (propertyNames.length > 1)
+			valueType = null;
+
+		PropertyDescriptor propertyDescriptor;
+		IValueProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousBeanValueProperty(propertyNames[0],
+					valueType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyNames[0]);
+			property = new BeanValueProperty(propertyDescriptor, valueType);
+		}
+
+		IBeanValueProperty beanProperty = new BeanValuePropertyDecorator(
+				property, propertyDescriptor);
+		for (int i = 1; i < propertyNames.length; i++) {
+			beanProperty = beanProperty.value(propertyNames[i]);
+		}
+		return beanProperty;
+	}
+
+	private static String[] split(String propertyName) {
+		if (propertyName.indexOf('.') == -1)
+			return new String[] { propertyName };
+		List propertyNames = new ArrayList();
+		int index;
+		while ((index = propertyName.indexOf('.')) != -1) {
+			propertyNames.add(propertyName.substring(0, index));
+			propertyName = propertyName.substring(index + 1);
+		}
+		propertyNames.add(propertyName);
+		return (String[]) propertyNames
+				.toArray(new String[propertyNames.size()]);
+	}
+
+	/**
+	 * Returns a value property array for the given property names of the given
+	 * bean class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyNames
+	 *            array of property names. May be nested e.g. "parent.name"
+	 * @return a value property array for the given property names of the given
+	 *         bean class.
+	 */
+	public static IBeanValueProperty[] values(Class beanClass,
+			String[] propertyNames) {
+		IBeanValueProperty[] properties = new IBeanValueProperty[propertyNames.length];
+		for (int i = 0; i < properties.length; i++)
+			properties[i] = value(beanClass, propertyNames[i], null);
+		return properties;
+	}
+
+	/**
+	 * Returns a value property array for the given property names of an
+	 * arbitrary bean class.
+	 * 
+	 * @param propertyNames
+	 *            array of property names. May be nested e.g. "parent.name"
+	 * @return a value property array for the given property names of the given
+	 *         bean class.
+	 */
+	public static IBeanValueProperty[] values(String[] propertyNames) {
+		return values(null, propertyNames);
+	}
+
+	/**
+	 * Returns a set property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty set.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @return a set property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(String propertyName) {
+		return set(null, propertyName, null);
+	}
+
+	/**
+	 * Returns a set property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty set.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned set property
+	 * @return a set property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(String propertyName, Class elementType) {
+		return set(null, propertyName, elementType);
+	}
+
+	/**
+	 * Returns a set property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @return a set property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(Class beanClass, String propertyName) {
+		return set(beanClass, propertyName, null);
+	}
+
+	/**
+	 * Returns a set property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned set property
+	 * @return a set property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(Class beanClass, String propertyName,
+			Class elementType) {
+		PropertyDescriptor propertyDescriptor;
+		ISetProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousBeanSetProperty(propertyName, elementType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyName);
+			property = new BeanSetProperty(propertyDescriptor, elementType);
+		}
+		return new BeanSetPropertyDecorator(property, propertyDescriptor);
+	}
+
+	/**
+	 * Returns a list property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty list.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @return a list property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(String propertyName) {
+		return list(null, propertyName, null);
+	}
+
+	/**
+	 * Returns a list property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty list.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned list property
+	 * @return a list property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(String propertyName, Class elementType) {
+		return list(null, propertyName, elementType);
+	}
+
+	/**
+	 * Returns a list property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @return a list property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(Class beanClass, String propertyName) {
+		return list(beanClass, propertyName, null);
+	}
+
+	/**
+	 * Returns a list property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned list property
+	 * @return a list property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(Class beanClass, String propertyName,
+			Class elementType) {
+		PropertyDescriptor propertyDescriptor;
+		IListProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousBeanListProperty(propertyName, elementType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyName);
+			property = new BeanListProperty(propertyDescriptor, elementType);
+		}
+		return new BeanListPropertyDecorator(property, propertyDescriptor);
+	}
+
+	/**
+	 * Returns a map property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty map.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @return a map property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(String propertyName) {
+		return map(null, propertyName, null, null);
+	}
+
+	/**
+	 * Returns a map property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty map.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @param keyType
+	 *            the key type for the returned map property
+	 * @param valueType
+	 *            the value type for the returned map property
+	 * @return a map property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(String propertyName, Class keyType,
+			Class valueType) {
+		return map(null, propertyName, keyType, valueType);
+	}
+
+	/**
+	 * Returns a map property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @return a map property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(Class beanClass, String propertyName) {
+		return map(beanClass, propertyName, null, null);
+	}
+
+	/**
+	 * Returns a map property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @param keyType
+	 *            the key type for the returned map property
+	 * @param valueType
+	 *            the value type for the returned map property
+	 * @return a map property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(Class beanClass, String propertyName,
+			Class keyType, Class valueType) {
+		PropertyDescriptor propertyDescriptor;
+		IMapProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousBeanMapProperty(propertyName, keyType,
+					valueType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyName);
+			property = new BeanMapProperty(propertyDescriptor, keyType,
+					valueType);
+		}
+		return new BeanMapPropertyDecorator(property, propertyDescriptor);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/BeansObservables.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/BeansObservables.java
new file mode 100644
index 0000000..bc02766
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/BeansObservables.java
@@ -0,0 +1,910 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164268, 171616, 147515
+ *     Matthew Hall - bug 221704, 234686, 246625, 226289, 246782, 194734,
+ *                    195222, 247997
+ *     Thomas Kratz - bug 213787
+ *******************************************************************************/
+package org.eclipse.core.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+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.value.IObservableValue;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.beans.BeanObservableListDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableMapDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableSetDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableValueDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
+import org.eclipse.core.internal.databinding.beans.Util;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A factory for creating observable objects of Java objects that conform to the
+ * <a href="http://java.sun.com/products/javabeans/docs/spec.html">JavaBean
+ * specification</a> for bound properties.
+ * 
+ * @since 1.1
+ * 
+ */
+final public class BeansObservables {
+
+	/**
+	 * 
+	 */
+	public static final boolean DEBUG = true;
+
+	/**
+	 * Returns an observable value in the default realm tracking the current
+	 * value of the named property of the given bean.
+	 * 
+	 * @param bean
+	 *            the object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value tracking the current value of the named
+	 *         property of the given bean
+	 */
+	public static IObservableValue observeValue(Object bean, String propertyName) {
+		return observeValue(Realm.getDefault(), bean, propertyName);
+	}
+
+	/**
+	 * Returns an observable value in the given realm tracking the current value
+	 * of the named property of the given bean.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value tracking the current value of the named
+	 *         property of the given bean
+	 */
+	public static IObservableValue observeValue(Realm realm, Object bean,
+			String propertyName) {
+		return BeanProperties.value(bean.getClass(), propertyName).observe(
+				realm, bean);
+	}
+
+	/**
+	 * Returns an observable map in the given observable set's realm tracking
+	 * the current values of the named property for the beans in the given set.
+	 * Elements in the set which do not have the named property will have null
+	 * values, and attempts to {@link IObservableMap#put(Object, Object) put}
+	 * values to these elements will be ignored.
+	 * 
+	 * @param domain
+	 *            the set of bean objects
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable map tracking the current values of the named
+	 *         property for the beans in the given domain set
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(IObservableSet domain,
+			String propertyName) {
+		return BeanProperties.value(propertyName).observeDetail(domain);
+	}
+
+	/**
+	 * Returns an observable map in the given observable set's realm tracking
+	 * the current values of the named property for the beans in the given set.
+	 * 
+	 * @param domain
+	 *            the set of bean objects
+	 * @param beanClass
+	 *            the common base type of bean objects that may be in the set
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable map tracking the current values of the named
+	 *         property for the beans in the given domain set
+	 */
+	public static IObservableMap observeMap(IObservableSet domain,
+			Class beanClass, String propertyName) {
+		return BeanProperties.value(beanClass, propertyName).observeDetail(
+				domain);
+	}
+
+	/**
+	 * Returns an observable map in the given realm tracking the map-typed named
+	 * property of the given bean object.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given bean object
+	 * @since 1.1
+	 */
+	public static IObservableMap observeMap(Realm realm, Object bean,
+			String propertyName) {
+		return observeMap(realm, bean, propertyName, null, null);
+	}
+
+	/**
+	 * Returns an observable map in the given realm tracking the map-typed named
+	 * property of the given bean object.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @param keyType
+	 *            the element type of the observable map's key set, or
+	 *            <code>null</code> if untyped
+	 * @param valueType
+	 *            the element type of the observable map's values collection, or
+	 *            <code>null</code> if untyped
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given bean object
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(Realm realm, Object bean,
+			String propertyName, Class keyType, Class valueType) {
+		return BeanProperties.map(bean.getClass(), propertyName, keyType,
+				valueType).observe(realm, bean);
+	}
+
+	/**
+	 * Returns an observable map in the default realm tracking the map-typed
+	 * named property of the given bean object.
+	 * 
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given bean object
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(Object bean, String propertyName) {
+		return observeMap(Realm.getDefault(), bean, propertyName, null, null);
+	}
+
+	/**
+	 * Returns an observable map in the default realm tracking the map-typed
+	 * named property of the given bean object.
+	 * 
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param keyType
+	 *            the element type of the observable map's key set, or
+	 *            <code>null</code> if untyped
+	 * @param valueType
+	 *            the element type of the observable map's values collection, or
+	 *            <code>null</code> if untyped
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given bean object
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(Object bean, String propertyName,
+			Class keyType, Class valueType) {
+		return observeMap(Realm.getDefault(), bean, propertyName, keyType,
+				valueType);
+	}
+
+	/**
+	 * Returns an array of observable maps in the given observable set's realm
+	 * tracking the current values of the named properties for the beans in the
+	 * given set. Elements in the set which do not have the named property will
+	 * have null values, and attempts to
+	 * {@link IObservableMap#put(Object, Object) put} values to these elements
+	 * will be ignored.
+	 * 
+	 * @param domain
+	 *            the set of objects
+	 * @param propertyNames
+	 *            the array of property names. May be nested e.g. "parent.name"
+	 * @return an array of observable maps tracking the current values of the
+	 *         named propertys for the beans in the given domain set
+	 * @since 1.2
+	 */
+	public static IObservableMap[] observeMaps(IObservableSet domain,
+			String[] propertyNames) {
+		IObservableMap[] result = new IObservableMap[propertyNames.length];
+		for (int i = 0; i < propertyNames.length; i++) {
+			result[i] = observeMap(domain, propertyNames[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Returns an array of observable maps in the given observable set's realm
+	 * tracking the current values of the named properties for the beans in the
+	 * given set.
+	 * 
+	 * @param domain
+	 *            the set of objects
+	 * @param beanClass
+	 *            the common base type of objects that may be in the set
+	 * @param propertyNames
+	 *            the array of property names. May be nested e.g. "parent.name"
+	 * @return an array of observable maps tracking the current values of the
+	 *         named propertys for the beans in the given domain set
+	 */
+	public static IObservableMap[] observeMaps(IObservableSet domain,
+			Class beanClass, String[] propertyNames) {
+		IObservableMap[] result = new IObservableMap[propertyNames.length];
+		for (int i = 0; i < propertyNames.length; i++) {
+			result[i] = observeMap(domain, beanClass, propertyNames[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Returns an observable list in the given realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * list is mutable.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the object
+	 * @param propertyName
+	 *            the name of the collection-typed property
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given bean object
+	 * @see #observeList(Realm, Object, String, Class)
+	 */
+	public static IObservableList observeList(Realm realm, Object bean,
+			String propertyName) {
+		return observeList(realm, bean, propertyName, null);
+	}
+
+	/**
+	 * Returns an observable list in the default realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * list is mutable.
+	 * 
+	 * @param bean
+	 *            the object
+	 * @param propertyName
+	 *            the name of the collection-typed property
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given bean object
+	 * @see #observeList(Realm, Object, String, Class)
+	 * @since 1.2
+	 */
+	public static IObservableList observeList(Object bean, String propertyName) {
+		return observeList(Realm.getDefault(), bean, propertyName);
+	}
+
+	/**
+	 * Returns an observable list in the given realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * list is mutable. When an item is added or removed the setter is invoked
+	 * for the list on the parent bean to provide notification to other
+	 * listeners via <code>PropertyChangeEvents</code>. This is done to provide
+	 * the same behavior as is expected from arrays as specified in the bean
+	 * spec in section 7.2.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the list. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given bean object
+	 */
+	public static IObservableList observeList(Realm realm, Object bean,
+			String propertyName, Class elementType) {
+		return BeanProperties.list(bean.getClass(), propertyName, elementType)
+				.observe(realm, bean);
+	}
+
+	/**
+	 * Returns an observable list in the default realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * list is mutable. When an item is added or removed the setter is invoked
+	 * for the list on the parent bean to provide notification to other
+	 * listeners via <code>PropertyChangeEvents</code>. This is done to provide
+	 * the same behavior as is expected from arrays as specified in the bean
+	 * spec in section 7.2.
+	 * 
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the list. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given bean object
+	 * @since 1.2
+	 */
+	public static IObservableList observeList(Object bean, String propertyName,
+			Class elementType) {
+		return observeList(Realm.getDefault(), bean, propertyName, elementType);
+	}
+
+	/**
+	 * Returns an observable set in the given realm tracking the
+	 * collection-typed named property of the given bean object
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set tracking the collection-typed named property of
+	 *         the given bean object
+	 */
+	public static IObservableSet observeSet(Realm realm, Object bean,
+			String propertyName) {
+		return observeSet(realm, bean, propertyName, null);
+	}
+
+	/**
+	 * Returns an observable set in the default realm tracking the
+	 * collection-typed named property of the given bean object
+	 * 
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set tracking the collection-typed named property of
+	 *         the given bean object
+	 * @since 1.2
+	 */
+	public static IObservableSet observeSet(Object bean, String propertyName) {
+		return observeSet(Realm.getDefault(), bean, propertyName);
+	}
+
+	/**
+	 * Returns a factory for creating observable values in the given realm,
+	 * tracking the given property of a particular bean object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value factory
+	 */
+	public static IObservableFactory valueFactory(final Realm realm,
+			final String propertyName) {
+		return BeanProperties.value(propertyName).valueFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable values in the current default
+	 * realm, tracking the given property of a particular bean object
+	 * 
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value factory
+	 * @since 1.2
+	 */
+	public static IObservableFactory valueFactory(String propertyName) {
+		return valueFactory(Realm.getDefault(), propertyName);
+	}
+
+	/**
+	 * Returns a factory for creating observable lists in the given realm,
+	 * tracking the given property of a particular bean object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 * @return an observable list factory
+	 */
+	public static IObservableFactory listFactory(final Realm realm,
+			final String propertyName, final Class elementType) {
+		return BeanProperties.list(propertyName, elementType)
+				.listFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable lists in the current default
+	 * realm, tracking the given property of a particular bean object
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 * @return an observable list factory
+	 * @since 1.2
+	 */
+	public static IObservableFactory listFactory(String propertyName,
+			Class elementType) {
+		return listFactory(Realm.getDefault(), propertyName, elementType);
+	}
+
+	/**
+	 * Returns a factory for creating observable sets in the given realm,
+	 * tracking the given property of a particular bean object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set factory
+	 */
+	public static IObservableFactory setFactory(final Realm realm,
+			final String propertyName) {
+		return BeanProperties.set(propertyName).setFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable sets in the current default
+	 * realm, tracking the given property of a particular bean object
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set factory
+	 * @since 1.2
+	 */
+	public static IObservableFactory setFactory(String propertyName) {
+		return setFactory(Realm.getDefault(), propertyName);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailValue(master, valueFactory(realm,
+	 propertyName), propertyType)</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable value that tracks the current value of the named
+	 *         property for the current value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @deprecated Use
+	 *             {@link #observeDetailValue(IObservableValue, String, Class)}
+	 *             instead
+	 */
+	public static IObservableValue observeDetailValue(Realm realm,
+			IObservableValue master, String propertyName, Class propertyType) {
+		warnIfDifferentRealms(realm, master.getRealm());
+
+		IObservableValue value = MasterDetailObservables.detailValue(master,
+				BeanProperties.value(propertyName, propertyType).valueFactory(
+						realm), propertyType);
+		return new BeanObservableValueDecorator(value, BeanPropertyHelper
+				.getValueTypePropertyDescriptor(master, propertyName));
+	}
+
+	/* package */static void warnIfDifferentRealms(Realm detailRealm,
+			Realm masterRealm) {
+		if (!Util.equals(detailRealm, masterRealm)) {
+			Throwable throwable = new Throwable();
+			throwable.fillInStackTrace();
+			String message = "Detail realm (" + detailRealm //$NON-NLS-1$
+					+ ") not equal to master realm (" //$NON-NLS-1$
+					+ masterRealm + ")"; //$NON-NLS-1$
+			Policy.getLog().log(
+					new Status(IStatus.WARNING, Policy.JFACE_DATABINDING,
+							message, throwable));
+		}
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailValue(master, valueFactory(master.getRealm(), propertyName), propertyType)</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable value that tracks the current value of the named
+	 *         property for the current value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableValue observeDetailValue(IObservableValue master,
+			String propertyName, Class propertyType) {
+		Class beanClass = null;
+		if (master.getValueType() instanceof Class)
+			beanClass = (Class) master.getValueType();
+		return observeDetailValue(master, beanClass, propertyName, propertyType);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailValue(master, valueFactory(realm,
+	 * propertyName), propertyType)</code>. This method returns an
+	 * {@link IBeanObservable} with a {@link PropertyDescriptor} based on the
+	 * given master type and property name.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param master
+	 *            the master observable value, for example tracking the
+	 *            selection in a list
+	 * @param masterType
+	 *            the type of the master observable value
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable value that tracks the current value of the named
+	 *         property for the current value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.1
+	 * @deprecated Use
+	 *             {@link #observeDetailValue(IObservableValue, Class, String, Class)}
+	 *             instead.
+	 */
+	public static IObservableValue observeDetailValue(Realm realm,
+			IObservableValue master, Class masterType, String propertyName,
+			Class propertyType) {
+		warnIfDifferentRealms(realm, master.getRealm());
+		Assert.isNotNull(masterType, "masterType cannot be null"); //$NON-NLS-1$
+		IObservableValue value = MasterDetailObservables.detailValue(master,
+				BeanProperties.value(masterType, propertyName, propertyType)
+						.valueFactory(realm), propertyType);
+		return new BeanObservableValueDecorator(value, BeanPropertyHelper
+				.getPropertyDescriptor(masterType, propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailValue(master, valueFactory(master.getRealm(), propertyName), propertyType)</code>
+	 * . This method returns an {@link IBeanObservable} with a
+	 * {@link PropertyDescriptor} based on the given master type and property
+	 * name.
+	 * 
+	 * @param master
+	 *            the master observable value, for example tracking the
+	 *            selection in a list
+	 * @param masterType
+	 *            the type of the master observable value
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable value that tracks the current value of the named
+	 *         property for the current value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableValue observeDetailValue(IObservableValue master,
+			Class masterType, String propertyName, Class propertyType) {
+		return BeanProperties.value(masterType, propertyName, propertyType)
+				.observeDetail(master);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailList(master, listFactory(realm,
+	 propertyName, propertyType), propertyType)</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable list that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @deprecated Use
+	 *             {@link #observeDetailList(IObservableValue, String, Class)}
+	 *             instead
+	 */
+	public static IObservableList observeDetailList(Realm realm,
+			IObservableValue master, String propertyName, Class propertyType) {
+		warnIfDifferentRealms(realm, master.getRealm());
+		IObservableList observableList = MasterDetailObservables.detailList(
+				master, BeanProperties.list(propertyName, propertyType)
+						.listFactory(realm), propertyType);
+		return new BeanObservableListDecorator(observableList,
+				BeanPropertyHelper.getValueTypePropertyDescriptor(master,
+						propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailList(master, listFactory(master.getRealm(), propertyName, propertyType), propertyType)</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable list that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableList observeDetailList(IObservableValue master,
+			String propertyName, Class propertyType) {
+		Class beanClass = null;
+		if (master.getValueType() instanceof Class)
+			beanClass = (Class) master.getValueType();
+		return BeanProperties.list(beanClass, propertyName, propertyType)
+				.observeDetail(master);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailSet(master, setFactory(realm,
+	 propertyName), propertyType)</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable set that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @deprecated Use
+	 *             {@link #observeDetailSet(IObservableValue, String, Class)}
+	 *             instead.
+	 */
+	public static IObservableSet observeDetailSet(Realm realm,
+			IObservableValue master, String propertyName, Class propertyType) {
+		warnIfDifferentRealms(realm, master.getRealm());
+
+		IObservableSet observableSet = MasterDetailObservables.detailSet(
+				master, BeanProperties.set(propertyName, propertyType)
+						.setFactory(realm), propertyType);
+		return new BeanObservableSetDecorator(observableSet, BeanPropertyHelper
+				.getValueTypePropertyDescriptor(master, propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailSet(master, setFactory(master.getRealm(), propertyName), propertyType)</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable set that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableSet observeDetailSet(IObservableValue master,
+			String propertyName, Class propertyType) {
+		Class beanClass = null;
+		if (master.getValueType() instanceof Class)
+			beanClass = (Class) master.getValueType();
+		return BeanProperties.set(beanClass, propertyName, propertyType)
+				.observeDetail(master);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailMap(master, mapFactory(realm, propertyName))</code>
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param master
+	 * @param propertyName
+	 * @return an observable map that tracks the map-type named property for the
+	 *         current value of the master observable value.
+	 * @since 1.1
+	 * @deprecated Use {@link #observeDetailMap(IObservableValue, String)}
+	 *             instead
+	 */
+	public static IObservableMap observeDetailMap(Realm realm,
+			IObservableValue master, String propertyName) {
+		warnIfDifferentRealms(realm, master.getRealm());
+		IObservableMap observableMap = MasterDetailObservables.detailMap(
+				master, BeanProperties.map(propertyName).mapFactory(realm));
+		return new BeanObservableMapDecorator(observableMap, BeanPropertyHelper
+				.getValueTypePropertyDescriptor(master, propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailMap(master, mapFactory(master.getRealm(), propertyName))</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 * @return an observable map that tracks the map-type named property for the
+	 *         current value of the master observable value.
+	 * @since 1.2
+	 */
+	public static IObservableMap observeDetailMap(IObservableValue master,
+			String propertyName) {
+		Class beanClass = null;
+		if (master.getValueType() instanceof Class)
+			beanClass = (Class) master.getValueType();
+		return BeanProperties.map(beanClass, propertyName)
+				.observeDetail(master);
+	}
+
+	/**
+	 * Returns an observable set in the given realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * set is mutable. When an item is added or removed the setter is invoked
+	 * for the set on the parent bean to provide notification to other listeners
+	 * via <code>PropertyChangeEvents</code>. This is done to provide the same
+	 * behavior as is expected from arrays as specified in the bean spec in
+	 * section 7.2.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable set tracking the collection-typed named property of
+	 *         the given bean object
+	 */
+	public static IObservableSet observeSet(Realm realm, Object bean,
+			String propertyName, Class elementType) {
+		return BeanProperties.set(bean.getClass(), propertyName, elementType)
+				.observe(realm, bean);
+	}
+
+	/**
+	 * Returns an observable set in the current default realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * set is mutable. When an item is added or removed the setter is invoked
+	 * for the set on the parent bean to provide notification to other listeners
+	 * via <code>PropertyChangeEvents</code>. This is done to provide the same
+	 * behavior as is expected from arrays as specified in the bean spec in
+	 * section 7.2.
+	 * 
+	 * @param bean
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable set tracking the collection-typed named property of
+	 *         the given bean object
+	 * @since 1.2
+	 */
+	public static IObservableSet observeSet(Object bean, String propertyName,
+			Class elementType) {
+		return observeSet(Realm.getDefault(), bean, propertyName, elementType);
+	}
+
+	/**
+	 * Returns a factory for creating observable sets in the given realm,
+	 * tracking the given property of a particular bean object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return a factory for creating observable sets in the given realm,
+	 *         tracking the given property of a particular bean object
+	 */
+	public static IObservableFactory setFactory(final Realm realm,
+			final String propertyName, final Class elementType) {
+		return BeanProperties.set(propertyName, elementType).setFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable sets in the current default
+	 * realm, tracking the given property of a particular bean object
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return a factory for creating observable sets in the given realm,
+	 *         tracking the given property of a particular bean object
+	 * @since 1.2
+	 */
+	public static IObservableFactory setFactory(String propertyName,
+			Class elementType) {
+		return setFactory(Realm.getDefault(), propertyName, elementType);
+	}
+
+	/**
+	 * Returns a factory for creating an observable map. The factory, when
+	 * provided with an {@link IObservableSet}, will create an
+	 * {@link IObservableMap} in the same realm as the underlying set that
+	 * tracks the current values of the named property for the beans in the
+	 * given set.
+	 * 
+	 * @param beanClass
+	 *            the common base type of bean objects that may be in the set
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return a factory for creating {@link IObservableMap} objects
+	 * 
+	 * @since 1.1
+	 */
+	public static IObservableFactory setToMapFactory(final Class beanClass,
+			final String propertyName) {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observeMap((IObservableSet) target, beanClass,
+						propertyName);
+			}
+		};
+	}
+
+	/**
+	 * Returns a factory for creating an observable map. The factory, when
+	 * provided with a bean object, will create an {@link IObservableMap} in the
+	 * given realm that tracks the map-typed named property for the specified
+	 * bean.
+	 * 
+	 * @param realm
+	 *            the realm assigned to observables created by the returned
+	 *            factory.
+	 * @param propertyName
+	 *            the name of the property
+	 * @return a factory for creating {@link IObservableMap} objects.
+	 * @since 1.1
+	 */
+	public static IObservableFactory mapPropertyFactory(final Realm realm,
+			final String propertyName) {
+		return BeanProperties.map(propertyName).mapFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating an observable map. The factory, when
+	 * provided with a bean object, will create an {@link IObservableMap} in the
+	 * current default realm that tracks the map-typed named property for the
+	 * specified bean.
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @return a factory for creating {@link IObservableMap} objects.
+	 * @since 1.2
+	 */
+	public static IObservableFactory mapPropertyFactory(String propertyName) {
+		return mapPropertyFactory(Realm.getDefault(), propertyName);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanListProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanListProperty.java
new file mode 100644
index 0000000..95f80a0
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanListProperty.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 195222)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import org.eclipse.core.databinding.property.list.IListProperty;
+
+/**
+ * An {@link IListProperty} extension interface with convenience methods for
+ * creating nested bean properties.
+ * 
+ * @since 1.2
+ */
+public interface IBeanListProperty extends IBeanProperty, IListProperty {
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @return a nested combination of this property and the specified value
+	 *         property.
+	 * @see #values(IBeanValueProperty)
+	 */
+	public IBeanListProperty values(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @param valueType
+	 *            the value type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #values(IBeanValueProperty)
+	 */
+	public IBeanListProperty values(String propertyName, Class valueType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property. The returned property will observe the specified value
+	 * property for all elements observed by this list property.
+	 * <p>
+	 * Example:
+	 * 
+	 * <pre>
+	 * // Observes the list-typed &quot;children&quot; property of a Person object,
+	 * // where the elements are Person objects
+	 * IBeanListProperty children = BeanProperties.list(Person.class, &quot;children&quot;,
+	 * 		Person.class);
+	 * // Observes the string-typed &quot;name&quot; property of a Person object
+	 * IBeanValueProperty name = BeanProperties.value(Person.class, &quot;name&quot;);
+	 * // Observes the names of children of a Person object.
+	 * IBeanListProperty childrenNames = children.values(name);
+	 * </pre>
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 */
+	public IBeanListProperty values(IBeanValueProperty property);
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanMapProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanMapProperty.java
new file mode 100644
index 0000000..1cfde35
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanMapProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 195222)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import java.util.Map;
+
+import org.eclipse.core.databinding.property.map.IMapProperty;
+
+/**
+ * An {@link IMapProperty} extension interface with convenience methods for
+ * creating nested bean properties.
+ * 
+ * @since 1.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IBeanMapProperty extends IBeanProperty, IMapProperty {
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #values(IBeanValueProperty)
+	 */
+	public IBeanMapProperty values(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @param valueType
+	 *            the value type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #values(IBeanValueProperty)
+	 */
+	public IBeanMapProperty values(String propertyName, Class valueType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property. The returned property will observe the specified value
+	 * property for all {@link Map#values() values} observed by this map
+	 * property, mapping from this map property's {@link Map#keySet() key set}
+	 * to the specified value property's value for each element in the master
+	 * property's {@link Map#values() values} collection.
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 */
+	public IBeanMapProperty values(IBeanValueProperty property);
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanObservable.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanObservable.java
new file mode 100644
index 0000000..c2545ef
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanObservable.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 147515
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.observable.IObserving;
+
+/**
+ * Provides access to details of bean observables.
+ * <p>
+ * This interface is not meant to be implemented by clients.
+ * </p>
+ * 
+ * @since 3.3
+ */
+public interface IBeanObservable extends IObserving {
+	/**
+	 * @return property descriptor of the property being observed,
+	 *         <code>null</code> if the runtime time information was not
+	 *         provided on construction of the observable
+	 */
+	public PropertyDescriptor getPropertyDescriptor();
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanProperty.java
new file mode 100644
index 0000000..a20631f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanProperty.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.property.IProperty;
+
+/**
+ * An IProperty extension interface providing access to details of bean
+ * properties.
+ * 
+ * @since 1.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IBeanProperty extends IProperty {
+	/**
+	 * Returns the property descriptor of the bean property being observed. This
+	 * method returns null in the case of anonymous properties.
+	 * 
+	 * @return the property descriptor of the bean property being observed
+	 */
+	public PropertyDescriptor getPropertyDescriptor();
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanSetProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanSetProperty.java
new file mode 100644
index 0000000..a5e70ae
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanSetProperty.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 195222)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import org.eclipse.core.databinding.property.set.ISetProperty;
+
+/**
+ * An {@link ISetProperty} extension interface with convenience methods for
+ * creating nested bean properties.
+ * 
+ * @since 1.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IBeanSetProperty extends IBeanProperty, ISetProperty {
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #values(IBeanValueProperty)
+	 */
+	public IBeanMapProperty values(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @param valueType
+	 *            the value type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #values(IBeanValueProperty)
+	 */
+	public IBeanMapProperty values(String propertyName, Class valueType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property. The returned property will observe the specified value
+	 * property for all elements observed by this set property, mapping from
+	 * this set property's elements (keys) to the specified value property's
+	 * value for each element (values).
+	 * <p>
+	 * Example:
+	 * 
+	 * <pre>
+	 * // Observes the set-typed &quot;children&quot; property of a Person object,
+	 * // where the elements are Person objects
+	 * IBeanSetProperty children = BeanProperties.set(Person.class, &quot;children&quot;,
+	 * 		Person.class);
+	 * // Observes the string-typed &quot;name&quot; property of a Person object
+	 * IBeanValueProperty name = BeanProperties.value(Person.class, &quot;name&quot;);
+	 * // Observes a map of children objects to their respective names.
+	 * IBeanMapProperty childrenNames = children.values(name);
+	 * </pre>
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 */
+	public IBeanMapProperty values(IBeanValueProperty property);
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanValueProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanValueProperty.java
new file mode 100644
index 0000000..72153a6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/IBeanValueProperty.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 195222)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * An {@link IValueProperty} extension interface with convenience methods for
+ * creating nested bean properties.
+ * 
+ * @since 1.2
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IBeanValueProperty extends IBeanProperty, IValueProperty {
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #value(IBeanValueProperty)
+	 */
+	public IBeanValueProperty value(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property.
+	 * 
+	 * @param propertyName
+	 *            the value property to observe. May be nested e.g.
+	 *            "parent.name"
+	 * @param valueType
+	 *            the value type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 * @see #value(IBeanValueProperty)
+	 */
+	public IBeanValueProperty value(String propertyName, Class valueType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * value property. The returned property will observe the specified detail
+	 * value property for the value of the master value property.
+	 * <p>
+	 * Example:
+	 * 
+	 * <pre>
+	 * // Observes the Node-typed &quot;parent&quot; property of a Node object
+	 * IBeanValueProperty parent = BeanProperties.value(Node.class, &quot;parent&quot;);
+	 * // Observes the string-typed &quot;name&quot; property of a Node object
+	 * IBeanValueProperty name = BeanProperties.value(Node.class, &quot;name&quot;);
+	 * // Observes the name of the parent of a Node object.
+	 * IBeanValueProperty parentName = parent.value(name);
+	 * </pre>
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         value property.
+	 */
+	public IBeanValueProperty value(IBeanValueProperty property);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * list property.
+	 * 
+	 * @param propertyName
+	 *            the list property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         list property.
+	 * @see #list(IBeanListProperty)
+	 */
+	public IBeanListProperty list(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * list property.
+	 * 
+	 * @param propertyName
+	 *            the list property to observe
+	 * @param elementType
+	 *            the element type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         list property.
+	 * @see #list(IBeanListProperty)
+	 */
+	public IBeanListProperty list(String propertyName, Class elementType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * list property. The returned property will observe the specified list
+	 * property for the value of the master property.
+	 * <p>
+	 * Example:
+	 * 
+	 * <pre>
+	 * // Observes the Node-typed &quot;parent&quot; property of a Node object.
+	 * IBeanValueProperty parent = BeanProperties.value(Node.class, &quot;parent&quot;);
+	 * // Observes the List-typed &quot;children&quot; property of a Node object
+	 * // where the elements are Node objects
+	 * IBeanListProperty children = BeanProperties.list(Node.class, &quot;children&quot;,
+	 * 		Node.class);
+	 * // Observes the children of the parent (siblings) of a Node object.
+	 * IBeanListProperty siblings = parent.list(children);
+	 * </pre>
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         list property.
+	 */
+	public IBeanListProperty list(IBeanListProperty property);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * set property.
+	 * 
+	 * @param propertyName
+	 *            the set property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         set property.
+	 * @see #set(IBeanSetProperty)
+	 */
+	public IBeanSetProperty set(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * set property.
+	 * 
+	 * @param propertyName
+	 *            the set property to observe
+	 * @param elementType
+	 *            the element type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         set property.
+	 * @see #set(IBeanSetProperty)
+	 */
+	public IBeanSetProperty set(String propertyName, Class elementType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * set property. The returned property will observe the specified set
+	 * property for the value of the master property.
+	 * <p>
+	 * Example:
+	 * 
+	 * <pre>
+	 * // Observes the Node-typed &quot;parent&quot; property of a Node object.
+	 * IBeanValueProperty parent = BeanProperties.value(Node.class, &quot;parent&quot;);
+	 * // Observes the Set-typed &quot;children&quot; property of a Node object
+	 * // where the elements are Node objects
+	 * IBeanSetProperty children = BeanProperties.set(Node.class, &quot;children&quot;,
+	 * 		Node.class);
+	 * // Observes the children of the parent (siblings) of a Node object.
+	 * IBeanSetProperty siblings = parent.set(children);
+	 * </pre>
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         set property.
+	 */
+	public IBeanSetProperty set(IBeanSetProperty property);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * map property.
+	 * 
+	 * @param propertyName
+	 *            the map property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         map property.
+	 * @see #map(IBeanMapProperty)
+	 */
+	public IBeanMapProperty map(String propertyName);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * map property.
+	 * 
+	 * @param propertyName
+	 *            the map property to observe
+	 * @param keyType
+	 *            the key type of the named property
+	 * @param valueType
+	 *            the value type of the named property
+	 * @return a master-detail combination of this property and the specified
+	 *         map property.
+	 * @see #map(IBeanMapProperty)
+	 */
+	public IBeanMapProperty map(String propertyName, Class keyType,
+			Class valueType);
+
+	/**
+	 * Returns a master-detail combination of this property and the specified
+	 * map property. The returned property will observe the specified map
+	 * property for the value of the master property.
+	 * <p>
+	 * Example:
+	 * 
+	 * <pre>
+	 * // Observes the Contact-typed &quot;supervisor&quot; property of a
+	 * // Contact class 
+	 * IBeanValueProperty supervisor = BeanProperties.value(Contact.class,
+	 * 		&quot;supervisor&quot;);
+	 * // Observes the property &quot;phoneNumbers&quot; of a Contact object--a property mapping
+	 * // from PhoneNumberType to PhoneNumber &quot;set-typed &quot;children&quot;,
+	 * IBeanMapProperty phoneNumbers = BeanProperties.map(Contact.class,
+	 * 		&quot;phoneNumbers&quot;, PhoneNumberType.class, PhoneNumber.class);
+	 * // Observes the phone numbers of a contact's supervisor:
+	 * IBeanMapProperty supervisorPhoneNumbers = supervisor.map(phoneNumbers);
+	 * </pre>
+	 * 
+	 * @param property
+	 *            the detail property to observe
+	 * @return a master-detail combination of this property and the specified
+	 *         map property.
+	 */
+	public IBeanMapProperty map(IBeanMapProperty property);
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/PojoObservables.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/PojoObservables.java
new file mode 100644
index 0000000..ecdcb2b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/PojoObservables.java
@@ -0,0 +1,777 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 221704, 234686, 246625, 226289, 246782, 194734,
+ *                    195222, 247997
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+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.value.IObservableValue;
+import org.eclipse.core.internal.databinding.beans.BeanObservableListDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableMapDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableSetDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableValueDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
+
+/**
+ * A factory for creating observable objects for POJOs (plain old java objects)
+ * that conform to idea of an object with getters and setters but does not
+ * provide {@link PropertyChangeEvent property change events} on change. This
+ * factory is identical to {@link BeansObservables} except for this fact.
+ * 
+ * @since 1.1
+ */
+final public class PojoObservables {
+
+	/**
+	 * Returns an observable value in the default realm tracking the current
+	 * value of the named property of the given pojo.
+	 * 
+	 * @param pojo
+	 *            the object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value tracking the current value of the named
+	 *         property of the given pojo
+	 */
+	public static IObservableValue observeValue(Object pojo, String propertyName) {
+		return observeValue(Realm.getDefault(), pojo, propertyName);
+	}
+
+	/**
+	 * Returns an observable value in the given realm tracking the current value
+	 * of the named property of the given pojo.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the object
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value tracking the current value of the named
+	 *         property of the given pojo
+	 */
+	public static IObservableValue observeValue(Realm realm, Object pojo,
+			String propertyName) {
+		return PojoProperties.value(pojo.getClass(), propertyName).observe(
+				realm, pojo);
+	}
+
+	/**
+	 * Returns an observable map in the given observable set's realm tracking
+	 * the current values of the named property for the beans in the given set.
+	 * Elements in the set which do not have the named property will have null
+	 * values, and attempts to {@link IObservableMap#put(Object, Object) put}
+	 * values to these elements will be ignored.
+	 * 
+	 * @param domain
+	 *            the set of bean objects
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable map tracking the current values of the named
+	 *         property for the beans in the given domain set
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(IObservableSet domain,
+			String propertyName) {
+		return PojoProperties.value(propertyName).observeDetail(domain);
+	}
+
+	/**
+	 * Returns an observable map in the given observable set's realm tracking
+	 * the current values of the named property for the pojos in the given set.
+	 * 
+	 * @param domain
+	 *            the set of pojo objects
+	 * @param pojoClass
+	 *            the common base type of pojo objects that may be in the set
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable map tracking the current values of the named
+	 *         property for the pojos in the given domain set
+	 */
+	public static IObservableMap observeMap(IObservableSet domain,
+			Class pojoClass, String propertyName) {
+		return PojoProperties.value(pojoClass, propertyName).observeDetail(
+				domain);
+	}
+
+	/**
+	 * Returns an array of observable maps in the given observable set's realm
+	 * tracking the current values of the named properties for the beans in the
+	 * given set. Elements in the set which do not have the named property will
+	 * have null values, and attempts to
+	 * {@link IObservableMap#put(Object, Object) put} values to these elements
+	 * will be ignored.
+	 * 
+	 * @param domain
+	 *            the set of objects
+	 * @param propertyNames
+	 *            the array of property names. May be nested e.g. "parent.name"
+	 * @return an array of observable maps tracking the current values of the
+	 *         named propertys for the beans in the given domain set
+	 * @since 1.2
+	 */
+	public static IObservableMap[] observeMaps(IObservableSet domain,
+			String[] propertyNames) {
+		IObservableMap[] result = new IObservableMap[propertyNames.length];
+		for (int i = 0; i < propertyNames.length; i++) {
+			result[i] = observeMap(domain, propertyNames[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Returns an array of observable maps in the given observable set's realm
+	 * tracking the current values of the named propertys for the pojos in the
+	 * given set.
+	 * 
+	 * @param domain
+	 *            the set of objects
+	 * @param pojoClass
+	 *            the common base type of objects that may be in the set
+	 * @param propertyNames
+	 *            the array of property names. May be nested e.g. "parent.name"
+	 * @return an array of observable maps tracking the current values of the
+	 *         named propertys for the pojos in the given domain set
+	 */
+	public static IObservableMap[] observeMaps(IObservableSet domain,
+			Class pojoClass, String[] propertyNames) {
+		IObservableMap[] result = new IObservableMap[propertyNames.length];
+		for (int i = 0; i < propertyNames.length; i++) {
+			result[i] = observeMap(domain, pojoClass, propertyNames[i]);
+		}
+		return result;
+	}
+
+	/**
+	 * Returns an observable map in the given realm tracking the map-typed named
+	 * property of the given pojo object.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given pojo object
+	 */
+	public static IObservableMap observeMap(Realm realm, Object pojo,
+			String propertyName) {
+		return observeMap(realm, pojo, propertyName, null, null);
+	}
+
+	/**
+	 * Returns an observable map in the given realm tracking the map-typed named
+	 * property of the given pojo object.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param keyType
+	 *            the element type of the observable map's key set, or
+	 *            <code>null</code> if untyped
+	 * @param valueType
+	 *            the element type of the observable map's values collection, or
+	 *            <code>null</code> if untyped
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given pojo object
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(Realm realm, Object pojo,
+			String propertyName, Class keyType, Class valueType) {
+		return PojoProperties.map(pojo.getClass(), propertyName, keyType,
+				valueType).observe(realm, pojo);
+	}
+
+	/**
+	 * Returns an observable map in the default realm tracking the map-typed
+	 * named property of the given pojo object.
+	 * 
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given pojo object
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(Object pojo, String propertyName) {
+		return observeMap(Realm.getDefault(), pojo, propertyName, null, null);
+	}
+
+	/**
+	 * Returns an observable map in the default realm tracking the map-typed
+	 * named property of the given pojo object.
+	 * 
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param keyType
+	 *            the element type of the observable map's key set, or
+	 *            <code>null</code> if untyped
+	 * @param valueType
+	 *            the element type of the observable map's values collection, or
+	 *            <code>null</code> if untyped
+	 * @return an observable map tracking the map-typed named property of the
+	 *         given pojo object
+	 * @since 1.2
+	 */
+	public static IObservableMap observeMap(Object pojo, String propertyName,
+			Class keyType, Class valueType) {
+		return observeMap(Realm.getDefault(), pojo, propertyName, keyType,
+				valueType);
+	}
+
+	/**
+	 * Returns an observable list in the given realm tracking the
+	 * collection-typed named property of the given pojo object. The returned
+	 * list is mutable.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the object
+	 * @param propertyName
+	 *            the name of the collection-typed property
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given pojo object
+	 * @see #observeList(Realm, Object, String, Class)
+	 */
+	public static IObservableList observeList(Realm realm, Object pojo,
+			String propertyName) {
+		return observeList(realm, pojo, propertyName, null);
+	}
+
+	/**
+	 * Returns an observable list in the default realm tracking the
+	 * collection-typed named property of the given pojo object. The returned
+	 * list is mutable.
+	 * 
+	 * @param pojo
+	 *            the object
+	 * @param propertyName
+	 *            the name of the collection-typed property
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given pojo object
+	 * @see #observeList(Realm, Object, String, Class)
+	 * @since 1.2
+	 */
+	public static IObservableList observeList(Object pojo, String propertyName) {
+		return observeList(Realm.getDefault(), pojo, propertyName);
+	}
+
+	/**
+	 * Returns an observable list in the given realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * list is mutable. When an item is added or removed the setter is invoked
+	 * for the list on the parent bean to provide notification to other
+	 * listeners via <code>PropertyChangeEvents</code>. This is done to provide
+	 * the same behavior as is expected from arrays as specified in the bean
+	 * spec in section 7.2.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the list. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given bean object
+	 */
+	public static IObservableList observeList(Realm realm, Object pojo,
+			String propertyName, Class elementType) {
+		return PojoProperties.list(pojo.getClass(), propertyName, elementType)
+				.observe(realm, pojo);
+	}
+
+	/**
+	 * Returns an observable list in the default realm tracking the
+	 * collection-typed named property of the given bean object. The returned
+	 * list is mutable. When an item is added or removed the setter is invoked
+	 * for the list on the parent bean to provide notification to other
+	 * listeners via <code>PropertyChangeEvents</code>. This is done to provide
+	 * the same behavior as is expected from arrays as specified in the bean
+	 * spec in section 7.2.
+	 * 
+	 * @param pojo
+	 *            the bean object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the list. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable list tracking the collection-typed named property
+	 *         of the given bean object
+	 * @since 1.2
+	 */
+	public static IObservableList observeList(Object pojo, String propertyName,
+			Class elementType) {
+		return observeList(Realm.getDefault(), pojo, propertyName, elementType);
+	}
+
+	/**
+	 * Returns an observable set in the given realm tracking the
+	 * collection-typed named property of the given pojo object.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set tracking the collection-typed named property of
+	 *         the given pojo object
+	 */
+	public static IObservableSet observeSet(Realm realm, Object pojo,
+			String propertyName) {
+		return observeSet(realm, pojo, propertyName, null);
+	}
+
+	/**
+	 * Returns an observable set in the default realm tracking the
+	 * collection-typed named property of the given pojo object.
+	 * 
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set tracking the collection-typed named property of
+	 *         the given pojo object
+	 * @since 1.2
+	 */
+	public static IObservableSet observeSet(Object pojo, String propertyName) {
+		return observeSet(Realm.getDefault(), pojo, propertyName);
+	}
+
+	/**
+	 * Returns an observable set in the given realm tracking the
+	 * collection-typed named property of the given pojo object.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable set that tracks the current value of the named
+	 *         property for given pojo object
+	 */
+	public static IObservableSet observeSet(Realm realm, Object pojo,
+			String propertyName, Class elementType) {
+		return PojoProperties.set(pojo.getClass(), propertyName, elementType)
+				.observe(realm, pojo);
+	}
+
+	/**
+	 * Returns an observable set in the default realm, tracking the
+	 * collection-typed named property of the given pojo object.
+	 * 
+	 * @param pojo
+	 *            the pojo object
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable set that tracks the current value of the named
+	 *         property for given pojo object
+	 * @since 1.2
+	 */
+	public static IObservableSet observeSet(Object pojo, String propertyName,
+			Class elementType) {
+		return observeSet(Realm.getDefault(), pojo, propertyName, elementType);
+	}
+
+	/**
+	 * Returns a factory for creating observable values in the given realm,
+	 * tracking the given property of a particular pojo object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value factory
+	 */
+	public static IObservableFactory valueFactory(final Realm realm,
+			final String propertyName) {
+		return PojoProperties.value(propertyName).valueFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable values in the current default
+	 * realm, tracking the given property of a particular pojo object
+	 * 
+	 * @param propertyName
+	 *            the name of the property. May be nested e.g. "parent.name"
+	 * @return an observable value factory
+	 * @since 1.2
+	 */
+	public static IObservableFactory valueFactory(String propertyName) {
+		return valueFactory(Realm.getDefault(), propertyName);
+	}
+
+	/**
+	 * Returns a factory for creating observable lists in the given realm,
+	 * tracking the given property of a particular pojo object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 * @return an observable list factory
+	 */
+	public static IObservableFactory listFactory(final Realm realm,
+			final String propertyName, final Class elementType) {
+		return PojoProperties.list(propertyName, elementType)
+				.listFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable lists in the current default
+	 * realm, tracking the given property of a particular pojo object
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 * @return an observable list factory
+	 * @since 1.2
+	 */
+	public static IObservableFactory listFactory(String propertyName,
+			Class elementType) {
+		return listFactory(Realm.getDefault(), propertyName, elementType);
+	}
+
+	/**
+	 * Returns a factory for creating observable sets in the given realm,
+	 * tracking the given property of a particular pojo object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set factory
+	 */
+	public static IObservableFactory setFactory(final Realm realm,
+			final String propertyName) {
+		return PojoProperties.set(propertyName).setFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable sets in the current default
+	 * realm, tracking the given property of a particular pojo object
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @return an observable set factory
+	 * @since 1.2
+	 */
+	public static IObservableFactory setFactory(String propertyName) {
+		return setFactory(Realm.getDefault(), propertyName);
+	}
+
+	/**
+	 * Returns a factory for creating observable set in the given realm,
+	 * tracking the given property of a particular pojo object
+	 * 
+	 * @param realm
+	 *            the realm to use
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable set factory for creating observable sets
+	 */
+	public static IObservableFactory setFactory(final Realm realm,
+			final String propertyName, final Class elementType) {
+		return PojoProperties.set(propertyName, elementType).setFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating observable set in the current default
+	 * realm, tracking the given property of a particular pojo object
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @param elementType
+	 *            type of the elements in the set. If <code>null</code> and the
+	 *            property is an array the type will be inferred. If
+	 *            <code>null</code> and the property type cannot be inferred
+	 *            element type will be <code>null</code>.
+	 * @return an observable set factory for creating observable sets
+	 * @since 1.2
+	 */
+	public static IObservableFactory setFactory(String propertyName,
+			Class elementType) {
+		return setFactory(Realm.getDefault(), propertyName, elementType);
+	}
+
+	/**
+	 * Returns a factory for creating an observable map. The factory, when
+	 * provided with a pojo object, will create an {@link IObservableMap} in the
+	 * given realm that tracks the map-typed named property for the specified
+	 * pojo.
+	 * 
+	 * @param realm
+	 *            the realm assigned to observables created by the returned
+	 *            factory.
+	 * @param propertyName
+	 *            the name of the property
+	 * @return a factory for creating {@link IObservableMap} objects.
+	 */
+	public static IObservableFactory mapPropertyFactory(final Realm realm,
+			final String propertyName) {
+		return PojoProperties.map(propertyName).mapFactory(realm);
+	}
+
+	/**
+	 * Returns a factory for creating an observable map. The factory, when
+	 * provided with a pojo object, will create an {@link IObservableMap} in the
+	 * current default realm that tracks the map-typed named property for the
+	 * specified pojo.
+	 * 
+	 * @param propertyName
+	 *            the name of the property
+	 * @return a factory for creating {@link IObservableMap} objects.
+	 * @since 1.2
+	 */
+	public static IObservableFactory mapPropertyFactory(String propertyName) {
+		return mapPropertyFactory(Realm.getDefault(), propertyName);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailValue(master, valueFactory(realm,
+	 propertyName), propertyType)</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable value that tracks the current value of the named
+	 *         property for the current value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @deprecated Use
+	 *             {@link #observeDetailValue(IObservableValue, String, Class)}
+	 *             instead
+	 */
+	public static IObservableValue observeDetailValue(Realm realm,
+			IObservableValue master, String propertyName, Class propertyType) {
+		BeansObservables.warnIfDifferentRealms(realm, master.getRealm());
+
+		IObservableValue value = MasterDetailObservables.detailValue(master,
+				PojoProperties.value(propertyName, propertyType).valueFactory(
+						realm), propertyType);
+		return new BeanObservableValueDecorator(value, BeanPropertyHelper
+				.getValueTypePropertyDescriptor(master, propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailValue(master, valueFactory(master.getRealm, propertyName), propertyType)</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable value that tracks the current value of the named
+	 *         property for the current value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableValue observeDetailValue(IObservableValue master,
+			String propertyName, Class propertyType) {
+		Class pojoClass = null;
+		if (master.getValueType() instanceof Class)
+			pojoClass = (Class) master.getValueType();
+		return PojoProperties.value(pojoClass, propertyName, propertyType)
+				.observeDetail(master);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailList(master, listFactory(realm,
+	 propertyName, propertyType), propertyType)</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable list that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @deprecated Use
+	 *             {@link #observeDetailList(IObservableValue, String, Class)}
+	 *             instead
+	 */
+	public static IObservableList observeDetailList(Realm realm,
+			IObservableValue master, String propertyName, Class propertyType) {
+		BeansObservables.warnIfDifferentRealms(realm, master.getRealm());
+		IObservableList observableList = MasterDetailObservables.detailList(
+				master, PojoProperties.list(propertyName, propertyType)
+						.listFactory(realm), propertyType);
+		return new BeanObservableListDecorator(observableList,
+				BeanPropertyHelper.getValueTypePropertyDescriptor(master,
+						propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailList(master, listFactory(master.getRealm(), propertyName, propertyType), propertyType)</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable list that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableList observeDetailList(IObservableValue master,
+			String propertyName, Class propertyType) {
+		Class pojoClass = null;
+		if (master.getValueType() instanceof Class)
+			pojoClass = (Class) master.getValueType();
+		return PojoProperties.list(pojoClass, propertyName).observeDetail(
+				master);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailSet(master, setFactory(realm,
+	 propertyName), propertyType)</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable set that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @deprecated Use
+	 *             {@link #observeDetailSet(IObservableValue, String, Class)}
+	 *             instead.
+	 */
+	public static IObservableSet observeDetailSet(Realm realm,
+			IObservableValue master, String propertyName, Class propertyType) {
+		BeansObservables.warnIfDifferentRealms(realm, master.getRealm());
+
+		IObservableSet observableSet = MasterDetailObservables.detailSet(
+				master, PojoProperties.set(propertyName, propertyType)
+						.setFactory(realm), propertyType);
+		return new BeanObservableSetDecorator(observableSet, BeanPropertyHelper
+				.getValueTypePropertyDescriptor(master, propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailSet(master, setFactory(master.getRealm(), propertyName), propertyType)</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 * @param propertyType
+	 *            can be <code>null</code>
+	 * @return an observable set that tracks the named property for the current
+	 *         value of the master observable value
+	 * 
+	 * @see MasterDetailObservables
+	 * @since 1.2
+	 */
+	public static IObservableSet observeDetailSet(IObservableValue master,
+			String propertyName, Class propertyType) {
+		Class pojoClass = null;
+		if (master.getValueType() instanceof Class)
+			pojoClass = (Class) master.getValueType();
+		return PojoProperties.set(pojoClass, propertyName, propertyType)
+				.observeDetail(master);
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailMap(master, mapFactory(realm, propertyName))</code>
+	 * 
+	 * @param realm
+	 * @param master
+	 * @param propertyName
+	 * @return an observable map that tracks the map-type named property for the
+	 *         current value of the master observable value.
+	 * @deprecated Use {@link #observeDetailMap(IObservableValue, String)}
+	 *             instead
+	 */
+	public static IObservableMap observeDetailMap(Realm realm,
+			IObservableValue master, String propertyName) {
+		BeansObservables.warnIfDifferentRealms(realm, master.getRealm());
+		IObservableMap observableMap = MasterDetailObservables.detailMap(
+				master, PojoProperties.map(propertyName).mapFactory(realm));
+		return new BeanObservableMapDecorator(observableMap, BeanPropertyHelper
+				.getValueTypePropertyDescriptor(master, propertyName));
+	}
+
+	/**
+	 * Helper method for
+	 * <code>MasterDetailObservables.detailMap(master, mapFactory(master.getRealm(), propertyName))</code>
+	 * 
+	 * @param master
+	 * @param propertyName
+	 * @return an observable map that tracks the map-type named property for the
+	 *         current value of the master observable value.
+	 * @since 1.2
+	 */
+	public static IObservableMap observeDetailMap(IObservableValue master,
+			String propertyName) {
+		Class pojoClass = null;
+		if (master.getValueType() instanceof Class)
+			pojoClass = (Class) master.getValueType();
+		return PojoProperties.map(pojoClass, propertyName)
+				.observeDetail(master);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/PojoProperties.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/PojoProperties.java
new file mode 100644
index 0000000..90ccdf6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/PojoProperties.java
@@ -0,0 +1,403 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 247997, 261843, 264307
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousPojoListProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousPojoMapProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousPojoSetProperty;
+import org.eclipse.core.internal.databinding.beans.AnonymousPojoValueProperty;
+import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
+import org.eclipse.core.internal.databinding.beans.PojoListProperty;
+import org.eclipse.core.internal.databinding.beans.PojoListPropertyDecorator;
+import org.eclipse.core.internal.databinding.beans.PojoMapProperty;
+import org.eclipse.core.internal.databinding.beans.PojoMapPropertyDecorator;
+import org.eclipse.core.internal.databinding.beans.PojoSetProperty;
+import org.eclipse.core.internal.databinding.beans.PojoSetPropertyDecorator;
+import org.eclipse.core.internal.databinding.beans.PojoValueProperty;
+import org.eclipse.core.internal.databinding.beans.PojoValuePropertyDecorator;
+
+/**
+ * A factory for creating properties for POJOs (plain old java objects) that
+ * conform to idea of an object with getters and setters but does not provide
+ * {@link PropertyChangeEvent property change events} on change. This factory is
+ * identical to {@link BeanProperties} except for this fact.
+ * 
+ * @since 1.2
+ */
+public class PojoProperties {
+	/**
+	 * Returns a value property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains null.
+	 * 
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @return a value property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(String propertyName) {
+		return value(null, propertyName, null);
+	}
+
+	/**
+	 * Returns a value property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains null.
+	 * 
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param valueType
+	 *            the value type of the returned value property
+	 * @return a value property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(String propertyName, Class valueType) {
+		return value(null, propertyName, valueType);
+	}
+
+	/**
+	 * Returns a value property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @return a value property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(Class beanClass, String propertyName) {
+		return value(beanClass, propertyName, null);
+	}
+
+	/**
+	 * Returns a value property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name. May be nested e.g. "parent.name"
+	 * @param valueType
+	 *            the value type of the returned value property
+	 * @return a value property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanValueProperty value(Class beanClass,
+			String propertyName, Class valueType) {
+		String[] propertyNames = split(propertyName);
+		if (propertyNames.length > 1)
+			valueType = null;
+
+		IValueProperty property;
+		PropertyDescriptor propertyDescriptor;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new PojoValuePropertyDecorator(
+					new AnonymousPojoValueProperty(propertyNames[0], valueType),
+					null);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyNames[0]);
+			property = new PojoValueProperty(propertyDescriptor, valueType);
+		}
+
+		IBeanValueProperty beanProperty = new PojoValuePropertyDecorator(
+				property, propertyDescriptor);
+		for (int i = 1; i < propertyNames.length; i++) {
+			beanProperty = beanProperty.value(propertyNames[i]);
+		}
+		return beanProperty;
+	}
+
+	private static String[] split(String propertyName) {
+		if (propertyName.indexOf('.') == -1)
+			return new String[] { propertyName };
+		List propertyNames = new ArrayList();
+		int index;
+		while ((index = propertyName.indexOf('.')) != -1) {
+			propertyNames.add(propertyName.substring(0, index));
+			propertyName = propertyName.substring(index + 1);
+		}
+		propertyNames.add(propertyName);
+		return (String[]) propertyNames
+				.toArray(new String[propertyNames.size()]);
+	}
+
+	/**
+	 * Returns a value property array for the given property names of the given
+	 * bean class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyNames
+	 *            array of property names. May be nested e.g. "parent.name"
+	 * @return a value property array for the given property names of the given
+	 *         bean class.
+	 */
+	public static IBeanValueProperty[] values(Class beanClass,
+			String[] propertyNames) {
+		IBeanValueProperty[] properties = new IBeanValueProperty[propertyNames.length];
+		for (int i = 0; i < properties.length; i++)
+			properties[i] = value(beanClass, propertyNames[i], null);
+		return properties;
+	}
+
+	/**
+	 * Returns a value property array for the given property names of an
+	 * arbitrary bean class.
+	 * 
+	 * @param propertyNames
+	 *            array of property names. May be nested e.g. "parent.name"
+	 * @return a value property array for the given property names of the given
+	 *         bean class.
+	 */
+	public static IBeanValueProperty[] values(String[] propertyNames) {
+		return values(null, propertyNames);
+	}
+
+	/**
+	 * Returns a set property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty set.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @return a set property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(String propertyName) {
+		return set(null, propertyName, null);
+	}
+
+	/**
+	 * Returns a set property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty set.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned set property
+	 * @return a set property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(String propertyName, Class elementType) {
+		return set(null, propertyName, elementType);
+	}
+
+	/**
+	 * Returns a set property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @return a set property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(Class beanClass, String propertyName) {
+		return set(beanClass, propertyName, null);
+	}
+
+	/**
+	 * Returns a set property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned set property
+	 * @return a set property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanSetProperty set(Class beanClass, String propertyName,
+			Class elementType) {
+		PropertyDescriptor propertyDescriptor;
+		ISetProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousPojoSetProperty(propertyName, elementType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyName);
+			property = new PojoSetProperty(propertyDescriptor, elementType);
+		}
+		return new PojoSetPropertyDecorator(property, propertyDescriptor);
+	}
+
+	/**
+	 * Returns a list property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty list.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @return a list property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(String propertyName) {
+		return list(null, propertyName, null);
+	}
+
+	/**
+	 * Returns a list property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty list.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned list property
+	 * @return a list property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(String propertyName, Class elementType) {
+		return list(null, propertyName, elementType);
+	}
+
+	/**
+	 * Returns a list property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @return a list property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(Class beanClass, String propertyName) {
+		return list(beanClass, propertyName, null);
+	}
+
+	/**
+	 * Returns a list property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @param elementType
+	 *            the element type of the returned list property
+	 * @return a list property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanListProperty list(Class beanClass, String propertyName,
+			Class elementType) {
+		PropertyDescriptor propertyDescriptor;
+		IListProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousPojoListProperty(propertyName, elementType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyName);
+			property = new PojoListProperty(propertyDescriptor, elementType);
+		}
+		return new PojoListPropertyDecorator(property, propertyDescriptor);
+	}
+
+	/**
+	 * Returns a map property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty map.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @return a map property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(String propertyName) {
+		return map(null, propertyName, null, null);
+	}
+
+	/**
+	 * Returns a map property for the given property name of an arbitrary bean
+	 * class. Objects lacking the named property are treated the same as if the
+	 * property always contains an empty map.
+	 * 
+	 * @param propertyName
+	 *            the property name
+	 * @param keyType
+	 *            the key type for the returned map property
+	 * @param valueType
+	 *            the value type for the returned map property
+	 * @return a map property for the given property name of an arbitrary bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(String propertyName, Class keyType,
+			Class valueType) {
+		return map(null, propertyName, keyType, valueType);
+	}
+
+	/**
+	 * Returns a map property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @return a map property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(Class beanClass, String propertyName) {
+		return map(beanClass, propertyName, null, null);
+	}
+
+	/**
+	 * Returns a map property for the given property name of the given bean
+	 * class.
+	 * 
+	 * @param beanClass
+	 *            the bean class
+	 * @param propertyName
+	 *            the property name
+	 * @param keyType
+	 *            the key type of the returned map property
+	 * @param valueType
+	 *            the value type of the returned map property
+	 * @return a map property for the given property name of the given bean
+	 *         class.
+	 */
+	public static IBeanMapProperty map(Class beanClass, String propertyName,
+			Class keyType, Class valueType) {
+		PropertyDescriptor propertyDescriptor;
+		IMapProperty property;
+		if (beanClass == null) {
+			propertyDescriptor = null;
+			property = new AnonymousPojoMapProperty(propertyName, keyType,
+					valueType);
+		} else {
+			propertyDescriptor = BeanPropertyHelper.getPropertyDescriptor(
+					beanClass, propertyName);
+			property = new PojoMapProperty(propertyDescriptor, keyType,
+					valueType);
+		}
+		return new PojoMapPropertyDecorator(property, propertyDescriptor);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/package.html b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/package.html
new file mode 100644
index 0000000..c290e49
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/databinding/beans/package.html
@@ -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 JavaBeans(tm) objects.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to observe objects that conform to the <a href="http://java.sun.com/products/javabeans/docs/spec.html">JavaBean specification</a> for bound properties.</p>
+</body>
+</html>
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanListProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanListProperty.java
new file mode 100644
index 0000000..3c81add
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanListProperty.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.property.list.DelegatingListProperty;
+import org.eclipse.core.databinding.property.list.IListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousBeanListProperty extends DelegatingListProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param elementType
+	 */
+	public AnonymousBeanListProperty(String propertyName, Class elementType) {
+		super(elementType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected IListProperty doGetDelegate(Object source) {
+		Class beanClass = source.getClass();
+		if (delegates.containsKey(beanClass))
+			return (IListProperty) delegates.get(beanClass);
+
+		IListProperty delegate;
+		try {
+			delegate = BeanProperties.list(beanClass, propertyName,
+					(Class) getElementType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName + "[]"; //$NON-NLS-1$ //$NON-NLS-2$
+		Class elementType = (Class) getElementType();
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanMapProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanMapProperty.java
new file mode 100644
index 0000000..06d8a52
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanMapProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.property.map.DelegatingMapProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousBeanMapProperty extends DelegatingMapProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param keyType
+	 * @param valueType
+	 */
+	public AnonymousBeanMapProperty(String propertyName, Class keyType,
+			Class valueType) {
+		super(keyType, valueType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected IMapProperty doGetDelegate(Object source) {
+		Class beanClass = source.getClass();
+		if (delegates.containsKey(beanClass))
+			return (IMapProperty) delegates.get(beanClass);
+
+		IMapProperty delegate;
+		try {
+			delegate = BeanProperties.map(beanClass, propertyName,
+					(Class) getKeyType(), (Class) getValueType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName + "{:}"; //$NON-NLS-1$ //$NON-NLS-2$
+		Class keyType = (Class) getKeyType();
+		Class valueType = (Class) getValueType();
+		if (keyType != null || valueType != null) {
+			s += " <" + BeanPropertyHelper.shortClassName(keyType) + ", " //$NON-NLS-1$//$NON-NLS-2$
+					+ BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$
+		}
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanSetProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanSetProperty.java
new file mode 100644
index 0000000..6ef3b48
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanSetProperty.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.property.set.DelegatingSetProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousBeanSetProperty extends DelegatingSetProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param elementType
+	 */
+	public AnonymousBeanSetProperty(String propertyName, Class elementType) {
+		super(elementType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected ISetProperty doGetDelegate(Object source) {
+		Class beanClass = source.getClass();
+		if (delegates.containsKey(beanClass))
+			return (ISetProperty) delegates.get(beanClass);
+
+		ISetProperty delegate;
+		try {
+			delegate = BeanProperties.set(beanClass, propertyName,
+					(Class) getElementType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName + "{}"; //$NON-NLS-1$ //$NON-NLS-2$
+		Class elementType = (Class) getElementType();
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanValueProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanValueProperty.java
new file mode 100644
index 0000000..38fd3b3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousBeanValueProperty.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bugs 264307, 264619
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousBeanValueProperty extends DelegatingValueProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param valueType
+	 */
+	public AnonymousBeanValueProperty(String propertyName, Class valueType) {
+		super(valueType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		return getClassDelegate(source.getClass());
+	}
+
+	private IValueProperty getClassDelegate(Class beanClass) {
+		if (delegates.containsKey(beanClass))
+			return (IValueProperty) delegates.get(beanClass);
+
+		IValueProperty delegate;
+		try {
+			delegate = BeanProperties.value(beanClass, propertyName,
+					(Class) getValueType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public IObservableValue observeDetail(IObservableValue master) {
+		Object valueType = getValueType();
+		if (valueType == null)
+			valueType = inferValueType(master.getValueType());
+		return MasterDetailObservables.detailValue(master, valueFactory(master
+				.getRealm()), valueType);
+	}
+
+	private Object inferValueType(Object masterObservableValueType) {
+		if (masterObservableValueType instanceof Class) {
+			return getClassDelegate((Class) masterObservableValueType)
+					.getValueType();
+		}
+		return null;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName; //$NON-NLS-1$
+		Class valueType = (Class) getValueType();
+		if (valueType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoListProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoListProperty.java
new file mode 100644
index 0000000..9abc7df
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoListProperty.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.property.list.DelegatingListProperty;
+import org.eclipse.core.databinding.property.list.IListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousPojoListProperty extends DelegatingListProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param elementType
+	 */
+	public AnonymousPojoListProperty(String propertyName, Class elementType) {
+		super(elementType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected IListProperty doGetDelegate(Object source) {
+		Class beanClass = source.getClass();
+		if (delegates.containsKey(beanClass))
+			return (IListProperty) delegates.get(beanClass);
+
+		IListProperty delegate;
+		try {
+			delegate = PojoProperties.list(beanClass, propertyName,
+					(Class) getElementType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName + "{}"; //$NON-NLS-1$ //$NON-NLS-2$
+		Class elementType = (Class) getElementType();
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoMapProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoMapProperty.java
new file mode 100644
index 0000000..10ce37d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoMapProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.property.map.DelegatingMapProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousPojoMapProperty extends DelegatingMapProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param keyType
+	 * @param valueType
+	 */
+	public AnonymousPojoMapProperty(String propertyName, Class keyType,
+			Class valueType) {
+		super(keyType, valueType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected IMapProperty doGetDelegate(Object source) {
+		Class beanClass = source.getClass();
+		if (delegates.containsKey(beanClass))
+			return (IMapProperty) delegates.get(beanClass);
+
+		IMapProperty delegate;
+		try {
+			delegate = PojoProperties.map(beanClass, propertyName,
+					(Class) getKeyType(), (Class) getValueType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName + "{:}"; //$NON-NLS-1$ //$NON-NLS-2$
+		Class keyType = (Class) getKeyType();
+		Class valueType = (Class) getValueType();
+		if (keyType != null || valueType != null) {
+			s += "<" + BeanPropertyHelper.shortClassName(keyType) + ", " //$NON-NLS-1$//$NON-NLS-2$
+					+ BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$
+		}
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoSetProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoSetProperty.java
new file mode 100644
index 0000000..7f33181
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoSetProperty.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.property.set.DelegatingSetProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousPojoSetProperty extends DelegatingSetProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param elementType
+	 */
+	public AnonymousPojoSetProperty(String propertyName, Class elementType) {
+		super(elementType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected ISetProperty doGetDelegate(Object source) {
+		Class beanClass = source.getClass();
+		if (delegates.containsKey(beanClass))
+			return (ISetProperty) delegates.get(beanClass);
+
+		ISetProperty delegate;
+		try {
+			delegate = PojoProperties.set(beanClass, propertyName,
+					(Class) getElementType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(beanClass, delegate);
+		return delegate;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName + "{}"; //$NON-NLS-1$ //$NON-NLS-2$
+		Class elementType = (Class) getElementType();
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoValueProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoValueProperty.java
new file mode 100644
index 0000000..2d55936
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/AnonymousPojoValueProperty.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247997)
+ *     Matthew Hall - bugs 264307, 264619
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class AnonymousPojoValueProperty extends DelegatingValueProperty {
+	private final String propertyName;
+
+	private Map delegates;
+
+	/**
+	 * @param propertyName
+	 * @param valueType
+	 */
+	public AnonymousPojoValueProperty(String propertyName, Class valueType) {
+		super(valueType);
+		this.propertyName = propertyName;
+		this.delegates = new HashMap();
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		return getClassDelegate(source.getClass());
+	}
+
+	private IValueProperty getClassDelegate(Class pojoClass) {
+		if (delegates.containsKey(pojoClass))
+			return (IValueProperty) delegates.get(pojoClass);
+
+		IValueProperty delegate;
+		try {
+			delegate = PojoProperties.value(pojoClass, propertyName,
+					(Class) getValueType());
+		} catch (IllegalArgumentException noSuchProperty) {
+			delegate = null;
+		}
+		delegates.put(pojoClass, delegate);
+		return delegate;
+	}
+
+	public IObservableValue observeDetail(IObservableValue master) {
+		Object valueType = getValueType();
+		if (valueType == null)
+			valueType = inferValueType(master.getValueType());
+		return MasterDetailObservables.detailValue(master, valueFactory(master
+				.getRealm()), valueType);
+	}
+
+	private Object inferValueType(Object masterObservableValueType) {
+		if (masterObservableValueType instanceof Class) {
+			return getClassDelegate((Class) masterObservableValueType)
+					.getValueType();
+		}
+		return null;
+	}
+
+	public String toString() {
+		String s = "?." + propertyName; //$NON-NLS-1$
+		Class valueType = (Class) getValueType();
+		if (valueType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanListProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanListProperty.java
new file mode 100644
index 0000000..281891f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanListProperty.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 264307, 265561, 301774
+ *     Ovidio Mallo - bug 306633
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanListProperty extends SimpleListProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class elementType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param elementType
+	 */
+	public BeanListProperty(PropertyDescriptor propertyDescriptor,
+			Class elementType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.elementType = elementType == null ? BeanPropertyHelper
+				.getCollectionPropertyElementType(propertyDescriptor)
+				: elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected List doGetList(Object source) {
+		return asList(BeanPropertyHelper.readProperty(source,
+				propertyDescriptor));
+	}
+
+	private List asList(Object propertyValue) {
+		if (propertyValue == null)
+			return Collections.EMPTY_LIST;
+		if (propertyDescriptor.getPropertyType().isArray())
+			return Arrays.asList((Object[]) propertyValue);
+		return (List) propertyValue;
+	}
+
+	protected void doSetList(Object source, List list, ListDiff diff) {
+		doSetList(source, list);
+	}
+
+	protected void doSetList(Object source, List list) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor,
+				convertListToBeanPropertyType(list));
+	}
+
+	private Object convertListToBeanPropertyType(List list) {
+		Object propertyValue = list;
+		if (propertyDescriptor.getPropertyType().isArray()) {
+			Class componentType = propertyDescriptor.getPropertyType()
+					.getComponentType();
+			Object[] array = (Object[]) Array.newInstance(componentType, list
+					.size());
+			list.toArray(array);
+			propertyValue = array;
+		}
+		return propertyValue;
+	}
+
+	public INativePropertyListener adaptListener(
+			final ISimplePropertyListener listener) {
+		return new BeanPropertyListener(this, propertyDescriptor, listener) {
+			protected IDiff computeDiff(Object oldValue, Object newValue) {
+				return Diffs
+						.computeListDiff(asList(oldValue), asList(newValue));
+			}
+		};
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor) + "[]"; //$NON-NLS-1$
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanListPropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanListPropertyDecorator.java
new file mode 100644
index 0000000..d54bbdc
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanListPropertyDecorator.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanListProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.list.ListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanListPropertyDecorator extends ListProperty implements
+		IBeanListProperty {
+	private final IListProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public BeanListPropertyDecorator(IListProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public Object getElementType() {
+		return delegate.getElementType();
+	}
+
+	protected List doGetList(Object source) {
+		return delegate.getList(source);
+	}
+
+	protected void doSetList(Object source, List list) {
+		delegate.setList(source, list);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		delegate.updateList(source, diff);
+	}
+
+	public IBeanListProperty values(String propertyName) {
+		return values(propertyName, null);
+	}
+
+	public IBeanListProperty values(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getElementType();
+		return values(BeanProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanListProperty values(IBeanValueProperty property) {
+		return new BeanListPropertyDecorator(super.values(property),
+				property.getPropertyDescriptor());
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public IObservableList observe(Object source) {
+		return new BeanObservableListDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return new BeanObservableListDecorator(delegate.observe(realm, source),
+				propertyDescriptor);
+	}
+
+	public IObservableList observeDetail(IObservableValue master) {
+		return new BeanObservableListDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanMapProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanMapProperty.java
new file mode 100644
index 0000000..99c247a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanMapProperty.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 264307, 265561, 301774
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.map.SimpleMapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanMapProperty extends SimpleMapProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class keyType;
+	private final Class valueType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param keyType
+	 * @param valueType
+	 */
+	public BeanMapProperty(PropertyDescriptor propertyDescriptor,
+			Class keyType, Class valueType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.keyType = keyType;
+		this.valueType = valueType;
+	}
+
+	public Object getKeyType() {
+		return keyType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Map doGetMap(Object source) {
+		return asMap(BeanPropertyHelper
+				.readProperty(source, propertyDescriptor));
+	}
+
+	private Map asMap(Object propertyValue) {
+		if (propertyValue == null)
+			return Collections.EMPTY_MAP;
+		return (Map) propertyValue;
+	}
+
+	protected void doSetMap(Object source, Map map, MapDiff diff) {
+		doSetMap(source, map);
+	}
+
+	protected void doSetMap(Object source, Map map) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor, map);
+	}
+
+	public INativePropertyListener adaptListener(
+			final ISimplePropertyListener listener) {
+		return new BeanPropertyListener(this, propertyDescriptor, listener) {
+			protected IDiff computeDiff(Object oldValue, Object newValue) {
+				return Diffs.computeMapDiff(asMap(oldValue), asMap(newValue));
+			}
+		};
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor) + "{:}"; //$NON-NLS-1$
+
+		if (keyType != null || valueType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(keyType) + ", " //$NON-NLS-1$ //$NON-NLS-2$
+					+ BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanMapPropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanMapPropertyDecorator.java
new file mode 100644
index 0000000..e7b47c2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanMapPropertyDecorator.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanMapProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.map.MapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanMapPropertyDecorator extends MapProperty implements
+		IBeanMapProperty {
+	private final IMapProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public BeanMapPropertyDecorator(IMapProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public Object getKeyType() {
+		return delegate.getKeyType();
+	}
+
+	public Object getValueType() {
+		return delegate.getValueType();
+	}
+
+	protected Map doGetMap(Object source) {
+		return delegate.getMap(source);
+	}
+
+	protected void doSetMap(Object source, Map map) {
+		delegate.setMap(source, map);
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		delegate.updateMap(source, diff);
+	}
+
+	public IBeanMapProperty values(String propertyName) {
+		return values(propertyName, null);
+	}
+
+	public IBeanMapProperty values(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return values(BeanProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanMapProperty values(IBeanValueProperty property) {
+		return new BeanMapPropertyDecorator(super.values(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IObservableMap observe(Object source) {
+		return new BeanObservableMapDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observe(Realm realm, Object source) {
+		return new BeanObservableMapDecorator(delegate.observe(realm, source),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observeDetail(IObservableValue master) {
+		return new BeanObservableMapDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableListDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableListDecorator.java
new file mode 100644
index 0000000..672399a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableListDecorator.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 208858, 246625
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.list.DecoratingObservableList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+
+/**
+ * {@link IBeanObservable} decorator for an {@link IObservableList}.
+ * 
+ * @since 3.3
+ */
+public class BeanObservableListDecorator extends DecoratingObservableList
+		implements IBeanObservable {
+	private PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param decorated
+	 * @param propertyDescriptor
+	 */
+	public BeanObservableListDecorator(IObservableList decorated,
+			PropertyDescriptor propertyDescriptor) {
+		super(decorated, true);
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public synchronized void dispose() {
+		this.propertyDescriptor = null;
+		super.dispose();
+	}
+
+	public Object getObserved() {
+		IObservable decorated = getDecorated();
+		if (decorated instanceof IObserving)
+			return ((IObserving) decorated).getObserved();
+		return null;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableMapDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableMapDecorator.java
new file mode 100644
index 0000000..f48d193
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableMapDecorator.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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 246625
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.map.DecoratingObservableMap;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+
+/**
+ * {@link IBeanObservable} decorator for an {@link IObservableMap}.
+ * 
+ * @since 3.3
+ */
+public class BeanObservableMapDecorator extends DecoratingObservableMap
+		implements IBeanObservable {
+	private PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param decorated
+	 * @param propertyDescriptor
+	 */
+	public BeanObservableMapDecorator(IObservableMap decorated,
+			PropertyDescriptor propertyDescriptor) {
+		super(decorated, true);
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public synchronized void dispose() {
+		this.propertyDescriptor = null;
+		super.dispose();
+	}
+
+	public Object getObserved() {
+		IObservable decorated = getDecorated();
+		if (decorated instanceof IObserving)
+			return ((IObserving) decorated).getObserved();
+		return null;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableSetDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableSetDecorator.java
new file mode 100644
index 0000000..b560b5e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableSetDecorator.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 246625
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.set.DecoratingObservableSet;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+
+/**
+ * {@link IBeanObservable} decorator for an {@link IObservableSet}.
+ * 
+ * @since 3.3
+ */
+public class BeanObservableSetDecorator extends DecoratingObservableSet
+		implements IBeanObservable {
+	private PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param decorated
+	 * @param propertyDescriptor
+	 */
+	public BeanObservableSetDecorator(IObservableSet decorated,
+			PropertyDescriptor propertyDescriptor) {
+		super(decorated, true);
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public synchronized void dispose() {
+		this.propertyDescriptor = null;
+		super.dispose();
+	}
+
+	public Object getObserved() {
+		IObservable decorated = getDecorated();
+		if (decorated instanceof IObserving)
+			return ((IObserving) decorated).getObserved();
+		return null;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableValueDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableValueDecorator.java
new file mode 100644
index 0000000..e8e2b2f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanObservableValueDecorator.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 246625
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.value.DecoratingObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * {@link IBeanObservable} decorator for an {@link IObservableValue}.
+ * 
+ * @since 3.3
+ */
+public class BeanObservableValueDecorator extends DecoratingObservableValue
+		implements IBeanObservable {
+	private PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param decorated
+	 * @param propertyDescriptor
+	 */
+	public BeanObservableValueDecorator(IObservableValue decorated,
+			PropertyDescriptor propertyDescriptor) {
+		super(decorated, true);
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public synchronized void dispose() {
+		this.propertyDescriptor = null;
+		super.dispose();
+	}
+
+	public Object getObserved() {
+		IObservable decorated = getDecorated();
+		if (decorated instanceof IObserving)
+			return ((IObserving) decorated).getObserved();
+		return null;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyHelper.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyHelper.java
new file mode 100644
index 0000000..83d1ebf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyHelper.java
@@ -0,0 +1,249 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Martin Frey <martin.frey@logica.com> - bug 256150
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * @since 1.2
+ * 
+ */
+public class BeanPropertyHelper {
+	/**
+	 * Sets the contents of the given property on the given source object to the
+	 * given value.
+	 * 
+	 * @param source
+	 *            the source object which has the property being updated
+	 * @param propertyDescriptor
+	 *            the property being changed
+	 * @param value
+	 *            the new value of the property
+	 */
+	public static void writeProperty(Object source,
+			PropertyDescriptor propertyDescriptor, Object value) {
+		try {
+			Method writeMethod = propertyDescriptor.getWriteMethod();
+			if (null == writeMethod) {
+				throw new IllegalArgumentException(
+						"Missing public setter method for " //$NON-NLS-1$
+								+ propertyDescriptor.getName() + " property"); //$NON-NLS-1$
+			}
+			if (!writeMethod.isAccessible()) {
+				writeMethod.setAccessible(true);
+			}
+			writeMethod.invoke(source, new Object[] { value });
+		} catch (InvocationTargetException e) {
+			/*
+			 * InvocationTargetException wraps any exception thrown by the
+			 * invoked method.
+			 */
+			throw new RuntimeException(e.getCause());
+		} catch (Exception e) {
+			if (BeansObservables.DEBUG) {
+				Policy
+						.getLog()
+						.log(
+								new Status(
+										IStatus.WARNING,
+										Policy.JFACE_DATABINDING,
+										IStatus.OK,
+										"Could not change value of " + source + "." + propertyDescriptor.getName(), e)); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+	}
+
+	/**
+	 * Returns the contents of the given property for the given bean.
+	 * 
+	 * @param source
+	 *            the source bean
+	 * @param propertyDescriptor
+	 *            the property to retrieve
+	 * @return the contents of the given property for the given bean.
+	 */
+	public static Object readProperty(Object source,
+			PropertyDescriptor propertyDescriptor) {
+		try {
+			Method readMethod = propertyDescriptor.getReadMethod();
+			if (readMethod == null) {
+				throw new IllegalArgumentException(propertyDescriptor.getName()
+						+ " property does not have a read method."); //$NON-NLS-1$
+			}
+			if (!readMethod.isAccessible()) {
+				readMethod.setAccessible(true);
+			}
+			return readMethod.invoke(source, null);
+		} catch (InvocationTargetException e) {
+			/*
+			 * InvocationTargetException wraps any exception thrown by the
+			 * invoked method.
+			 */
+			throw new RuntimeException(e.getCause());
+		} catch (Exception e) {
+			if (BeansObservables.DEBUG) {
+				Policy
+						.getLog()
+						.log(
+								new Status(
+										IStatus.WARNING,
+										Policy.JFACE_DATABINDING,
+										IStatus.OK,
+										"Could not read value of " + source + "." + propertyDescriptor.getName(), e)); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+			return null;
+		}
+	}
+
+	/**
+	 * Returns the element type of the given collection-typed property for the
+	 * given bean.
+	 * 
+	 * @param descriptor
+	 *            the property being inspected
+	 * @return the element type of the given collection-typed property if it is
+	 *         an array property, or Object.class otherwise.
+	 */
+	public static Class getCollectionPropertyElementType(
+			PropertyDescriptor descriptor) {
+		Class propertyType = descriptor.getPropertyType();
+		return propertyType.isArray() ? propertyType.getComponentType()
+				: Object.class;
+	}
+
+	/**
+	 * @param beanClass
+	 * @param propertyName
+	 * @return the PropertyDescriptor for the named property on the given bean
+	 *         class
+	 */
+	public static PropertyDescriptor getPropertyDescriptor(Class beanClass,
+			String propertyName) {
+		if (!beanClass.isInterface()) {
+			BeanInfo beanInfo;
+			try {
+				beanInfo = Introspector.getBeanInfo(beanClass);
+			} catch (IntrospectionException e) {
+				// cannot introspect, give up
+				return null;
+			}
+			PropertyDescriptor[] propertyDescriptors = beanInfo
+					.getPropertyDescriptors();
+			for (int i = 0; i < propertyDescriptors.length; i++) {
+				PropertyDescriptor descriptor = propertyDescriptors[i];
+				if (descriptor.getName().equals(propertyName)) {
+					return descriptor;
+				}
+			}
+		} else {
+			try {
+				PropertyDescriptor propertyDescriptors[];
+				List pds = new ArrayList();
+				getInterfacePropertyDescriptors(pds, beanClass);
+				if (pds.size() > 0) {
+					propertyDescriptors = (PropertyDescriptor[]) pds
+							.toArray(new PropertyDescriptor[pds.size()]);
+					PropertyDescriptor descriptor;
+					for (int i = 0; i < propertyDescriptors.length; i++) {
+						descriptor = propertyDescriptors[i];
+						if (descriptor.getName().equals(propertyName))
+							return descriptor;
+					}
+				}
+			} catch (IntrospectionException e) {
+				// cannot introspect, give up
+				return null;
+			}
+		}
+		throw new IllegalArgumentException(
+				"Could not find property with name " + propertyName + " in class " + beanClass); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Goes recursively into the interface and gets all defined
+	 * propertyDescriptors
+	 * 
+	 * @param propertyDescriptors
+	 *            The result list of all PropertyDescriptors the given interface
+	 *            defines (hierarchical)
+	 * @param iface
+	 *            The interface to fetch the PropertyDescriptors
+	 * @throws IntrospectionException
+	 */
+	private static void getInterfacePropertyDescriptors(
+			List propertyDescriptors, Class iface)
+			throws IntrospectionException {
+		BeanInfo beanInfo = Introspector.getBeanInfo(iface);
+		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
+		for (int i = 0; i < pds.length; i++) {
+			PropertyDescriptor pd = pds[i];
+			propertyDescriptors.add(pd);
+		}
+		Class[] subIntfs = iface.getInterfaces();
+		for (int j = 0; j < subIntfs.length; j++) {
+			getInterfacePropertyDescriptors(propertyDescriptors, subIntfs[j]);
+		}
+	}
+
+	/**
+	 * @param observable
+	 * @param propertyName
+	 * @return property descriptor or <code>null</code>
+	 */
+	/* package */public static PropertyDescriptor getValueTypePropertyDescriptor(
+			IObservableValue observable, String propertyName) {
+		if (observable.getValueType() != null)
+			return getPropertyDescriptor((Class) observable.getValueType(),
+					propertyName);
+		return null;
+	}
+
+	/**
+	 * @param propertyDescriptor
+	 * @return String description of property descriptor
+	 */
+	public static String propertyName(PropertyDescriptor propertyDescriptor) {
+		Class beanClass = propertyDescriptor.getReadMethod()
+				.getDeclaringClass();
+		return shortClassName(beanClass)
+				+ "." + propertyDescriptor.getName() + ""; //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * @param beanClass
+	 * @return class name excluding package
+	 */
+	public static String shortClassName(Class beanClass) {
+		if (beanClass == null)
+			return "?"; //$NON-NLS-1$
+		String className = beanClass.getName();
+		int lastDot = className.lastIndexOf('.');
+		if (lastDot != -1)
+			className = className.substring(lastDot + 1);
+		return className;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyListener.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyListener.java
new file mode 100644
index 0000000..01b3275
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyListener.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 265561)
+ *     Matthew Hall - bug 268336
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class BeanPropertyListener extends NativePropertyListener
+		implements PropertyChangeListener {
+	private final PropertyDescriptor propertyDescriptor;
+
+	protected BeanPropertyListener(IProperty property,
+			PropertyDescriptor propertyDescriptor,
+			ISimplePropertyListener listener) {
+		super(property, listener);
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public void propertyChange(java.beans.PropertyChangeEvent evt) {
+		if (evt.getPropertyName() == null
+				|| propertyDescriptor.getName().equals(evt.getPropertyName())) {
+			Object oldValue = evt.getOldValue();
+			Object newValue = evt.getNewValue();
+			IDiff diff;
+			if (evt.getPropertyName() == null || oldValue == null
+					|| newValue == null)
+				diff = null;
+			else
+				diff = computeDiff(oldValue, newValue);
+			fireChange(evt.getSource(), diff);
+		}
+	}
+
+	protected abstract IDiff computeDiff(Object oldValue, Object newValue);
+
+	protected void doAddTo(Object source) {
+		BeanPropertyListenerSupport.hookListener(source, propertyDescriptor
+				.getName(), this);
+	}
+
+	protected void doRemoveFrom(Object source) {
+		BeanPropertyListenerSupport.unhookListener(source, propertyDescriptor
+				.getName(), this);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyListenerSupport.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyListenerSupport.java
new file mode 100644
index 0000000..23975db
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanPropertyListenerSupport.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * This is a helper that will hook up and listen for
+ * <code>PropertyChangeEvent</code> events for a set of target JavaBeans
+ * 
+ * @since 1.0
+ */
+public class BeanPropertyListenerSupport {
+	/**
+	 * Start listen to target (if it supports the JavaBean property change
+	 * listener pattern)
+	 * 
+	 * @param bean
+	 * @param propertyName
+	 * @param listener
+	 */
+	public static void hookListener(Object bean, String propertyName,
+			PropertyChangeListener listener) {
+		Assert.isNotNull(bean, "Bean cannot be null"); //$NON-NLS-1$
+		Assert.isNotNull(listener, "Listener cannot be null"); //$NON-NLS-1$
+		Assert.isNotNull(propertyName, "Property name cannot be null"); //$NON-NLS-1$
+		processListener(bean, propertyName, listener,
+				"addPropertyChangeListener", "Could not attach listener to ");//$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Stop listen to target
+	 * 
+	 * @param bean
+	 * @param propertyName
+	 * @param listener
+	 */
+	public static void unhookListener(Object bean, String propertyName,
+			PropertyChangeListener listener) {
+		Assert.isNotNull(bean, "Bean cannot be null"); //$NON-NLS-1$
+		Assert.isNotNull(listener, "Listener cannot be null"); //$NON-NLS-1$
+		Assert.isNotNull(propertyName, "Property name cannot be null"); //$NON-NLS-1$
+
+		processListener(
+				bean,
+				propertyName,
+				listener,
+				"removePropertyChangeListener", "Cound not remove listener from "); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Invokes the method for the provided <code>methodName</code> attempting to
+	 * first use the method with the property name and then the unnamed version.
+	 * 
+	 * @param bean
+	 *            object to invoke the method on
+	 * @param methodName
+	 *            either addPropertyChangeListener or
+	 *            removePropertyChangeListener
+	 * @param message
+	 *            string that will be prefixed to the target in an error message
+	 * 
+	 * @return <code>true</code> if the method was invoked successfully
+	 */
+	private static boolean processListener(Object bean, String propertyName,
+			PropertyChangeListener listener, String methodName, String message) {
+		Method method = null;
+		Object[] parameters = null;
+
+		try {
+			try {
+				method = bean.getClass().getMethod(
+						methodName,
+						new Class[] { String.class,
+								PropertyChangeListener.class });
+
+				parameters = new Object[] { propertyName, listener };
+			} catch (NoSuchMethodException e) {
+				method = bean.getClass().getMethod(methodName,
+						new Class[] { PropertyChangeListener.class });
+
+				parameters = new Object[] { listener };
+			}
+		} catch (SecurityException e) {
+			// ignore
+		} catch (NoSuchMethodException e) {
+			log(IStatus.WARNING, message + bean, e);
+		}
+
+		if (method != null) {
+			if (!method.isAccessible()) {
+				method.setAccessible(true);
+			}
+			try {
+				method.invoke(bean, parameters);
+				return true;
+			} catch (IllegalArgumentException e) {
+				log(IStatus.WARNING, message + bean, e);
+			} catch (IllegalAccessException e) {
+				log(IStatus.WARNING, message + bean, e);
+			} catch (InvocationTargetException e) {
+				log(IStatus.WARNING, message + bean, e);
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Logs a message to the Data Binding logger.
+	 */
+	private static void log(int severity, String message, Throwable throwable) {
+		if (BeansObservables.DEBUG) {
+			Policy.getLog().log(
+					new Status(severity, Policy.JFACE_DATABINDING, IStatus.OK,
+							message, throwable));
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanSetProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanSetProperty.java
new file mode 100644
index 0000000..50714cb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanSetProperty.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 264307, 265064, 265561
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+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.IDiff;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanSetProperty extends SimpleSetProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class elementType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param elementType
+	 */
+	public BeanSetProperty(PropertyDescriptor propertyDescriptor,
+			Class elementType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.elementType = elementType == null ? BeanPropertyHelper
+				.getCollectionPropertyElementType(propertyDescriptor)
+				: elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected Set doGetSet(Object source) {
+		return asSet(BeanPropertyHelper
+				.readProperty(source, propertyDescriptor));
+	}
+
+	private Set asSet(Object propertyValue) {
+		if (propertyValue == null)
+			return Collections.EMPTY_SET;
+		if (propertyDescriptor.getPropertyType().isArray())
+			return new HashSet(Arrays.asList((Object[]) propertyValue));
+		return (Set) propertyValue;
+	}
+
+	protected void doSetSet(Object source, Set set, SetDiff diff) {
+		doSetSet(source, set);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor,
+				convertSetToBeanPropertyType(set));
+	}
+
+	private Object convertSetToBeanPropertyType(Set set) {
+		Object propertyValue = set;
+		if (propertyDescriptor.getPropertyType().isArray()) {
+			Class componentType = propertyDescriptor.getPropertyType()
+					.getComponentType();
+			Object[] array = (Object[]) Array.newInstance(componentType, set
+					.size());
+			propertyValue = set.toArray(array);
+		}
+		return propertyValue;
+	}
+
+	public INativePropertyListener adaptListener(
+			final ISimplePropertyListener listener) {
+		return new BeanPropertyListener(this, propertyDescriptor, listener) {
+			protected IDiff computeDiff(Object oldValue, Object newValue) {
+				return Diffs.computeSetDiff(asSet(oldValue), asSet(newValue));
+			}
+		};
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor) + "{}"; //$NON-NLS-1$
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanSetPropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanSetPropertyDecorator.java
new file mode 100644
index 0000000..3b52f7c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanSetPropertyDecorator.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.Set;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanMapProperty;
+import org.eclipse.core.databinding.beans.IBeanSetProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.set.SetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanSetPropertyDecorator extends SetProperty implements
+		IBeanSetProperty {
+	private final ISetProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public BeanSetPropertyDecorator(ISetProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public Object getElementType() {
+		return delegate.getElementType();
+	}
+
+	protected Set doGetSet(Object source) {
+		return delegate.getSet(source);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		delegate.setSet(source, set);
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		delegate.updateSet(source, diff);
+	}
+
+	public IBeanMapProperty values(String propertyName) {
+		return values(propertyName, null);
+	}
+
+	public IBeanMapProperty values(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getElementType();
+		return values(BeanProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanMapProperty values(IBeanValueProperty property) {
+		return new BeanMapPropertyDecorator(super.values(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IObservableSet observe(Object source) {
+		return new BeanObservableSetDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		return new BeanObservableSetDecorator(delegate.observe(realm, source),
+				propertyDescriptor);
+	}
+
+	public IObservableSet observeDetail(IObservableValue master) {
+		return new BeanObservableSetDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanValueProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanValueProperty.java
new file mode 100644
index 0000000..85e6c97
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanValueProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 264307, 265561
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanValueProperty extends SimpleValueProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class valueType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param valueType
+	 */
+	public BeanValueProperty(PropertyDescriptor propertyDescriptor,
+			Class valueType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.valueType = valueType == null ? propertyDescriptor
+				.getPropertyType() : valueType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Object doGetValue(Object source) {
+		return BeanPropertyHelper.readProperty(source, propertyDescriptor);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor, value);
+	}
+
+	public INativePropertyListener adaptListener(
+			final ISimplePropertyListener listener) {
+		return new BeanPropertyListener(this, propertyDescriptor, listener) {
+			protected IDiff computeDiff(Object oldValue, Object newValue) {
+				return Diffs.createValueDiff(oldValue, newValue);
+			}
+		};
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor);
+		if (valueType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanValuePropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanValuePropertyDecorator.java
new file mode 100644
index 0000000..034afba
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/BeanValuePropertyDecorator.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanListProperty;
+import org.eclipse.core.databinding.beans.IBeanMapProperty;
+import org.eclipse.core.databinding.beans.IBeanSetProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.observable.Realm;
+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.databinding.property.value.IValueProperty;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BeanValuePropertyDecorator extends ValueProperty implements
+		IBeanValueProperty {
+	private final IValueProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public BeanValuePropertyDecorator(IValueProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public Object getValueType() {
+		return delegate.getValueType();
+	}
+
+	protected Object doGetValue(Object source) {
+		return delegate.getValue(source);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		delegate.setValue(source, value);
+	}
+
+	public IBeanValueProperty value(String propertyName) {
+		return value(propertyName, null);
+	}
+
+	public IBeanValueProperty value(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return value(BeanProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanValueProperty value(IBeanValueProperty property) {
+		return new BeanValuePropertyDecorator(super.value(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IBeanListProperty list(String propertyName) {
+		return list(propertyName, null);
+	}
+
+	public IBeanListProperty list(String propertyName, Class elementType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return list(BeanProperties.list(beanClass, propertyName, elementType));
+	}
+
+	public IBeanListProperty list(IBeanListProperty property) {
+		return new BeanListPropertyDecorator(super.list(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IBeanSetProperty set(String propertyName) {
+		return set(propertyName, null);
+	}
+
+	public IBeanSetProperty set(String propertyName, Class elementType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return set(BeanProperties.set(beanClass, propertyName, elementType));
+	}
+
+	public IBeanSetProperty set(IBeanSetProperty property) {
+		return new BeanSetPropertyDecorator(super.set(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IBeanMapProperty map(String propertyName) {
+		return map(propertyName, null, null);
+	}
+
+	public IBeanMapProperty map(String propertyName, Class keyType,
+			Class valueType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return map(BeanProperties.map(beanClass, propertyName, keyType,
+				valueType));
+	}
+
+	public IBeanMapProperty map(IBeanMapProperty property) {
+		return new BeanMapPropertyDecorator(super.map(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IObservableValue observe(Object source) {
+		return new BeanObservableValueDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		return new BeanObservableValueDecorator(
+				delegate.observe(realm, source), propertyDescriptor);
+	}
+
+	public IObservableValue observeDetail(IObservableValue master) {
+		return new BeanObservableValueDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public IObservableList observeDetail(IObservableList master) {
+		return new BeanObservableListDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observeDetail(IObservableSet master) {
+		return new BeanObservableMapDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observeDetail(IObservableMap master) {
+		return new BeanObservableMapDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoListProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoListProperty.java
new file mode 100644
index 0000000..44634d4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoListProperty.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222, 264307, 265561
+ *     Ovidio Mallo - bug 306633
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoListProperty extends SimpleListProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class elementType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param elementType
+	 */
+	public PojoListProperty(PropertyDescriptor propertyDescriptor,
+			Class elementType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.elementType = elementType == null ? BeanPropertyHelper
+				.getCollectionPropertyElementType(propertyDescriptor)
+				: elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected List doGetList(Object source) {
+		return asList(BeanPropertyHelper.readProperty(source,
+				propertyDescriptor));
+	}
+
+	private List asList(Object propertyValue) {
+		if (propertyValue == null)
+			return Collections.EMPTY_LIST;
+		if (propertyDescriptor.getPropertyType().isArray())
+			return Arrays.asList((Object[]) propertyValue);
+		return (List) propertyValue;
+	}
+
+	protected void doSetList(Object source, List list, ListDiff diff) {
+		doSetList(source, list);
+	}
+
+	protected void doSetList(Object source, List list) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor,
+				convertListToBeanPropertyType(list));
+	}
+
+	private Object convertListToBeanPropertyType(List list) {
+		Object propertyValue = list;
+		if (propertyDescriptor.getPropertyType().isArray()) {
+			Class componentType = propertyDescriptor.getPropertyType()
+					.getComponentType();
+			Object[] array = (Object[]) Array.newInstance(componentType, list
+					.size());
+			list.toArray(array);
+			propertyValue = array;
+		}
+		return propertyValue;
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor) + "[]"; //$NON-NLS-1$
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoListPropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoListPropertyDecorator.java
new file mode 100644
index 0000000..a77ced2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoListPropertyDecorator.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.IBeanListProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.list.ListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoListPropertyDecorator extends ListProperty implements
+		IBeanListProperty {
+	private final IListProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public PojoListPropertyDecorator(IListProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public Object getElementType() {
+		return delegate.getElementType();
+	}
+
+	protected List doGetList(Object source) {
+		return delegate.getList(source);
+	}
+
+	protected void doSetList(Object source, List list) {
+		delegate.setList(source, list);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		delegate.updateList(source, diff);
+	}
+
+	public IBeanListProperty values(String propertyName) {
+		return values(propertyName, null);
+	}
+
+	public IBeanListProperty values(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getElementType();
+		return values(PojoProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanListProperty values(IBeanValueProperty property) {
+		return new PojoListPropertyDecorator(super.values(property),
+				property.getPropertyDescriptor());
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public IObservableList observe(Object source) {
+		return new BeanObservableListDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return new BeanObservableListDecorator(delegate.observe(realm, source),
+				propertyDescriptor);
+	}
+
+	public IObservableList observeDetail(IObservableValue master) {
+		return new BeanObservableListDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoMapProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoMapProperty.java
new file mode 100644
index 0000000..5c81d48
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoMapProperty.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222, 264307, 265561
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.map.SimpleMapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoMapProperty extends SimpleMapProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class keyType;
+	private final Class valueType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param keyType
+	 * @param valueType
+	 */
+	public PojoMapProperty(PropertyDescriptor propertyDescriptor,
+			Class keyType, Class valueType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.keyType = keyType;
+		this.valueType = valueType;
+	}
+
+	public Object getKeyType() {
+		return keyType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Map doGetMap(Object source) {
+		return asMap(BeanPropertyHelper
+				.readProperty(source, propertyDescriptor));
+	}
+
+	private Map asMap(Object propertyValue) {
+		if (propertyValue == null)
+			return new HashMap();
+		return (Map) propertyValue;
+	}
+
+	protected void doSetMap(Object source, Map map, MapDiff diff) {
+		doSetMap(source, map);
+	}
+
+	protected void doSetMap(Object source, Map map) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor, map);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor) + "{:}"; //$NON-NLS-1$
+
+		if (keyType != null || valueType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(keyType) + ", " //$NON-NLS-1$ //$NON-NLS-2$
+					+ BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoMapPropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoMapPropertyDecorator.java
new file mode 100644
index 0000000..fa318a9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoMapPropertyDecorator.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.Map;
+
+import org.eclipse.core.databinding.beans.IBeanMapProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.map.MapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoMapPropertyDecorator extends MapProperty implements
+		IBeanMapProperty {
+	private final IMapProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public PojoMapPropertyDecorator(IMapProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public Object getKeyType() {
+		return delegate.getKeyType();
+	}
+
+	public Object getValueType() {
+		return delegate.getValueType();
+	}
+
+	protected Map doGetMap(Object source) {
+		return delegate.getMap(source);
+	}
+
+	protected void doSetMap(Object source, Map map) {
+		delegate.setMap(source, map);
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		delegate.updateMap(source, diff);
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public IBeanMapProperty values(String propertyName) {
+		return values(propertyName, null);
+	}
+
+	public IBeanMapProperty values(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return values(PojoProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanMapProperty values(IBeanValueProperty property) {
+		return new PojoMapPropertyDecorator(super.values(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IObservableMap observe(Object source) {
+		return new BeanObservableMapDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observe(Realm realm, Object source) {
+		return new BeanObservableMapDecorator(delegate.observe(realm, source),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observeDetail(IObservableValue master) {
+		return new BeanObservableMapDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoSetProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoSetProperty.java
new file mode 100644
index 0000000..d0f2fa6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoSetProperty.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 264307, 265561
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoSetProperty extends SimpleSetProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class elementType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param elementType
+	 */
+	public PojoSetProperty(PropertyDescriptor propertyDescriptor,
+			Class elementType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.elementType = elementType == null ? BeanPropertyHelper
+				.getCollectionPropertyElementType(propertyDescriptor)
+				: elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected Set doGetSet(Object source) {
+		return asSet(BeanPropertyHelper
+				.readProperty(source, propertyDescriptor));
+	}
+
+	private Set asSet(Object propertyValue) {
+		if (propertyValue == null)
+			return Collections.EMPTY_SET;
+		if (propertyDescriptor.getPropertyType().isArray())
+			return new HashSet(Arrays.asList((Object[]) propertyValue));
+		return (Set) propertyValue;
+	}
+
+	protected void doSetSet(Object source, Set set, SetDiff diff) {
+		doSetSet(source, set);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor,
+				convertSetToBeanPropertyType(set));
+	}
+
+	private Object convertSetToBeanPropertyType(Set set) {
+		Object propertyValue = set;
+		if (propertyDescriptor.getPropertyType().isArray()) {
+			Class componentType = propertyDescriptor.getPropertyType()
+					.getComponentType();
+			Object[] array = (Object[]) Array.newInstance(componentType, set
+					.size());
+			propertyValue = set.toArray(array);
+		}
+		return propertyValue;
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor) + "{}"; //$NON-NLS-1$
+		if (elementType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(elementType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoSetPropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoSetPropertyDecorator.java
new file mode 100644
index 0000000..ebe54a9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoSetPropertyDecorator.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.Set;
+
+import org.eclipse.core.databinding.beans.IBeanMapProperty;
+import org.eclipse.core.databinding.beans.IBeanSetProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.set.SetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoSetPropertyDecorator extends SetProperty implements
+		IBeanSetProperty {
+	private final ISetProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public PojoSetPropertyDecorator(ISetProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public Object getElementType() {
+		return delegate.getElementType();
+	}
+
+	protected Set doGetSet(Object source) {
+		return delegate.getSet(source);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		delegate.setSet(source, set);
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		delegate.updateSet(source, diff);
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public IBeanMapProperty values(String propertyName) {
+		return values(propertyName, null);
+	}
+
+	public IBeanMapProperty values(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getElementType();
+		return values(PojoProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanMapProperty values(IBeanValueProperty property) {
+		return new BeanMapPropertyDecorator(super.values(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IObservableSet observe(Object source) {
+		return new BeanObservableSetDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		return new BeanObservableSetDecorator(delegate.observe(realm, source),
+				propertyDescriptor);
+	}
+
+	public IObservableSet observeDetail(IObservableValue master) {
+		return new BeanObservableSetDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoValueProperty.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoValueProperty.java
new file mode 100644
index 0000000..f63a8c8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoValueProperty.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 195222, 264307, 265561
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoValueProperty extends SimpleValueProperty {
+	private final PropertyDescriptor propertyDescriptor;
+	private final Class valueType;
+
+	/**
+	 * @param propertyDescriptor
+	 * @param valueType
+	 */
+	public PojoValueProperty(PropertyDescriptor propertyDescriptor,
+			Class valueType) {
+		this.propertyDescriptor = propertyDescriptor;
+		this.valueType = valueType == null ? propertyDescriptor
+				.getPropertyType() : valueType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Object doGetValue(Object source) {
+		if (source == null)
+			return null;
+		return BeanPropertyHelper.readProperty(source, propertyDescriptor);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		BeanPropertyHelper.writeProperty(source, propertyDescriptor, value);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	public String toString() {
+		String s = BeanPropertyHelper.propertyName(propertyDescriptor);
+		if (valueType != null)
+			s += "<" + BeanPropertyHelper.shortClassName(valueType) + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoValuePropertyDecorator.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoValuePropertyDecorator.java
new file mode 100644
index 0000000..af410e7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/PojoValuePropertyDecorator.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 195222)
+ *     Matthew Hall - bug 264307
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.IBeanListProperty;
+import org.eclipse.core.databinding.beans.IBeanMapProperty;
+import org.eclipse.core.databinding.beans.IBeanSetProperty;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.Realm;
+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.databinding.property.value.IValueProperty;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PojoValuePropertyDecorator extends ValueProperty implements
+		IBeanValueProperty {
+	private final IValueProperty delegate;
+	private final PropertyDescriptor propertyDescriptor;
+
+	/**
+	 * @param delegate
+	 * @param propertyDescriptor
+	 */
+	public PojoValuePropertyDecorator(IValueProperty delegate,
+			PropertyDescriptor propertyDescriptor) {
+		this.delegate = delegate;
+		this.propertyDescriptor = propertyDescriptor;
+	}
+
+	public PropertyDescriptor getPropertyDescriptor() {
+		return propertyDescriptor;
+	}
+
+	public Object getValueType() {
+		return delegate.getValueType();
+	}
+
+	protected Object doGetValue(Object source) {
+		return delegate.getValue(source);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		delegate.setValue(source, value);
+	}
+
+	public IBeanValueProperty value(String propertyName) {
+		return value(propertyName, null);
+	}
+
+	public IBeanValueProperty value(String propertyName, Class valueType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return value(PojoProperties.value(beanClass, propertyName, valueType));
+	}
+
+	public IBeanValueProperty value(IBeanValueProperty property) {
+		return new PojoValuePropertyDecorator(super.value(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IBeanListProperty list(String propertyName) {
+		return list(propertyName, null);
+	}
+
+	public IBeanListProperty list(String propertyName, Class elementType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return list(PojoProperties.list(beanClass, propertyName, elementType));
+	}
+
+	public IBeanListProperty list(IBeanListProperty property) {
+		return new BeanListPropertyDecorator(super.list(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IBeanSetProperty set(String propertyName) {
+		return set(propertyName, null);
+	}
+
+	public IBeanSetProperty set(String propertyName, Class elementType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return set(PojoProperties.set(beanClass, propertyName, elementType));
+	}
+
+	public IBeanSetProperty set(IBeanSetProperty property) {
+		return new BeanSetPropertyDecorator(super.set(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IBeanMapProperty map(String propertyName) {
+		return map(propertyName, null, null);
+	}
+
+	public IBeanMapProperty map(String propertyName, Class keyType,
+			Class valueType) {
+		Class beanClass = (Class) delegate.getValueType();
+		return map(PojoProperties.map(beanClass, propertyName, keyType,
+				valueType));
+	}
+
+	public IBeanMapProperty map(IBeanMapProperty property) {
+		return new BeanMapPropertyDecorator(super.map(property),
+				property.getPropertyDescriptor());
+	}
+
+	public IObservableValue observe(Object source) {
+		return new BeanObservableValueDecorator(delegate.observe(source),
+				propertyDescriptor);
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		return new BeanObservableValueDecorator(
+				delegate.observe(realm, source), propertyDescriptor);
+	}
+
+	public IObservableValue observeDetail(IObservableValue master) {
+		return new BeanObservableValueDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public IObservableList observeDetail(IObservableList master) {
+		return new BeanObservableListDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observeDetail(IObservableSet master) {
+		return new BeanObservableMapDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public IObservableMap observeDetail(IObservableMap master) {
+		return new BeanObservableMapDecorator(delegate.observeDetail(master),
+				propertyDescriptor);
+	}
+
+	public String toString() {
+		return delegate.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/Util.java b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/Util.java
new file mode 100644
index 0000000..f18e856
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.beans/src/org/eclipse/core/internal/databinding/beans/Util.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.beans;
+
+/**
+ * @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 boolean equals(final Object left, final Object right) {
+		return left == null ? right == null : ((right != null) && left
+				.equals(right));
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/.classpath b/bundles/org.eclipse.core.databinding.observable/.classpath
new file mode 100644
index 0000000..6f3b481
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.core.databinding.observable/.project b/bundles/org.eclipse.core.databinding.observable/.project
new file mode 100644
index 0000000..528960a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.core.databinding.observable</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..9e52c97
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,354 @@
+#Thu Feb 05 11:35:38 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..f590c4e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:05:48 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.3\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.pde.api.tools.prefs b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..60843cb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,145 @@
+#Wed Apr 02 17:10:39 EDT 2008
+ANNOTATION_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITH_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_LEAK=Warning
+API_PROFILE_ELEMENT_TYPE_REMOVED_API_COMPONENT=Error
+CLASS_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERCLASS_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_CHANGED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+CLASS_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+ENUM_ELEMENT_TYPE_ADDED_FIELD=Error
+ENUM_ELEMENT_TYPE_ADDED_METHOD=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+ENUM_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Ignore
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENTS=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETERS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+METHOD_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
diff --git a/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.pde.prefs b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..4a56680
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:49:44 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=0
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=0
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.core.databinding.observable/.settings/org.moreunit.prefs b/bundles/org.eclipse.core.databinding.observable/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..7935a31
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/.settings/org.moreunit.prefs
@@ -0,0 +1,5 @@
+#Tue Feb 02 23:24:37 MST 2010
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=org.eclipse.core.databinding.observable\:src\:org.eclipse.jface.tests.databinding\:src
+org.moreunit.useprojectsettings=true
diff --git a/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1bdf4a1
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.core.databinding.observable
+Bundle-Version: 1.4.0.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.core.databinding.observable,
+ org.eclipse.core.databinding.observable.list;x-internal:=false,
+ org.eclipse.core.databinding.observable.map,
+ org.eclipse.core.databinding.observable.masterdetail,
+ org.eclipse.core.databinding.observable.set;x-internal:=false,
+ org.eclipse.core.databinding.observable.value;x-internal:=false,
+ org.eclipse.core.databinding.util,
+ org.eclipse.core.internal.databinding.identity;x-friends:="org.eclipse.core.databinding,org.eclipse.core.databinding.property",
+ org.eclipse.core.internal.databinding.observable;x-internal:=true,
+ org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding"
+Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)"
+Import-Package: org.osgi.framework;version="[1.4.0,2.0.0)";resolution:=optional,
+ org.osgi.util.tracker;version="[1.3.3,2.0.0)";resolution:=optional,
+ org.eclipse.osgi.framework.log;version="[1.0.0,2.0.0)";resolution:=optional
+Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,
+ J2SE-1.4
+Bundle-Activator: org.eclipse.core.internal.databinding.observable.Activator
+Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.core.databinding.observable/about.html b/bundles/org.eclipse.core.databinding.observable/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.observable/build.properties b/bundles/org.eclipse.core.databinding.observable/build.properties
new file mode 100644
index 0000000..98cdfec
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/build.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+src.includes = about.html
+
diff --git a/bundles/org.eclipse.core.databinding.observable/plugin.properties b/bundles/org.eclipse.core.databinding.observable/plugin.properties
new file mode 100644
index 0000000..bf1a6b7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# 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
+###############################################################################
+pluginName = JFace Data Binding Observables
+providerName = Eclipse.org
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/AbstractObservable.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/AbstractObservable.java
new file mode 100644
index 0000000..7770eff
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/AbstractObservable.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 118516, 146397, 249526
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.AssertionFailedException;
+
+/**
+ * @since 1.0
+ */
+public abstract class AbstractObservable extends ChangeManager implements IObservable {
+	private boolean disposed = false;
+
+	/**
+	 * @param realm
+	 */
+	public AbstractObservable(Realm realm) {
+		super(realm);
+		ObservableTracker.observableCreated(this);
+	}
+
+	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);
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized void addDisposeListener(IDisposeListener listener) {
+		addListener(DisposeEvent.TYPE, listener);
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized void removeDisposeListener(IDisposeListener listener) {
+		removeListener(DisposeEvent.TYPE, listener);
+	}
+
+	protected void fireChange() {
+		checkRealm();
+		fireEvent(new ChangeEvent(this));
+	}
+
+	protected void fireStale() {
+		checkRealm();
+		fireEvent(new StaleEvent(this));
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized boolean isDisposed() {
+		return disposed;
+	}
+
+	/**
+	 * 
+	 */
+	public synchronized void dispose() {
+		if (!disposed) {
+			disposed = true;
+			fireEvent(new DisposeEvent(this));
+			super.dispose();
+		}
+	}
+
+	/**
+	 * Asserts that the realm is the current realm.
+	 * 
+	 * @see Realm#isCurrent()
+	 * @throws AssertionFailedException if the realm is not the current realm
+	 */
+	protected void checkRealm() {
+		Assert.isTrue(getRealm().isCurrent(),
+				"This operation must be run within the observable's realm"); //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeEvent.java
new file mode 100644
index 0000000..60950cf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeEvent.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * 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 extends 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 ChangeEvent(IObservable source) {
+		super(source);
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((IChangeListener) listener).handleChange(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeManager.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeManager.java
new file mode 100644
index 0000000..d4b2a0d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeManager.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 118516, 255734
+ *     Chris Audley - bug 273265
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+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 final Realm realm;
+
+	/**
+	 * @param realm
+	 * 
+	 */
+	/* package */ChangeManager(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 == -1) {
+			int length;
+			if (listenerTypes == 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();
+			listenerTypeIndex = length;
+		}
+		boolean hadListeners = hasListeners();
+		listenerLists[listenerTypeIndex].add(listener);
+		if (!hadListeners && hasListeners()) {
+			firstListenerAdded();
+		}
+	}
+
+	/**
+	 * @param listenerType
+	 * @param listener
+	 */
+	protected void removeListener(Object listenerType,
+			IObservablesListener listener) {
+		int listenerTypeIndex = findListenerTypeIndex(listenerType);
+		if (listenerTypeIndex != -1) {
+			boolean hadListeners = hasListeners();
+			listenerLists[listenerTypeIndex].remove(listener);
+			if (listenerLists[listenerTypeIndex].size() == 0) {
+				if (hadListeners && !hasListeners()) {
+					this.lastListenerRemoved();
+				}
+			}
+		}
+	}
+
+	protected boolean hasListeners() {
+		if (listenerTypes != null)
+			for (int i = 0; i < listenerTypes.length; i++)
+				if (listenerTypes[i] != DisposeEvent.TYPE)
+					if (listenerLists[i].size() > 0)
+						return true;
+		return false;
+	}
+
+	private int findListenerTypeIndex(Object listenerType) {
+		if (listenerTypes != null) {
+			for (int i = 0; i < listenerTypes.length; i++) {
+				if (listenerTypes[i] == listenerType) {
+					return i;
+				}
+			}
+		}
+		return -1;
+	}
+
+	protected void fireEvent(ObservableEvent event) {
+		Object listenerType = event.getListenerType();
+		int listenerTypeIndex = findListenerTypeIndex(listenerType);
+		if (listenerTypeIndex != -1) {
+			Object[] listeners = listenerLists[listenerTypeIndex]
+					.getListeners();
+			for (int i = 0; i < listeners.length; i++) {
+				event.dispatch((IObservablesListener) listeners[i]);
+			}
+		}
+	}
+
+	/**
+	 * 
+	 */
+	protected void firstListenerAdded() {
+	}
+
+	/**
+	 * 
+	 */
+	protected void lastListenerRemoved() {
+	}
+
+	/**
+	 * 
+	 */
+	public void dispose() {
+		listenerLists = null;
+		listenerTypes = null;
+	}
+
+	/**
+	 * @return Returns the realm.
+	 */
+	public Realm getRealm() {
+		return realm;
+	}
+
+	protected Object clone() throws CloneNotSupportedException {
+		ChangeManager duplicate = (ChangeManager) super.clone();
+		duplicate.listenerLists = null;
+		duplicate.listenerTypes = null;
+		return duplicate;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeSupport.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeSupport.java
new file mode 100644
index 0000000..902ea8b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ChangeSupport.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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 146397
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * @since 1.0
+ *
+ */
+public abstract class ChangeSupport extends ChangeManager {
+
+	/**
+	 * @param realm 
+	 */
+	public ChangeSupport(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);
+	}
+
+	/**
+	 * @param listener 
+	 * @since 1.2
+	 */
+	public void addDisposeListener(IDisposeListener listener) {
+		addListener(DisposeEvent.TYPE, listener);
+	}
+
+	/**
+	 * @param listener
+	 * @since 1.2
+	 */
+	public void removeDisposeListener(IDisposeListener listener) {
+		removeListener(DisposeEvent.TYPE, listener);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DecoratingObservable.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DecoratingObservable.java
new file mode 100644
index 0000000..d079c99
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DecoratingObservable.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 237718)
+ *     Matthew Hall - bugs 246626, 255734, 264925
+ *     Boris Bokowski, IBM Corporation - bug 257112
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * An observable which decorates another observable
+ * 
+ * @since 1.2
+ * 
+ */
+public class DecoratingObservable extends AbstractObservable implements
+		IDecoratingObservable {
+
+	private IObservable decorated;
+
+	private IStaleListener staleListener;
+
+	private boolean disposedDecoratedOnDispose;
+
+	/**
+	 * Constructs a DecoratingObservable which decorates the given observable.
+	 * 
+	 * @param decorated
+	 *            the observable being decorated.
+	 * @param disposeDecoratedOnDispose
+	 *            whether the decorated observable should be disposed when the
+	 *            decorator is disposed
+	 */
+	public DecoratingObservable(IObservable decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated.getRealm());
+		this.decorated = decorated;
+		this.disposedDecoratedOnDispose = disposeDecoratedOnDispose;
+		decorated.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				dispose();
+			}
+		});
+	}
+
+	public IObservable getDecorated() {
+		return decorated;
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return decorated.isStale();
+	}
+
+	protected void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	protected void firstListenerAdded() {
+		if (staleListener == null) {
+			staleListener = new IStaleListener() {
+				public void handleStale(StaleEvent staleEvent) {
+					DecoratingObservable.this.handleStaleEvent(staleEvent);
+				}
+			};
+		}
+		decorated.addStaleListener(staleListener);
+	}
+
+	protected void lastListenerRemoved() {
+		if (staleListener != null) {
+			decorated.removeStaleListener(staleListener);
+			staleListener = null;
+		}
+	}
+
+	/**
+	 * Called whenever a StaleEvent is received from the decorated observable.
+	 * By default, this method fires the stale event again, with the decorating
+	 * observable as the event source. Subclasses may override to provide
+	 * different behavior.
+	 * 
+	 * @param event
+	 *            the stale event received from the decorated observable
+	 */
+	protected void handleStaleEvent(StaleEvent event) {
+		fireStale();
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() == obj.getClass()) {
+			DecoratingObservable other = (DecoratingObservable) obj;
+			return Util.equals(this.decorated, other.decorated);
+		}
+		return Util.equals(decorated, obj);
+	}
+
+	public int hashCode() {
+		return decorated.hashCode();
+	}
+
+	public synchronized void dispose() {
+		if (decorated != null && staleListener != null) {
+			decorated.removeStaleListener(staleListener);
+		}
+		if (decorated != null) {
+			if (disposedDecoratedOnDispose)
+				decorated.dispose();
+			decorated = null;
+		}
+		staleListener = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DecoratingObservableCollection.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DecoratingObservableCollection.java
new file mode 100644
index 0000000..de13bd0
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DecoratingObservableCollection.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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 237718)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * An observable collection which decorates another observable collection
+ * 
+ * @since 1.2
+ */
+public class DecoratingObservableCollection extends DecoratingObservable
+		implements IObservableCollection {
+	private IObservableCollection decorated;
+
+	/**
+	 * @param decorated
+	 * @param disposeDecoratedOnDispose
+	 */
+	public DecoratingObservableCollection(IObservableCollection decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated, disposeDecoratedOnDispose);
+		this.decorated = decorated;
+	}
+
+	public boolean add(Object o) {
+		getterCalled();
+		return decorated.add(o);
+	}
+
+	public boolean addAll(Collection c) {
+		getterCalled();
+		return decorated.addAll(c);
+	}
+
+	public void clear() {
+		checkRealm();
+		decorated.clear();
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+		return decorated.contains(o);
+	}
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return decorated.containsAll(c);
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return decorated.isEmpty();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator decoratedIterator = decorated.iterator();
+		return new Iterator() {
+			public void remove() {
+				decoratedIterator.remove();
+			}
+
+			public boolean hasNext() {
+				getterCalled();
+				return decoratedIterator.hasNext();
+			}
+
+			public Object next() {
+				getterCalled();
+				return decoratedIterator.next();
+			}
+		};
+	}
+
+	public boolean remove(Object o) {
+		getterCalled();
+		return decorated.remove(o);
+	}
+
+	public boolean removeAll(Collection c) {
+		getterCalled();
+		return decorated.removeAll(c);
+	}
+
+	public boolean retainAll(Collection c) {
+		getterCalled();
+		return decorated.retainAll(c);
+	}
+
+	public int size() {
+		getterCalled();
+		return decorated.size();
+	}
+
+	public Object[] toArray() {
+		getterCalled();
+		return decorated.toArray();
+	}
+
+	public Object[] toArray(Object[] a) {
+		getterCalled();
+		return decorated.toArray(a);
+	}
+
+	public Object getElementType() {
+		return decorated.getElementType();
+	}
+
+	public boolean equals(Object obj) {
+		getterCalled();
+		if (this == obj) {
+			return true;
+		}
+		return decorated.equals(obj);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return decorated.hashCode();
+	}
+
+	public String toString() {
+		getterCalled();
+		return decorated.toString();
+	}
+
+	public synchronized void dispose() {
+		decorated = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Diffs.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Diffs.java
new file mode 100644
index 0000000..5f8792a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Diffs.java
@@ -0,0 +1,606 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+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.observable.Util;
+
+/**
+ * @since 1.0
+ * 
+ */
+public class Diffs {
+
+	/**
+	 * Returns a {@link ListDiff} describing the change between the specified
+	 * old and new list states.
+	 * 
+	 * @param oldList
+	 *            the old list state
+	 * @param newList
+	 *            the new list state
+	 * @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((ListDiffEntry[]) diffEntries
+				.toArray(new ListDiffEntry[diffEntries.size()]));
+		return listDiff;
+	}
+
+	/**
+	 * Returns a lazily computed {@link ListDiff} describing the change between
+	 * the specified old and new list states.
+	 * 
+	 * @param oldList
+	 *            the old list state
+	 * @param newList
+	 *            the new list state
+	 * @return a lazily computed {@link ListDiff} describing the change between
+	 *         the specified old and new list states.
+	 * @since 1.3
+	 */
+	public static ListDiff computeLazyListDiff(final List oldList,
+			final List newList) {
+		return new ListDiff() {
+			ListDiff lazyDiff;
+
+			public ListDiffEntry[] getDifferences() {
+				if (lazyDiff == null) {
+					lazyDiff = Diffs.computeListDiff(oldList, newList);
+				}
+				return lazyDiff.getDifferences();
+			}
+		};
+	}
+
+	/**
+	 * 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 {
+				boolean done;
+				do {
+					done = true;
+					Object oldValue = oldList.get(index);
+					if (oldValue == null ? newValue != null : !oldValue
+							.equals(newValue)) {
+						int oldIndexOfNewValue = listIndexOf(oldList, newValue,
+								index);
+						if (oldIndexOfNewValue != -1) {
+							int newIndexOfOldValue = listIndexOf(newList,
+									oldValue, index);
+							if (newIndexOfOldValue == -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 (candidate == null ? object == null : 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 boolean equals(final Object left, final Object right) {
+		return left == null ? right == null : ((right != null) && left
+				.equals(right));
+	}
+
+	/**
+	 * Returns a {@link SetDiff} describing the change between the specified old
+	 * and new set states.
+	 * 
+	 * @param oldSet
+	 *            the old set state
+	 * @param newSet
+	 *            the new set state
+	 * @return a {@link SetDiff} describing the change between the specified old
+	 *         and new set states.
+	 */
+	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);
+	}
+
+	/**
+	 * Returns a lazily computed {@link SetDiff} describing the change between
+	 * the specified old and new set states.
+	 * 
+	 * @param oldSet
+	 *            the old set state
+	 * @param newSet
+	 *            the new set state
+	 * @return a lazily computed {@link SetDiff} describing the change between
+	 *         the specified old and new set states.
+	 * @since 1.3
+	 */
+	public static SetDiff computeLazySetDiff(final Set oldSet, final Set newSet) {
+		return new SetDiff() {
+
+			private SetDiff lazyDiff;
+
+			private SetDiff getLazyDiff() {
+				if (lazyDiff == null) {
+					lazyDiff = computeSetDiff(oldSet, newSet);
+				}
+				return lazyDiff;
+			}
+
+			public Set getAdditions() {
+				return getLazyDiff().getAdditions();
+			}
+
+			public Set getRemovals() {
+				return getLazyDiff().getRemovals();
+			}
+
+		};
+	}
+
+	/**
+	 * Returns a {@link MapDiff} describing the change between the specified old
+	 * and new map states.
+	 * 
+	 * @param oldMap
+	 *            the old map state
+	 * @param newMap
+	 *            the new map state
+	 * @return a {@link MapDiff} describing the change between the specified old
+	 *         and new map states.
+	 */
+	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 = (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 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);
+			}
+		};
+	}
+
+	/**
+	 * Returns a lazily computed {@link MapDiff} describing the change between
+	 * the specified old and new map states.
+	 * 
+	 * @param oldMap
+	 *            the old map state
+	 * @param newMap
+	 *            the new map state
+	 * @return a lazily computed {@link MapDiff} describing the change between
+	 *         the specified old and new map states.
+	 * @since 1.3
+	 */
+	public static MapDiff computeLazyMapDiff(final Map oldMap, final Map newMap) {
+		return new MapDiff() {
+
+			private MapDiff lazyDiff;
+
+			private MapDiff getLazyDiff() {
+				if (lazyDiff == null) {
+					lazyDiff = computeMapDiff(oldMap, newMap);
+				}
+				return lazyDiff;
+			}
+
+			public Set getAddedKeys() {
+				return getLazyDiff().getAddedKeys();
+			}
+
+			public Set getRemovedKeys() {
+				return getLazyDiff().getRemovedKeys();
+			}
+
+			public Set getChangedKeys() {
+				return getLazyDiff().getChangedKeys();
+			}
+
+			public Object getOldValue(Object key) {
+				return getLazyDiff().getOldValue(key);
+			}
+
+			public Object getNewValue(Object key) {
+				return getLazyDiff().getNewValue(key);
+			}
+
+		};
+	}
+
+	/**
+	 * @param oldValue
+	 * @param newValue
+	 * @return a value diff
+	 */
+	public static ValueDiff createValueDiff(final Object oldValue,
+			final Object newValue) {
+		return new ValueDiff() {
+
+			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) {
+		final Set unmodifiableAdditions = Collections
+				.unmodifiableSet(additions);
+		final Set unmodifiableRemovals = Collections.unmodifiableSet(removals);
+		return new SetDiff() {
+
+			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(new ListDiffEntry[] { difference });
+	}
+
+	/**
+	 * @param difference1
+	 * @param difference2
+	 * @return a list diff with two differing entries
+	 */
+	public static ListDiff createListDiff(ListDiffEntry difference1,
+			ListDiffEntry difference2) {
+		return createListDiff(new ListDiffEntry[] { difference1, difference2 });
+	}
+
+	/**
+	 * @param differences
+	 * @return a list diff with the given entries
+	 */
+	public static ListDiff createListDiff(final ListDiffEntry[] differences) {
+		return new ListDiff() {
+			public ListDiffEntry[] getDifferences() {
+				return differences;
+			}
+		};
+	}
+
+	/**
+	 * @param position
+	 * @param isAddition
+	 * @param element
+	 * @return a list diff entry
+	 */
+	public static ListDiffEntry createListDiffEntry(final int position,
+			final boolean isAddition, final Object element) {
+		return new ListDiffEntry() {
+
+			public int getPosition() {
+				return position;
+			}
+
+			public boolean isAddition() {
+				return isAddition;
+			}
+
+			public Object getElement() {
+				return element;
+			}
+		};
+	}
+
+	/**
+	 * @param addedKey
+	 * @param newValue
+	 * @return a map diff
+	 */
+	public static MapDiff createMapDiffSingleAdd(final Object addedKey,
+			final Object newValue) {
+		return new MapDiff() {
+
+			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(final Object existingKey,
+			final Object oldValue, final Object newValue) {
+		return new MapDiff() {
+
+			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(final Object removedKey,
+			final Object oldValue) {
+		return new MapDiff() {
+
+			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(final Map copyOfOldMap) {
+		return new MapDiff() {
+
+			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(final Set addedKeys,
+			final Set removedKeys, final Set changedKeys, final Map oldValues,
+			final Map newValues) {
+		return new MapDiff() {
+
+			public Set getAddedKeys() {
+				return addedKeys;
+			}
+
+			public Set getChangedKeys() {
+				return changedKeys;
+			}
+
+			public Object getNewValue(Object key) {
+				return newValues.get(key);
+			}
+
+			public Object getOldValue(Object key) {
+				return oldValues.get(key);
+			}
+
+			public Set getRemovedKeys() {
+				return removedKeys;
+			}
+		};
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DisposeEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DisposeEvent.java
new file mode 100644
index 0000000..f99986e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/DisposeEvent.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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 146397)
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * Event denoting that an {@link IObservable} object was disposed.
+ * 
+ * @since 1.2
+ */
+public class DisposeEvent extends ObservableEvent {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3241193109844979384L;
+
+	static final Object TYPE = new Object();
+
+	/**
+	 * Creates a new dispose event object.
+	 * 
+	 * @param source
+	 *            the observable that was disposed
+	 */
+	public DisposeEvent(IObservable source) {
+		super(source);
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((IDisposeListener) listener).handleDispose(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IChangeListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IChangeListener.java
new file mode 100644
index 0000000..d0a5cee
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IChangeListener.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+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 extends IObservablesListener {
+
+	/**
+	 * Handle a generic change to the given observable. The given event object
+	 * must only be used locally in this method because it may be reused for
+	 * other change notifications.
+	 * 
+	 * @param event
+	 */
+	public void handleChange(ChangeEvent event);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDecoratingObservable.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDecoratingObservable.java
new file mode 100644
index 0000000..f66e2f8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDecoratingObservable.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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 237718)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * Interface for observables which decorate other observables.
+ * 
+ * @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.2
+ */
+public interface IDecoratingObservable extends IObservable {
+	/**
+	 * @return the observable that this observable decorates.
+	 */
+	public IObservable getDecorated();
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDiff.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDiff.java
new file mode 100644
index 0000000..6483c1c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDiff.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+
+/**
+ * Marker interface for objects which describe a difference in state.
+ * 
+ * @since 1.2
+ * @see ValueDiff
+ * @see ListDiff
+ * @see SetDiff
+ * @see MapDiff
+ */
+public interface IDiff {
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDisposeListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDisposeListener.java
new file mode 100644
index 0000000..35bf0af
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IDisposeListener.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 146397)
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * Listener for dispose events. An observable object is disposed if its
+ * {@link IObservable#dispose()} method has been called.
+ * 
+ * @since 1.2
+ */
+public interface IDisposeListener extends IObservablesListener {
+	/**
+	 * Handle the event that the given observable object has been disposed. The
+	 * given event object must only be used locally in this method because it
+	 * may be reused for other dispose notifications.
+	 * 
+	 * @param event
+	 */
+	public void handleDispose(DisposeEvent event);
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservable.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservable.java
new file mode 100644
index 0000000..b402608
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservable.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * 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 - bugs 237718, 146397
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable;
+
+/**
+ * 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>
+ * 
+ * @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#getterCalled(IObservable)}.
+	 */
+	public boolean isStale();
+
+	/**
+	 * Adds the given dispose listener to the list of dispose listeners. Dispose
+	 * listeners are notified when an observable has been disposed.
+	 * 
+	 * @param listener
+	 *            the listener to add
+	 * @since 1.2
+	 */
+	public void addDisposeListener(IDisposeListener listener);
+
+	/**
+	 * Removes the given dispose listener from the list of dispose listeners.
+	 * Has no effect if the given listener is not registered as a dispose
+	 * listener.
+	 * 
+	 * @param listener
+	 *            the listener to remove
+	 * @since 1.2
+	 */
+	public void removeDisposeListener(IDisposeListener listener);
+
+	/**
+	 * Returns whether the observable has been disposed
+	 * 
+	 * @return whether the observable has been disposed
+	 * @since 1.2
+	 */
+	public boolean isDisposed();
+
+	/**
+	 * Disposes of this observable object, removing all listeners registered
+	 * with this object, and all listeners this object might have registered on
+	 * other objects.
+	 */
+	public void dispose();
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservableCollection.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservableCollection.java
new file mode 100644
index 0000000..8d1ee9b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservableCollection.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+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 extends IObservable, Collection {
+
+	/**
+	 * Returns the element type of this observable collection, or
+	 * <code>null</code> if this observable collection is untyped.
+	 * 
+	 * @return the element type of this observable collection, or
+	 *         <code>null</code> if this observable collection is untyped.
+	 */
+	Object getElementType();
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservablesListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservablesListener.java
new file mode 100644
index 0000000..469fe02
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObservablesListener.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * Marker interface for all listener types in the observables framework.
+ * 
+ * @noextend This interface is not intended to be extended by clients.
+ * 
+ * @since 1.0
+ */
+public interface IObservablesListener {
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObserving.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObserving.java
new file mode 100644
index 0000000..1c9fc13
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IObserving.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * 
+ * 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();
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IStaleListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IStaleListener.java
new file mode 100644
index 0000000..5729198
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/IStaleListener.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * Listener for staleness events. An observable object is stale if its state
+ * will change eventually.
+ * 
+ * @since 1.0
+ */
+public interface IStaleListener extends IObservablesListener {
+
+	/**
+	 * Handle the event that the given observable object is now stale. The given
+	 * event object must only be used locally in this method because it may be
+	 * reused for other change notifications.
+	 * 
+	 * @param staleEvent
+	 */
+	public void handleStale(StaleEvent staleEvent);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ObservableEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ObservableEvent.java
new file mode 100644
index 0000000..38a865c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ObservableEvent.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+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 extends EventObject {
+
+	/**
+	 * Creates a new observable event.
+	 * 
+	 * @param source
+	 */
+	public ObservableEvent(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 (IObservable) getSource();
+	}
+
+	/**
+	 * Dispatch this event to the given listener. Subclasses must implement this
+	 * method by calling the appropriate type-safe event handling method on the
+	 * given listener according to the type of this event.
+	 * 
+	 * @param listener
+	 *            the listener that should handle the event
+	 */
+	protected abstract void dispatch(IObservablesListener listener);
+
+	/**
+	 * Returns a unique object used for distinguishing this event type from
+	 * others.
+	 * 
+	 * @return a unique object representing the concrete type of this event.
+	 */
+	protected abstract Object getListenerType();
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ObservableTracker.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ObservableTracker.java
new file mode 100644
index 0000000..0c27e3b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/ObservableTracker.java
@@ -0,0 +1,291 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 210115, 146397, 249526, 262269, 251424
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable;
+
+import java.util.Set;
+
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * 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 runAndMonitor(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 currentGetterCalledSet = new ThreadLocal();
+
+	private static ThreadLocal currentObservableCreatedSet = new ThreadLocal();
+
+	private static ThreadLocal currentIgnoreCount = 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 = (Set) currentGetterCalledSet.get();
+		IChangeListener lastChangeListener = (IChangeListener) currentChangeListener
+				.get();
+		IStaleListener lastStaleListener = (IStaleListener) currentStaleListener
+				.get();
+		Integer lastIgnore = (Integer) currentIgnoreCount.get();
+
+		Set observableSet = new IdentitySet();
+		// Push the new listeners to the top of the stack
+		currentGetterCalledSet.set(observableSet);
+		currentChangeListener.set(changeListener);
+		currentStaleListener.set(staleListener);
+		currentIgnoreCount.set(null);
+		try {
+			runnable.run();
+		} finally {
+			// Pop the new listener off the top of the stack (by restoring the
+			// previous listener)
+			currentGetterCalledSet.set(lastObservableSet);
+			currentChangeListener.set(lastChangeListener);
+			currentStaleListener.set(lastStaleListener);
+			checkUnmatchedIgnore(runnable);
+			currentIgnoreCount.set(lastIgnore);
+		}
+
+		return (IObservable[]) observableSet
+				.toArray(new IObservable[observableSet.size()]);
+	}
+
+	/**
+	 * Invokes the given runnable, and returns the set of IObservables that were
+	 * created by the runnable. If the runnable calls this method recursively,
+	 * the result will not contain IObservables that were created within the
+	 * inner runnable.
+	 * <p>
+	 * <em>NOTE: As of 1.2 (Eclipse 3.5), there are unresolved problems with this API, see
+	 * <a href="https://bugs.eclipse.org/278550">bug 278550</a>. If we cannot
+	 * find a way to make this API work, it will be deprecated as of 3.6.</em>
+	 * </p>
+	 * 
+	 * @param runnable
+	 *            runnable to execute
+	 * @return an array of unique observable objects
+	 * @since 1.2
+	 */
+	public static IObservable[] runAndCollect(Runnable runnable) {
+		Set lastObservableCreatedSet = (Set) currentObservableCreatedSet.get();
+		Integer lastIgnore = (Integer) currentIgnoreCount.get();
+
+		Set observableSet = new IdentitySet();
+		// Push the new listeners to the top of the stack
+		currentObservableCreatedSet.set(observableSet);
+		currentIgnoreCount.set(null);
+		try {
+			runnable.run();
+		} finally {
+			// Pop the new listener off the top of the stack (by restoring the
+			// previous listener)
+			currentObservableCreatedSet.set(lastObservableCreatedSet);
+			checkUnmatchedIgnore(runnable);
+			currentIgnoreCount.set(lastIgnore);
+		}
+
+		return (IObservable[]) observableSet
+				.toArray(new IObservable[observableSet.size()]);
+	}
+
+	private static void checkUnmatchedIgnore(Runnable runnable) {
+		if (isIgnore()) {
+			Policy
+					.getLog()
+					.log(
+							new Status(
+									IStatus.ERROR,
+									Policy.JFACE_DATABINDING,
+									"There were " //$NON-NLS-1$
+											+ currentIgnoreCount.get()
+											+ " unmatched setIgnore(true) invocations in runnable " //$NON-NLS-1$
+											+ runnable));
+		}
+	}
+
+	/**
+	 * If the argument is <code>true</code>, causes subsequent calls to
+	 * {@link #getterCalled(IObservable)} and
+	 * {@link #observableCreated(IObservable)} to be ignored on the current
+	 * thread. When the flag is set to <code>false</code>, calls to
+	 * {@link #getterCalled(IObservable)} and
+	 * {@link #observableCreated(IObservable)} will resume gathering
+	 * observables. Nested calls to this method are stacked.
+	 * 
+	 * @param ignore
+	 *            the new ignore state
+	 * 
+	 * @exception IllegalStateException
+	 *                if
+	 *                <code>ignore<code> is false and the ignore count is already zero.
+	 * 
+	 * @see #getterCalled(IObservable)
+	 * @see #observableCreated(IObservable)
+	 * @since 1.3
+	 */
+	public static void setIgnore(boolean ignore) {
+		Integer lastCount = (Integer) currentIgnoreCount.get();
+
+		int newCount = (lastCount == null ? 0 : lastCount.intValue())
+				+ (ignore ? 1 : -1);
+
+		if (newCount < 0)
+			throw new IllegalStateException("Ignore count is already zero"); //$NON-NLS-1$
+
+		currentIgnoreCount.set(newCount == 0 ? null : new Integer(newCount));
+	}
+
+	/**
+	 * Runs the given runnable without tracking dependencies.
+	 * 
+	 * @param runnable
+	 * 
+	 * @since 1.1
+	 */
+	public static void runAndIgnore(Runnable runnable) {
+		setIgnore(true);
+		try {
+			runnable.run();
+		} finally {
+			setIgnore(false);
+		}
+	}
+
+	/*
+	 * 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));
+	}
+
+	private static boolean isIgnore() {
+		return currentIgnoreCount.get() != null;
+	}
+
+	/**
+	 * 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) {
+		if (observable.isDisposed())
+			Assert.isTrue(false, "Getter called on disposed observable " //$NON-NLS-1$
+					+ toString(observable));
+		Realm realm = observable.getRealm();
+		if (!realm.isCurrent())
+			Assert.isTrue(false, "Getter called outside realm of observable " //$NON-NLS-1$
+					+ toString(observable));
+
+		if (isIgnore())
+			return;
+
+		Set getterCalledSet = (Set) currentGetterCalledSet.get();
+		if (getterCalledSet != null && getterCalledSet.add(observable)) {
+			// If anyone is listening for observable usage...
+			IChangeListener changeListener = (IChangeListener) currentChangeListener
+					.get();
+			if (changeListener != null)
+				observable.addChangeListener(changeListener);
+			IStaleListener staleListener = (IStaleListener) currentStaleListener
+					.get();
+			if (staleListener != null)
+				observable.addStaleListener(staleListener);
+		}
+	}
+
+	/**
+	 * Notifies the ObservableTracker that an observable was created.
+	 * 
+	 * @param observable
+	 *            the observable that was created
+	 * @since 1.2
+	 */
+	public static void observableCreated(IObservable observable) {
+		if (isIgnore())
+			return;
+		Set observableCreatedSet = (Set) currentObservableCreatedSet.get();
+		if (observableCreatedSet != null) {
+			observableCreatedSet.add(observable);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Observables.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Observables.java
new file mode 100644
index 0000000..1c08fc1
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Observables.java
@@ -0,0 +1,662 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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, 237718, 245647,
+ *         226289
+ *     Marko Topolnik - bug 184830
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.list.DecoratingObservableList;
+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.DecoratingObservableMap;
+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.DecoratingObservableSet;
+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.DecoratingObservableValue;
+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.observable.ConstantObservableValue;
+import org.eclipse.core.internal.databinding.observable.DelayedObservableValue;
+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.StalenessObservableValue;
+import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableList;
+import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableMap;
+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 observable which delays notification of value change events
+	 * from <code>observable</code> until <code>delay</code> milliseconds have
+	 * elapsed since the last change event. This observable helps to boost
+	 * performance in situations where an observable has computationally
+	 * expensive listeners or many dependencies. A common use of this observable
+	 * is to delay validation of user input until the user stops typing in a UI
+	 * field.
+	 * <p>
+	 * To notify about pending changes, the returned observable fires a stale
+	 * event when the wrapped observable value fires a change event, and remains
+	 * stale until the delay has elapsed and the value change is fired. A call
+	 * to {@link IObservableValue#getValue() getValue()} while a value change is
+	 * pending will fire the value change immediately, short-circuiting the
+	 * delay.
+	 * <p>
+	 * <b>Note:</b>
+	 * <ul>
+	 * <li>Use SWTObservables.observeDelayedValue() instead when the target
+	 * observable is observing a SWT Control, or
+	 * ViewersObservables.observeDelayedValue() when the target observable is
+	 * observing a JFace Viewer. These observables ensure that pending value
+	 * changes are fired when the underlying control loses focus. (Otherwise, it
+	 * is possible for pending changes to be lost if a window is closed before
+	 * the delay has elapsed.)
+	 * <li>This observable does not forward {@link ValueChangingEvent} events
+	 * from a wrapped {@link IVetoableValue}.
+	 * </ul>
+	 * 
+	 * @param delay
+	 *            the delay in milliseconds
+	 * @param observable
+	 *            the observable being delayed
+	 * @return an observable which delays notification of value change events
+	 *         from <code>observable</code> until <code>delay</code>
+	 *         milliseconds have elapsed since the last change event.
+	 * 
+	 * @since 1.2
+	 */
+	public static IObservableValue observeDelayedValue(int delay,
+			IObservableValue observable) {
+		return new DelayedObservableValue(delay, observable);
+	}
+
+	/**
+	 * 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 == 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 == null) {
+			throw new IllegalArgumentException("Set parameter cannot be null"); //$NON-NLS-1$
+		}
+
+		return new UnmodifiableObservableSet(set);
+	}
+
+	/**
+	 * Returns an unmodifiable observable map backed by the given observable
+	 * map.
+	 * 
+	 * @param map
+	 *            the map to wrap in an unmodifiable map
+	 * @return an unmodifiable observable map backed by the given observable
+	 *         map.
+	 * @since 1.2
+	 */
+	public static IObservableMap unmodifiableObservableMap(IObservableMap map) {
+		if (map == null) {
+			throw new IllegalArgumentException("Map parameter cannot be null"); //$NON-NLS-1$
+		}
+
+		return new UnmodifiableObservableMap(map);
+	}
+
+	/**
+	 * 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.class);
+	}
+
+	/**
+	 * 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.class);
+	}
+
+	/**
+	 * 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 ObservableSet(realm, set, elementType) {
+			public synchronized void addChangeListener(IChangeListener listener) {
+			}
+
+			public synchronized void addStaleListener(IStaleListener listener) {
+			}
+
+			public synchronized void addSetChangeListener(
+					ISetChangeListener listener) {
+			}
+		};
+	}
+
+	/**
+	 * Returns an observable value that contains the same value as the given
+	 * observable, and fires the same events as the given observable, but can be
+	 * disposed of without disposing of the wrapped observable.
+	 * 
+	 * @param target
+	 *            the observable value to wrap
+	 * @return a disposable proxy for the given observable value.
+	 * @since 1.2
+	 */
+	public static IObservableValue proxyObservableValue(IObservableValue target) {
+		return new DecoratingObservableValue(target, false);
+	}
+
+	/**
+	 * 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 DecoratingObservableSet(target, false);
+	}
+
+	/**
+	 * 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 DecoratingObservableList(target, false);
+	}
+
+	/**
+	 * Returns an observable map that contains the same entries as the given
+	 * map, and fires the same events as the given map, but can be disposed of
+	 * without disposing of the wrapped map.
+	 * 
+	 * @param target
+	 *            the map to wrap
+	 * @return a disposable proxy for the given observable map
+	 * @since 1.2
+	 */
+	public static IObservableMap proxyObservableMap(IObservableMap target) {
+		return new DecoratingObservableMap(target, false);
+	}
+
+	/**
+	 * 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.class);
+	}
+
+	/**
+	 * 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.class);
+	}
+
+	/**
+	 * 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 ObservableList(realm, list, elementType) {
+			public synchronized void addChangeListener(IChangeListener listener) {
+			}
+
+			public synchronized void addStaleListener(IStaleListener listener) {
+			}
+
+			public synchronized 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.
+	 * @return an observable value that tracks the value associated with the
+	 *         specified key in the given map
+	 * @since 1.2
+	 */
+	public static IObservableValue observeMapEntry(IObservableMap map,
+			Object key) {
+		return observeMapEntry(map, key, map.getValueType());
+	}
+
+	/**
+	 * 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) {
+		if (valueType == null)
+			valueType = map.getValueType();
+		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(
+			final IObservableMap map, final Object valueType) {
+		return new IObservableFactory() {
+			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);
+	}
+
+	/**
+	 * Copies the current value of the source observable to the destination
+	 * observable, and upon value change events fired by the source observable,
+	 * updates the destination observable accordingly, until the source
+	 * observable is disposed. This method assumes that both observables are on
+	 * the same realm.
+	 * 
+	 * @param source
+	 *            the source observable
+	 * @param destination
+	 *            the destination observable
+	 * @since 1.2
+	 */
+	public static void pipe(IObservableValue source,
+			final IObservableValue destination) {
+		destination.setValue(source.getValue());
+		source.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				destination.setValue(event.diff.getNewValue());
+			}
+		});
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Realm.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Realm.java
new file mode 100644
index 0000000..9d540f6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/Realm.java
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *     Boris Bokowski - bug 245647
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.observable.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
+ * <code>Binding</code> 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 #syncExec(Runnable)},
+ * {@link #asyncExec(Runnable)}, or {@link #exec(Runnable)} to execute a
+ * runnable within this realm. Note that using {@link #syncExec(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 #asyncExec(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 #asyncExec(Runnable)},
+ * {@link #syncExec(Runnable)}, or {@link #exec(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 #syncExec(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 (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 boolean isCurrent();
+
+	private Thread workerThread;
+	
+	private volatile Timer timer;
+
+	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(final Runnable runnable) {
+		ISafeRunnable safeRunnable;
+		if (runnable instanceof ISafeRunnable) {
+			safeRunnable = (ISafeRunnable) runnable;
+		} else {
+			safeRunnable = new ISafeRunnable() {
+				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() throws Exception {
+					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 #safeRun(Runnable)} to run the runnable.
+	 * </p>
+	 * 
+	 * @param runnable
+	 */
+	public void asyncExec(Runnable runnable) {
+		synchronized (workQueue) {
+			ensureWorkerThreadIsRunning();
+			workQueue.enqueue(runnable);
+			workQueue.notifyAll();
+		}
+	}
+
+	/**
+	 * Causes the <code>run()</code> method of the runnable to be invoked from
+	 * within this realm after the specified number of milliseconds have
+	 * elapsed. If milliseconds is less than zero, the runnable is not executed.
+	 * 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 #safeRun(Runnable)} to run the runnable.
+	 * </p>
+	 * 
+	 * @param milliseconds
+	 * @param runnable
+	 * @since 1.2
+	 */
+	public void timerExec(int milliseconds, final Runnable runnable) {
+		if (milliseconds < 0) {
+			return;
+		} else if (milliseconds == 0) {
+			asyncExec(runnable);
+		} else {
+			synchronized (workQueue) {
+				if (timer == null) {
+					timer = new Timer(true);
+				}
+				timer.schedule(new TimerTask() {
+					public void run() {
+						asyncExec(runnable);
+					}
+				}, milliseconds);
+			}
+		}
+
+	}
+
+	/**
+	 * 
+	 */
+	private void ensureWorkerThreadIsRunning() {
+		if (workerThread == null) {
+			workerThread = new Thread() {
+				public void run() {
+					try {
+						while (true) {
+							Runnable work = null;
+							synchronized (workQueue) {
+								while (workQueue.isEmpty()) {
+									workQueue.wait();
+								}
+								work = (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 #safeRun(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 implements Runnable {
+		boolean hasRun = false;
+
+		private Runnable runnable;
+
+		SyncRunnable(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);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/StaleEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/StaleEvent.java
new file mode 100644
index 0000000..14e8c76
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/StaleEvent.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable;
+
+/**
+ * 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 extends ObservableEvent {
+
+	/**
+	 * Creates a new stale event.
+	 * 
+	 * @param source
+	 *            the source observable
+	 */
+	public StaleEvent(IObservable source) {
+		super(source);
+	}
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3491012225431471077L;
+
+	static final Object TYPE = new Object();
+
+	protected void dispatch(IObservablesListener listener) {
+		((IStaleListener) listener).handleStale(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/AbstractObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/AbstractObservableList.java
new file mode 100644
index 0000000..8641afb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/AbstractObservableList.java
@@ -0,0 +1,371 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164653, 167204
+ *     Matthew Hall - bugs 118516, 208858, 208332, 247367, 146397, 249526
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+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.DisposeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+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 extends AbstractList implements
+		IObservableList {
+	private final class PrivateChangeSupport extends ChangeSupport {
+		private PrivateChangeSupport(Realm realm) {
+			super(realm);
+		}
+
+		protected void firstListenerAdded() {
+			AbstractObservableList.this.firstListenerAdded();
+		}
+
+		protected void lastListenerRemoved() {
+			AbstractObservableList.this.lastListenerRemoved();
+		}
+
+		protected boolean hasListeners() {
+			return super.hasListeners();
+		}
+	}
+
+	private final Realm realm;
+	private PrivateChangeSupport changeSupport;
+	private boolean disposed = false;
+
+	/**
+	 * @param realm 
+	 * 
+	 */
+	public AbstractObservableList(Realm realm) {
+		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
+		ObservableTracker.observableCreated(this);
+		this.realm = realm;
+		changeSupport = new PrivateChangeSupport(realm);
+	}
+
+	/**
+	 * 
+	 */
+	public AbstractObservableList() {
+		this(Realm.getDefault());
+	}
+	
+	/**
+	 * Returns whether this observable list has any registered listeners.
+	 * 
+	 * @return whether this observable list has any registered listeners.
+	 * @since 1.2
+	 */
+	protected boolean hasListeners() {
+		return changeSupport.hasListeners();
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return false;
+	}
+
+	public synchronized void addListChangeListener(IListChangeListener listener) {
+		if (!disposed)
+			changeSupport.addListener(ListChangeEvent.TYPE, listener);
+	}
+
+	public synchronized void removeListChangeListener(IListChangeListener listener) {
+		if (!disposed)
+			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) {
+		if (!disposed)
+			changeSupport.addChangeListener(listener);
+	}
+
+	public synchronized void removeChangeListener(IChangeListener listener) {
+		if (!disposed)
+			changeSupport.removeChangeListener(listener);
+	}
+
+	public synchronized void addStaleListener(IStaleListener listener) {
+		if (!disposed)
+			changeSupport.addStaleListener(listener);
+	}
+
+	public synchronized void removeStaleListener(IStaleListener listener) {
+		if (!disposed)
+			changeSupport.removeStaleListener(listener);
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized void addDisposeListener(IDisposeListener listener) {
+		if (changeSupport != null) {
+			changeSupport.addDisposeListener(listener);
+		}
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized void removeDisposeListener(IDisposeListener listener) {
+		if (changeSupport != null) {
+			changeSupport.removeDisposeListener(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() {
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized boolean isDisposed() {
+		return disposed;
+	}
+
+	/**
+	 * 
+	 */
+	public synchronized void dispose() {
+		if (!disposed) {
+			disposed = true;
+			changeSupport.fireEvent(new DisposeEvent(this));
+			changeSupport.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 boolean isEmpty() {
+		getterCalled();
+		return super.isEmpty();
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+		return super.contains(o);
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator wrappedIterator = super.iterator();
+		return new Iterator() {
+			public void remove() {
+				wrappedIterator.remove();
+			}
+
+			public boolean 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 boolean 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#accept(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#accept(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 boolean remove(Object o) {
+		getterCalled();
+		return super.remove(o);
+	}
+
+	// Bulk Modification Operations
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return super.containsAll(c);
+	}
+
+	public boolean addAll(Collection c) {
+		getterCalled();
+		return super.addAll(c);
+	}
+
+	public boolean addAll(int index, Collection c) {
+		getterCalled();
+		return super.addAll(c);
+	}
+
+	public boolean removeAll(Collection c) {
+		getterCalled();
+		return super.removeAll(c);
+	}
+
+	public boolean retainAll(Collection c) {
+		getterCalled();
+		return super.retainAll(c);
+	}
+
+	// Comparison and hashing
+
+	public boolean equals(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 realm;
+	}
+	
+	/**
+	 * Asserts that the realm is the current realm.
+	 * 
+	 * @see Realm#isCurrent()
+	 * @throws AssertionFailedException
+	 *             if the realm is not the current realm
+	 */
+	protected void checkRealm() {
+		Assert.isTrue(getRealm().isCurrent(),
+				"This operation must be run within the observable's realm"); //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java
new file mode 100644
index 0000000..6823362
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java
@@ -0,0 +1,330 @@
+/************************************************************************************************************
+ * Copyright (c) 2007, 2009 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 - bugs 211786, 274081
+ ***********************************************************************************************************/
+package org.eclipse.core.databinding.observable.list;
+
+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;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * A lazily calculated list that automatically computes and registers listeners
+ * on its dependencies as long as all of its dependencies are
+ * {@link IObservable} objects. Any change to one of the observable dependencies
+ * causes the list to be recomputed.
+ * <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>
+ * <p>
+ * Example: compute the fibonacci sequence, up to as many elements as the value
+ * of an {@link IObservableValue} &lt; {@link Integer} &gt;.
+ * </p>
+ * 
+ * <pre>
+ * final IObservableValue count = WritableValue.withValueType(Integer.TYPE);
+ * count.setValue(new Integer(0));
+ * IObservableList fibonacci = new ComputedList() {
+ * 	protected List calculate() {
+ * 		int size = ((Integer) count.getValue()).intValue();
+ * 
+ * 		List result = new ArrayList();
+ * 		for (int i = 0; i &lt; size; i++) {
+ * 			if (i == 0)
+ * 				result.add(new Integer(0));
+ * 			else if (i == 1)
+ * 				result.add(new Integer(1));
+ * 			else {
+ * 				Integer left = (Integer) result.get(i - 2);
+ * 				Integer right = (Integer) result.get(i - 1);
+ * 				result.add(new Integer(left.intValue() + right.intValue()));
+ * 			}
+ * 		}
+ * 		return result;
+ * 	}
+ * };
+ * 
+ * System.out.println(fibonacci); // =&gt; &quot;[]&quot;
+ * 
+ * count.setValue(new Integer(5));
+ * System.out.println(fibonacci); // =&gt; &quot;[0, 1, 1, 2, 3]&quot;
+ * </pre>
+ * 
+ * @since 1.1
+ */
+public abstract class ComputedList extends AbstractObservableList {
+	private List cachedList = new ArrayList();
+
+	private boolean dirty = true;
+	private boolean stale = false;
+
+	private IObservable[] dependencies = new IObservable[0];
+
+	/**
+	 * Creates a computed list in the default realm and with an unknown (null)
+	 * element type.
+	 */
+	public ComputedList() {
+		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 ComputedList(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 ComputedList(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 ComputedList(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 implements Runnable, IChangeListener,
+			IStaleListener {
+		public void run() {
+			cachedList = calculate();
+			if (cachedList == 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. Any
+	 * dependencies used to calculate the list must be {@link IObservable}, and
+	 * implementers must use one of the interface methods tagged TrackedGetter
+	 * for ComputedList to recognize it as a dependency.
+	 * 
+	 * @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 ListDiff() {
+				ListDiffEntry[] differences;
+
+				public ListDiffEntry[] getDifferences() {
+					if (differences == null)
+						differences = Diffs.computeListDiff(oldList, getList())
+								.getDifferences();
+					return differences;
+				}
+			});
+		}
+	}
+
+	private void stopListening() {
+		if (dependencies != 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 boolean 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 Runnable() {
+			public void run() {
+				if (dependencies == null) {
+					// We are not currently listening.
+					// But someone is listening for changes. Call getValue()
+					// to make sure we start listening to the observables we
+					// depend on.
+					getList();
+				}
+			}
+		});
+	}
+
+	public synchronized void dispose() {
+		stopListening();
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/DecoratingObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/DecoratingObservableList.java
new file mode 100644
index 0000000..e09a3b4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/DecoratingObservableList.java
@@ -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 237718)
+ *     Matthew Hall - but 246626
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.core.databinding.observable.DecoratingObservableCollection;
+
+/**
+ * An observable list which decorates another observable list.
+ * 
+ * @since 1.2
+ */
+public class DecoratingObservableList extends DecoratingObservableCollection
+		implements IObservableList {
+
+	private IObservableList decorated;
+
+	private IListChangeListener listChangeListener;
+
+	/**
+	 * Constructs a DecoratingObservableList which decorates the given
+	 * observable.
+	 * 
+	 * @param decorated
+	 *            the observable list being decorated
+	 * @param disposeDecoratedOnDispose
+	 */
+	public DecoratingObservableList(IObservableList decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated, disposeDecoratedOnDispose);
+		this.decorated = decorated;
+	}
+
+	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));
+	}
+
+	protected void fireChange() {
+		throw new RuntimeException(
+				"fireChange should not be called, use fireListChange() instead"); //$NON-NLS-1$
+	}
+
+	protected void firstListenerAdded() {
+		if (listChangeListener == null) {
+			listChangeListener = new IListChangeListener() {
+				public void handleListChange(ListChangeEvent event) {
+					DecoratingObservableList.this.handleListChange(event);
+				}
+			};
+		}
+		decorated.addListChangeListener(listChangeListener);
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+		if (listChangeListener != null) {
+			decorated.removeListChangeListener(listChangeListener);
+			listChangeListener = null;
+		}
+	}
+
+	/**
+	 * Called whenever a ListChangeEvent is received from the decorated
+	 * observable. By default, this method fires the list change event again,
+	 * with the decorating observable as the event source. Subclasses may
+	 * override to provide different behavior.
+	 * 
+	 * @param event
+	 *            the change event received from the decorated observable
+	 */
+	protected void handleListChange(final ListChangeEvent event) {
+		fireListChange(event.diff);
+	}
+
+	public void add(int index, Object o) {
+		checkRealm();
+		decorated.add(index, o);
+	}
+
+	public boolean addAll(int index, Collection c) {
+		checkRealm();
+		return decorated.addAll(index, c);
+	}
+
+	public Object get(int index) {
+		getterCalled();
+		return decorated.get(index);
+	}
+
+	public int indexOf(Object o) {
+		getterCalled();
+		return decorated.indexOf(o);
+	}
+
+	public int lastIndexOf(Object o) {
+		getterCalled();
+		return decorated.lastIndexOf(o);
+	}
+
+	public ListIterator listIterator() {
+		return listIterator(0);
+	}
+
+	public ListIterator listIterator(int index) {
+		getterCalled();
+		final ListIterator iterator = decorated.listIterator(index);
+		return new ListIterator() {
+
+			public void add(Object o) {
+				iterator.add(o);
+			}
+
+			public boolean hasNext() {
+				getterCalled();
+				return iterator.hasNext();
+			}
+
+			public boolean hasPrevious() {
+				getterCalled();
+				return iterator.hasPrevious();
+			}
+
+			public Object next() {
+				getterCalled();
+				return iterator.next();
+			}
+
+			public int nextIndex() {
+				getterCalled();
+				return iterator.nextIndex();
+			}
+
+			public Object previous() {
+				getterCalled();
+				return iterator.previous();
+			}
+
+			public int previousIndex() {
+				getterCalled();
+				return iterator.previousIndex();
+			}
+
+			public void remove() {
+				checkRealm();
+				iterator.remove();
+			}
+
+			public void set(Object o) {
+				checkRealm();
+				iterator.set(o);
+			}
+		};
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		checkRealm();
+		return decorated.move(oldIndex, newIndex);
+	}
+
+	public Object remove(int index) {
+		checkRealm();
+		return decorated.remove(index);
+	}
+
+	public Object set(int index, Object element) {
+		checkRealm();
+		return decorated.set(index, element);
+	}
+
+	public List subList(int fromIndex, int toIndex) {
+		getterCalled();
+		return decorated.subList(fromIndex, toIndex);
+	}
+
+	public synchronized void dispose() {
+		if (decorated != null && listChangeListener != null) {
+			decorated.removeListChangeListener(listChangeListener);
+		}
+		decorated = null;
+		listChangeListener = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/IListChangeListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/IListChangeListener.java
new file mode 100644
index 0000000..8372729
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/IListChangeListener.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+import org.eclipse.core.databinding.observable.IObservablesListener;
+
+/**
+ * Listener for changes to observable lists.
+ * 
+ * @since 1.0
+ */
+public interface IListChangeListener extends IObservablesListener {
+
+	/**
+	 * Handle a change to an observable list. The change is described by the
+	 * diff object. The given event object must only be used locally in this
+	 * method because it may be reused for other change notifications. The diff
+	 * object referenced by the event is immutable and may be used non-locally.
+	 * 
+	 * @param event
+	 */
+	void handleListChange(ListChangeEvent event);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/IObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/IObservableList.java
new file mode 100644
index 0000000..858ae23
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/IObservableList.java
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * 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 - bugs 208858, 237718
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+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.
+ * 
+ * @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 extends 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 boolean isEmpty();
+
+	/**
+	 * @TrackedGetter
+	 */
+    public boolean contains(Object o);
+
+	/**
+	 * @TrackedGetter
+	 */
+    public Iterator iterator();
+
+	/**
+	 * @TrackedGetter
+	 */
+    public Object[] toArray();
+
+	/**
+	 * @TrackedGetter
+	 */
+    public Object[] toArray(Object a[]);
+
+	/**
+	 * 
+	 */
+    public boolean add(Object o);
+
+	/**
+	 * 
+	 */
+    public boolean remove(Object o);
+
+	/**
+	 * @TrackedGetter
+	 */
+    public boolean containsAll(Collection c);
+
+	/**
+	 * 
+	 */
+    public boolean addAll(Collection c);
+
+	/**
+	 * 
+	 */
+    public boolean addAll(int index, Collection c);
+
+	/**
+	 * 
+	 */
+    public boolean removeAll(Collection c);
+
+	/**
+	 *
+	 */
+    public boolean retainAll(Collection c);
+
+	/**
+	 * @TrackedGetter
+	 */
+    public boolean equals(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#accept(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#accept(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();
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListChangeEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListChangeEvent.java
new file mode 100644
index 0000000..1b2ce4d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListChangeEvent.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+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 extends 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 ListChangeEvent(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 (IObservableList) getSource();
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((IListChangeListener) listener).handleListChange(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiff.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiff.java
new file mode 100644
index 0000000..5d631cb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiff.java
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208858, 251884, 194734, 272651, 301774
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+import java.util.AbstractList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * Object describing a diff between two lists.
+ * 
+ * @since 1.0
+ */
+public abstract class ListDiff implements IDiff {
+
+	/**
+	 * 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 an add entry is adjacent to a remove entry, and both entries
+	 * operate on the same location in the list.
+	 * <li>{@link ListDiffVisitor#handleMove(int, int, Object)} is called
+	 * whenever an add entry is adjacent to a remove entry, and both entries
+	 * have equivalent elements.
+	 * <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];
+			Object elem = entry.getElement();
+			int pos = entry.getPosition();
+			boolean add = entry.isAddition();
+
+			if (i + 1 < differences.length) {
+				ListDiffEntry nextEntry = differences[i + 1];
+				if (add != nextEntry.isAddition()) {
+					int addPos;
+					Object addElem;
+
+					int removePos;
+					Object removeElem;
+
+					if (add) {
+						addPos = pos;
+						addElem = elem;
+
+						removePos = nextEntry.getPosition();
+						removeElem = nextEntry.getElement();
+
+						if (addPos > removePos) {
+							// a b c d e f -- start
+							// a b c b d e f -- add b at 4
+							// a c b d e f -- remove b at 2
+
+							// net effect is the same as:
+							// a b c d e f -- start
+							// a c d e f -- remove b at 2
+							// a c b d e f -- add b at 3
+
+							addPos--;
+						} else if (removePos > addPos) {
+							// a b c d e f -- start
+							// a d b c d e f -- add d at 2
+							// a d b c e f -- remove d at 5
+
+							// net effect is the same as
+							// a b c d e f -- start
+							// a b c e f -- remove d at 4
+							// a d b c d e f -- add d at 2
+
+							// So we adjust the remove index to fit the indices
+							// of the remove-then-add scenario
+							removePos--;
+						} else {
+							// rare case: element is added and then immediately
+							// removed. Handle the add entry and then continue
+							// iterating starting at the remove entry
+							visitor.handleAdd(pos, elem);
+							continue;
+						}
+					} else {
+						removePos = pos;
+						removeElem = elem;
+
+						addPos = nextEntry.getPosition();
+						addElem = nextEntry.getElement();
+					}
+
+					if (removePos == addPos) {
+						visitor.handleReplace(pos, removeElem, addElem);
+						i++;
+						continue;
+					}
+
+					if (Util.equals(removeElem, addElem)) {
+						visitor.handleMove(removePos, addPos, elem);
+						i++;
+						continue;
+					}
+				}
+			}
+
+			if (add)
+				visitor.handleAdd(pos, elem);
+			else
+				visitor.handleRemove(pos, elem);
+		}
+	}
+
+	/**
+	 * Returns true if the diff contains no added, removed, moved or replaced
+	 * elements.
+	 * 
+	 * @return true if the diff contains no added, removed, moved or replaced
+	 *         elements.
+	 * @since 1.2
+	 */
+	public boolean isEmpty() {
+		return getDifferences().length == 0;
+	}
+
+	/**
+	 * Applies the changes in this diff to the given list
+	 * 
+	 * @param list
+	 *            the list to which the diff will be applied
+	 * @since 1.2
+	 */
+	public void applyTo(final List list) {
+		accept(new ListDiffVisitor() {
+			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);
+			}
+		});
+	}
+
+	/**
+	 * Returns a list showing what <code>list</code> would look like if this
+	 * diff were applied to it.
+	 * <p>
+	 * <b>Note</b>: the returned list is only valid until structural changes are
+	 * made to the passed-in list.
+	 * 
+	 * @param list
+	 *            the list over which the diff will be simulated
+	 * @return an unmodifiable list showing what <code>list</code> would look
+	 *         like if it were passed to the {@link #applyTo(List)} method.
+	 * @see #applyTo(List)
+	 * @since 1.3
+	 */
+	public List simulateOn(List list) {
+		final List[] result = { list };
+		accept(new ListDiffVisitor() {
+			public void handleAdd(int index, Object element) {
+				List first = result[0].subList(0, index);
+				List middle = Collections.singletonList(element);
+				List last = result[0].subList(index, result[0].size());
+				result[0] = ConcatList.cat(first, middle, last);
+			}
+
+			public void handleRemove(int index, Object element) {
+				List first = result[0].subList(0, index);
+				List last = result[0].subList(index + 1, result[0].size());
+				result[0] = ConcatList.cat(first, last);
+			}
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				List first = result[0].subList(0, index);
+				List middle = Collections.singletonList(newElement);
+				List last = result[0].subList(index + 1, result[0].size());
+				result[0] = ConcatList.cat(first, middle, last);
+			}
+		});
+		return result[0];
+	}
+
+	private static class ConcatList extends AbstractList {
+		private final List[] subLists;
+
+		public static List cat(List a, List b, List c) {
+			if (a.isEmpty()) {
+				return cat(b, c);
+			} else if (b.isEmpty()) {
+				return cat(a, c);
+			} else if (c.isEmpty()) {
+				return cat(a, b);
+			}
+			return new ConcatList(new List[] { a, b, c });
+		}
+
+		public static List cat(List a, List b) {
+			if (a.isEmpty()) {
+				if (b.isEmpty()) {
+					return Collections.EMPTY_LIST;
+				}
+				return b;
+			} else if (b.isEmpty()) {
+				return a;
+			}
+			return new ConcatList(new List[] { a, b });
+		}
+
+		private ConcatList(List[] sublists) {
+			this.subLists = sublists;
+		}
+
+		public Object get(int index) {
+			int offset = 0;
+			for (int i = 0; i < subLists.length; i++) {
+				int subListIndex = index - offset;
+				if (subListIndex < subLists[i].size()) {
+					return subLists[i].get(subListIndex);
+				}
+				offset += subLists[i].size();
+			}
+			throw new IndexOutOfBoundsException();
+		}
+
+		public int size() {
+			int size = 0;
+			for (int i = 0; i < subLists.length; i++) {
+				size += subLists[i].size();
+			}
+			return size;
+		}
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		ListDiffEntry[] differences = getDifferences();
+		StringBuffer buffer = new StringBuffer();
+		buffer.append(getClass().getName());
+
+		if (differences == null || differences.length == 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] != null ? differences[i]
+										.toString() : "null") //$NON-NLS-1$
+						.append("]"); //$NON-NLS-1$
+			}
+			buffer.append("}"); //$NON-NLS-1$
+		}
+
+		return buffer.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiffEntry.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiffEntry.java
new file mode 100644
index 0000000..da2e79f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiffEntry.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+/**
+ * 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 boolean 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() != null ? getElement().toString() : "null") //$NON-NLS-1$
+			.append("]}"); //$NON-NLS-1$
+		
+		return buffer.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiffVisitor.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiffVisitor.java
new file mode 100644
index 0000000..acbd89f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ListDiffVisitor.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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 208858)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+import java.util.List;
+
+/**
+ * A visitor for processing differences in a ListDiff.
+ * 
+ * @see ListDiff#accept(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);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/MultiList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/MultiList.java
new file mode 100644
index 0000000..453fcc7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/MultiList.java
@@ -0,0 +1,567 @@
+/*******************************************************************************
+ * 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 222289)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+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.Realm;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * An observable list backed by an array of observable lists. This class
+ * supports all removal methods (including {@link #clear()}), as well as the
+ * {@link #set(int, Object)} method. All other mutator methods (addition methods
+ * and {@link #move(int, int)}) throw an {@link UnsupportedOperationException}.
+ * 
+ * @since 1.2
+ */
+public class MultiList extends AbstractObservableList {
+	private IObservableList[] lists;
+	private Object elementType;
+
+	private IListChangeListener listChangeListener;
+	private IStaleListener staleListener;
+	private Boolean stale;
+
+	/**
+	 * Constructs a MultiList in the default realm, and backed by the given
+	 * observable lists.
+	 * 
+	 * @param lists
+	 *            the array of observable lists backing this MultiList.
+	 */
+	public MultiList(IObservableList[] lists) {
+		this(Realm.getDefault(), lists, null);
+	}
+
+	/**
+	 * Constructs a MultiList in the default realm backed by the given
+	 * observable lists.
+	 * 
+	 * @param lists
+	 *            the array of observable lists backing this MultiList.
+	 * @param elementType
+	 *            element type of the constructed list.
+	 */
+	public MultiList(IObservableList[] lists, Object elementType) {
+		this(Realm.getDefault(), lists, elementType);
+	}
+
+	/**
+	 * Constructs a MultiList belonging to the given realm, and backed by the
+	 * given observable lists.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param lists
+	 *            the array of observable lists backing this MultiList
+	 */
+	public MultiList(Realm realm, IObservableList[] lists) {
+		this(realm, lists, null);
+	}
+
+	/**
+	 * Constructs a MultiList belonging to the given realm, and backed by the
+	 * given observable lists.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param lists
+	 *            the array of observable lists backing this MultiList
+	 * @param elementType
+	 *            element type of the constructed list.
+	 */
+	public MultiList(Realm realm, IObservableList[] lists, Object elementType) {
+		super(realm);
+		this.lists = lists;
+		this.elementType = elementType;
+
+		for (int i = 0; i < lists.length; i++) {
+			Assert
+					.isTrue(realm.equals(lists[i].getRealm()),
+							"All source lists in a MultiList must belong to the same realm"); //$NON-NLS-1$
+		}
+	}
+
+	protected void firstListenerAdded() {
+		if (listChangeListener == null) {
+			listChangeListener = new IListChangeListener() {
+				public void handleListChange(final ListChangeEvent event) {
+					getRealm().exec(new Runnable() {
+						public void run() {
+							stale = null;
+							listChanged(event);
+							if (isStale())
+								fireStale();
+						}
+					});
+				}
+			};
+		}
+		if (staleListener == null) {
+			staleListener = new IStaleListener() {
+				public void handleStale(StaleEvent staleEvent) {
+					getRealm().exec(new Runnable() {
+						public void run() {
+							makeStale();
+						}
+					});
+				}
+			};
+		}
+
+		for (int i = 0; i < lists.length; i++) {
+			lists[i].addListChangeListener(listChangeListener);
+			lists[i].addStaleListener(staleListener);
+
+			// Determining staleness at this time prevents firing redundant
+			// stale events if MultiList happens to be stale now, and a sublist
+			// fires a stale event later.
+			this.stale = computeStaleness() ? Boolean.TRUE : Boolean.FALSE;
+		}
+	}
+
+	protected void lastListenerRemoved() {
+		if (listChangeListener != null) {
+			for (int i = 0; i < lists.length; i++) {
+				lists[i].removeListChangeListener(listChangeListener);
+			}
+			listChangeListener = null;
+		}
+		if (staleListener != null) {
+			for (int i = 0; i < lists.length; i++) {
+				lists[i].removeStaleListener(staleListener);
+			}
+			staleListener = null;
+		}
+		stale = null;
+	}
+
+	private void makeStale() {
+		if (stale == null || stale.booleanValue() == false) {
+			stale = Boolean.TRUE;
+			fireStale();
+		}
+	}
+
+	private void listChanged(ListChangeEvent event) {
+		IObservableList source = event.getObservableList();
+		int offset = 0;
+		for (int i = 0; i < lists.length; i++) {
+			if (source == lists[i]) {
+				fireListChange(offsetListDiff(offset, event.diff));
+				return;
+			}
+			offset += lists[i].size();
+		}
+		Assert
+				.isLegal(
+						false,
+						"MultiList received a ListChangeEvent from an observable list that is not one of its sources."); //$NON-NLS-1$
+	}
+
+	private ListDiff offsetListDiff(int offset, ListDiff diff) {
+		return Diffs.createListDiff(offsetListDiffEntries(offset, diff
+				.getDifferences()));
+	}
+
+	private ListDiffEntry[] offsetListDiffEntries(int offset,
+			ListDiffEntry[] entries) {
+		ListDiffEntry[] offsetEntries = new ListDiffEntry[entries.length];
+		for (int i = 0; i < entries.length; i++) {
+			offsetEntries[i] = offsetListDiffEntry(offset, entries[i]);
+		}
+		return offsetEntries;
+	}
+
+	private ListDiffEntry offsetListDiffEntry(int offset, ListDiffEntry entry) {
+		return Diffs.createListDiffEntry(offset + entry.getPosition(), entry
+				.isAddition(), entry.getElement());
+	}
+
+	protected int doGetSize() {
+		int size = 0;
+		for (int i = 0; i < lists.length; i++)
+			size += lists[i].size();
+		return size;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	public boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void add(int index, Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(int index, Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		checkRealm();
+		for (int i = 0; i < lists.length; i++)
+			lists[i].clear();
+	}
+
+	public Object get(int index) {
+		getterCalled();
+		int offset = 0;
+		for (int i = 0; i < lists.length; i++) {
+			if (index - offset < lists[i].size())
+				return lists[i].get(index - offset);
+			offset += lists[i].size();
+		}
+		throw new IndexOutOfBoundsException("index: " + index + ", size: " //$NON-NLS-1$ //$NON-NLS-2$
+				+ offset);
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+		for (int i = 0; i < lists.length; i++) {
+			if (lists[i].contains(o))
+				return true;
+		}
+		return false;
+	}
+
+	public boolean equals(Object o) {
+		getterCalled();
+		if (o == this)
+			return true;
+		if (o == null)
+			return false;
+		if (!(o instanceof List))
+			return false;
+		List that = (List) o;
+		if (doGetSize() != that.size())
+			return false;
+
+		int subListIndex = 0;
+		for (int i = 0; i < lists.length; i++) {
+			List subList = that.subList(subListIndex, subListIndex
+					+ lists[i].size());
+			if (!lists[i].equals(subList)) {
+				return false;
+			}
+			subListIndex += lists[i].size();
+		}
+		return true;
+	}
+
+	public int hashCode() {
+		getterCalled();
+		int result = 1;
+		for (int i = 0; i < lists.length; i++) {
+			result = result * 31 + lists[i].hashCode();
+		}
+		return result;
+	}
+
+	public int indexOf(Object o) {
+		getterCalled();
+		int offset = 0;
+		for (int i = 0; i < lists.length; i++) {
+			int index = lists[i].indexOf(o);
+			if (index != -1)
+				return offset + index;
+			offset += lists[i].size();
+		}
+		return -1;
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		for (int i = 0; i < lists.length; i++) {
+			if (!lists[i].isEmpty())
+				return false;
+		}
+		return true;
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return new MultiListItr();
+	}
+
+	public int lastIndexOf(Object o) {
+		getterCalled();
+		int offset = size();
+		for (int i = 0; i < lists.length; i++) {
+			offset -= lists[i].size();
+			int index = lists[i].indexOf(o);
+			if (index != -1)
+				return offset + index;
+		}
+		return -1;
+	}
+
+	public ListIterator listIterator(int index) {
+		getterCalled();
+		return new MultiListListItr(index);
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		checkRealm();
+		int i = indexOf(o);
+		if (i != -1) {
+			remove(i);
+			return true;
+		}
+		return false;
+	}
+
+	public Object remove(int index) {
+		int offset = 0;
+		for (int i = 0; i < lists.length; i++) {
+			if (index - offset < lists[i].size()) {
+				return lists[i].remove(index - offset);
+			}
+			offset += lists[i].size();
+		}
+		throw new IndexOutOfBoundsException("index: " + index + ", size: " //$NON-NLS-1$ //$NON-NLS-2$
+				+ offset);
+	}
+
+	public boolean removeAll(Collection c) {
+		boolean changed = false;
+		for (int i = 0; i < lists.length; i++) {
+			changed = changed | lists[i].removeAll(c);
+		}
+		return changed;
+	}
+
+	public boolean retainAll(Collection c) {
+		boolean changed = false;
+		for (int i = 0; i < lists.length; i++) {
+			changed = changed | lists[i].retainAll(c);
+		}
+		return changed;
+	}
+
+	public Object set(int index, Object o) {
+		int offset = 0;
+		for (int i = 0; i < lists.length; i++) {
+			if (index - offset < lists[i].size()) {
+				return lists[i].set(index - offset, o);
+			}
+			offset += lists[i].size();
+		}
+		throw new IndexOutOfBoundsException("index: " + index + ", size: " //$NON-NLS-1$ //$NON-NLS-2$
+				+ offset);
+	}
+
+	public Object[] toArray() {
+		getterCalled();
+		return toArray(new Object[doGetSize()]);
+	}
+
+	public Object[] toArray(Object[] a) {
+		getterCalled();
+		Object[] result = a;
+		if (result.length < doGetSize()) {
+			result = (Object[]) Array.newInstance(a.getClass()
+					.getComponentType(), doGetSize());
+		}
+		int offset = 0;
+		for (int i = 0; i < lists.length; i++) {
+			Object[] oa = lists[i].toArray();
+			System.arraycopy(oa, 0, result, offset, oa.length);
+			offset += lists[i].size();
+		}
+
+		return result;
+	}
+
+	public boolean isStale() {
+		getterCalled();
+
+		if (staleListener == null || listChangeListener == null) {
+			// this.stale is only updated in response to list change events or
+			// stale events on the sublists. If we are not listening to sublists
+			// then we must calculate staleness on every invocation.
+			return computeStaleness();
+		}
+
+		if (stale == null) {
+			this.stale = computeStaleness() ? Boolean.TRUE : Boolean.FALSE;
+		}
+
+		return stale.booleanValue();
+	}
+
+	private boolean computeStaleness() {
+		boolean stale = false;
+		for (int i = 0; i < lists.length; i++) {
+			if (lists[i].isStale()) {
+				stale = true;
+				break;
+			}
+		}
+		return stale;
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public synchronized void dispose() {
+		if (lists != null) {
+			if (listChangeListener != null) {
+				for (int i = 0; i < lists.length; i++) {
+					lists[i].removeListChangeListener(listChangeListener);
+				}
+			}
+			if (staleListener != null) {
+				for (int i = 0; i < lists.length; i++) {
+					lists[i].removeStaleListener(staleListener);
+				}
+			}
+		}
+		listChangeListener = null;
+		staleListener = null;
+		lists = null;
+		elementType = null;
+		stale = null;
+		super.dispose();
+	}
+
+	private final class MultiListItr implements Iterator {
+		Iterator[] iters;
+		int iterIndex = 0;
+
+		MultiListItr() {
+			iters = new Iterator[lists.length];
+			for (int i = 0; i < lists.length; i++) {
+				iters[i] = lists[i].iterator();
+			}
+		}
+
+		public boolean hasNext() {
+			for (int i = iterIndex; i < iters.length; i++) {
+				if (iters[i].hasNext())
+					return true;
+			}
+			return false;
+		}
+
+		public Object next() {
+			while (iterIndex < iters.length && !iters[iterIndex].hasNext())
+				iterIndex++;
+			return iters[iterIndex].next();
+		}
+
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+	}
+
+	private class MultiListListItr implements ListIterator {
+		ListIterator[] iters;
+		int iterIndex;
+
+		private MultiListListItr(int initialIndex) {
+			iters = new ListIterator[lists.length];
+			int offset = 0;
+			for (int i = 0; i < lists.length; i++) {
+				if (offset <= initialIndex) {
+					if (offset + lists[i].size() > initialIndex) {
+						// current list contains initial index
+						iters[i] = lists[i].listIterator(initialIndex - offset);
+						iterIndex = i;
+					} else {
+						// current list ends before initial index
+						iters[i] = lists[i].listIterator(lists[i].size());
+					}
+				} else {
+					// current list begins after initial index
+					iters[i] = lists[i].listIterator();
+				}
+				offset += lists[i].size();
+			}
+		}
+
+		public void add(Object o) {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean hasNext() {
+			for (int i = iterIndex; i < iters.length; i++) {
+				if (iters[i].hasNext())
+					return true;
+			}
+			return false;
+		}
+
+		public boolean hasPrevious() {
+			for (int i = iterIndex; i >= 0; i--) {
+				if (iters[i].hasPrevious())
+					return true;
+			}
+			return false;
+		}
+
+		public Object next() {
+			while (iterIndex < iters.length && !iters[iterIndex].hasNext())
+				iterIndex++;
+			return iters[iterIndex].next();
+		}
+
+		public int nextIndex() {
+			int offset = 0;
+			for (int i = 0; i < iterIndex; i++)
+				offset += iters[i].nextIndex();
+			return offset + iters[iterIndex].nextIndex();
+		}
+
+		public Object previous() {
+			while (iterIndex >= 0 && !iters[iterIndex].hasPrevious())
+				iterIndex--;
+			return iters[iterIndex].previous();
+		}
+
+		public int previousIndex() {
+			int offset = 0;
+			for (int i = 0; i < iterIndex; i++)
+				offset += iters[i].nextIndex();
+			return offset + iters[iterIndex].previousIndex();
+		}
+
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+
+		public void set(Object o) {
+			iters[iterIndex].set(o);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ObservableList.java
new file mode 100644
index 0000000..be1ffe9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ObservableList.java
@@ -0,0 +1,370 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164653, 167204
+ *     Matthew Hall - bugs 208858, 208332, 274450
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.list;
+
+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 extends AbstractObservable implements
+		IObservableList {
+
+	protected List wrappedList;
+
+	/**
+	 * Stale state of the list. Access must occur in the current realm.
+	 */
+	private boolean stale = false;
+
+	private Object elementType;
+
+	protected ObservableList(List wrappedList, Object elementType) {
+		this(Realm.getDefault(), wrappedList, elementType);
+	}
+
+	protected ObservableList(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 boolean contains(Object o) {
+		getterCalled();
+		return wrappedList.contains(o);
+	}
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return wrappedList.containsAll(c);
+	}
+
+	public boolean equals(Object o) {
+		getterCalled();
+		return o == this || wrappedList.equals(o);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return wrappedList.hashCode();
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return wrappedList.isEmpty();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator wrappedIterator = wrappedList.iterator();
+		return new Iterator() {
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean 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 ListIterator() {
+
+			public int nextIndex() {
+				return wrappedIterator.nextIndex();
+			}
+
+			public int previousIndex() {
+				return wrappedIterator.previousIndex();
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean hasNext() {
+				return wrappedIterator.hasNext();
+			}
+
+			public boolean 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(final int fromIndex, final int toIndex) {
+		getterCalled();
+		if (fromIndex < 0 || fromIndex > toIndex || toIndex > size()) {
+			throw new IndexOutOfBoundsException();
+		}
+		return new AbstractObservableList(getRealm()) {
+
+			public Object getElementType() {
+				return ObservableList.this.getElementType();
+			}
+
+			public Object get(int location) {
+				return ObservableList.this.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#accept(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#accept(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 boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void add(int index, Object element) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(int index, Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean 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 boolean 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 boolean is true and this observable list was not already
+	 *            stale.
+	 */
+	public void setStale(boolean stale) {
+		checkRealm();
+
+		boolean 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$
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected void updateWrappedList(List newList) {
+		List oldList = wrappedList;
+		ListDiff listDiff = Diffs.computeListDiff(oldList, newList);
+		wrappedList = newList;
+		fireListChange(listDiff);
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java
new file mode 100644
index 0000000..7d447a5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/WritableList.java
@@ -0,0 +1,288 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164653, 167204
+ *     Gautam Saggar - bug 169529
+ *     Brad Reynolds - bug 147515
+ *     Sebastian Fuchs <spacehorst@gmail.com> - bug 243848
+ *     Matthew Hall - bugs 208858, 213145, 243848
+ *     Ovidio Mallo - bug 332367
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.list;
+
+import java.util.ArrayList;
+import java.util.Collection;
+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.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 extends ObservableList {
+
+	/**
+	 * Creates an empty writable list in the default realm with a
+	 * <code>null</code> element type.
+	 * 
+	 */
+	public WritableList() {
+		this(Realm.getDefault());
+	}
+
+	/**
+	 * Creates an empty writable list with a <code>null</code> element type.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 */
+	public WritableList(Realm realm) {
+		this(realm, new ArrayList(), null);
+	}
+
+	/**
+	 * Constructs a new instance with the default realm. Note that for backwards
+	 * compatibility reasons, the contents of the created WritableList will
+	 * change with the contents of the given list. If this is not desired,
+	 * {@link #WritableList(Collection, Object)} should be used by casting the
+	 * first argument to {@link Collection}.
+	 * 
+	 * @param toWrap
+	 *            The java.util.List to wrap
+	 * @param elementType
+	 *            can be <code>null</code>
+	 */
+	public WritableList(List toWrap, Object elementType) {
+		this(Realm.getDefault(), toWrap, elementType);
+	}
+
+	/**
+	 * 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 WritableList.
+	 * 
+	 * @param collection
+	 *            the collection to copy
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @since 1.2
+	 */
+	public WritableList(Collection collection, Object elementType) {
+		this(Realm.getDefault(), new ArrayList(collection), elementType);
+	}
+
+	/**
+	 * Creates a writable list containing elements of the given type, wrapping
+	 * an existing client-supplied list. Note that for backwards compatibility
+	 * reasons, the contents of the created WritableList will change with the
+	 * contents of the given list. If this is not desired,
+	 * {@link #WritableList(Realm, Collection, Object)} should be used by
+	 * casting the second argument to {@link Collection}.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param toWrap
+	 *            The java.util.List to wrap
+	 * @param elementType
+	 *            can be <code>null</code>
+	 */
+	public WritableList(Realm realm, List toWrap, Object elementType) {
+		super(realm, toWrap, elementType);
+	}
+
+	/**
+	 * 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 WritableList.
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param collection
+	 *            the collection to copy
+	 * @param elementType
+	 *            can be <code>null</code>
+	 * @since 1.2
+	 */
+	public WritableList(Realm realm, Collection collection, Object elementType) {
+		super(realm, new ArrayList(collection), 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 == 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 boolean add(Object element) {
+		checkRealm();
+		boolean 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 boolean 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);
+		}
+		boolean added = wrappedList.addAll(c);
+		fireListChange(Diffs.createListDiff(entries));
+		return added;
+	}
+
+	public boolean 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);
+		}
+		boolean added = wrappedList.addAll(index, c);
+		fireListChange(Diffs.createListDiff(entries));
+		return added;
+	}
+
+	public boolean remove(Object o) {
+		checkRealm();
+		int index = wrappedList.indexOf(o);
+		if (index == -1) {
+			return false;
+		}
+		wrappedList.remove(index);
+		fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index,
+				false, o)));
+		return true;
+	}
+
+	public boolean 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 != -1) {
+				wrappedList.remove(removeIndex);
+				entries.add(Diffs.createListDiffEntry(removeIndex, false,
+						element));
+			}
+		}
+		if (entries.size() > 0)
+			fireListChange(Diffs.createListDiff((ListDiffEntry[]) entries
+					.toArray(new ListDiffEntry[entries.size()])));
+		return entries.size() > 0;
+	}
+
+	public boolean 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((ListDiffEntry[]) entries
+					.toArray(new ListDiffEntry[entries.size()])));
+		return entries.size() > 0;
+	}
+
+	public void clear() {
+		checkRealm();
+		// We remove the elements from back to front which is typically much
+		// faster on common list implementations like ArrayList.
+		ListDiffEntry[] entries = new ListDiffEntry[wrappedList.size()];
+		int entryIndex = 0;
+		for (ListIterator it = wrappedList.listIterator(wrappedList.size()); it
+				.hasPrevious();) {
+			int elementIndex = it.previousIndex();
+			Object element = it.previous();
+			entries[entryIndex++] = Diffs.createListDiffEntry(elementIndex,
+					false, element);
+		}
+		wrappedList.clear();
+		fireListChange(Diffs.createListDiff(entries));
+	}
+
+	/**
+	 * @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);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/package.html
new file mode 100644
index 0000000..2c2f1db
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.java
new file mode 100644
index 0000000..fddee3e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 118516, 146397, 226289, 246103, 249526, 264307
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+import java.util.AbstractMap;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.ChangeSupport;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+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;
+
+/**
+ * 
+ * <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 extends AbstractMap implements
+		IObservableMap {
+
+	private final class PrivateChangeSupport extends ChangeSupport {
+		private PrivateChangeSupport(Realm realm) {
+			super(realm);
+		}
+
+		protected void firstListenerAdded() {
+			AbstractObservableMap.this.firstListenerAdded();
+		}
+
+		protected void lastListenerRemoved() {
+			AbstractObservableMap.this.lastListenerRemoved();
+		}
+
+		protected boolean hasListeners() {
+			return super.hasListeners();
+		}
+	}
+
+	private final Realm realm;
+	private PrivateChangeSupport changeSupport;
+	private boolean disposed = false;
+
+	private boolean stale;
+
+	/**
+	 */
+	public AbstractObservableMap() {
+		this(Realm.getDefault());
+	}
+
+	/**
+	 * 
+	 */
+	protected void lastListenerRemoved() {
+	}
+
+	/**
+	 * 
+	 */
+	protected void firstListenerAdded() {
+	}
+
+	/**
+	 * @param realm
+	 */
+	public AbstractObservableMap(Realm realm) {
+		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
+		ObservableTracker.observableCreated(this);
+		this.realm = realm;
+		changeSupport = new PrivateChangeSupport(realm);
+	}
+
+	public synchronized void addMapChangeListener(IMapChangeListener listener) {
+		if (!disposed)
+			changeSupport.addListener(MapChangeEvent.TYPE, listener);
+	}
+
+	public synchronized void removeMapChangeListener(IMapChangeListener listener) {
+		if (!disposed)
+			changeSupport.removeListener(MapChangeEvent.TYPE, listener);
+	}
+
+	public synchronized void addChangeListener(IChangeListener listener) {
+		if (!disposed)
+			changeSupport.addChangeListener(listener);
+	}
+
+	public synchronized void addStaleListener(IStaleListener listener) {
+		if (!disposed)
+			changeSupport.addStaleListener(listener);
+	}
+
+	/**
+	 * @return whether the observable map has listeners registered
+	 * @since 1.2
+	 */
+	protected synchronized boolean hasListeners() {
+		return !disposed && changeSupport.hasListeners();
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public void addDisposeListener(IDisposeListener listener) {
+		if (!disposed)
+			changeSupport.addDisposeListener(listener);
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public void removeDisposeListener(IDisposeListener listener) {
+		if (!disposed)
+			changeSupport.removeDisposeListener(listener);
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public synchronized boolean isDisposed() {
+		return disposed;
+	}
+
+	public synchronized void dispose() {
+		if (!disposed) {
+			disposed = true;
+			changeSupport.fireEvent(new DisposeEvent(this));
+			changeSupport.dispose();
+			changeSupport = null;
+		}
+	}
+
+	public Realm getRealm() {
+		return realm;
+	}
+
+	public boolean isStale() {
+		checkRealm();
+		return stale;
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getKeyType() {
+		return null;
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getValueType() {
+		return null;
+	}
+
+	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(boolean 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();
+		fireChange();
+		changeSupport.fireEvent(new MapChangeEvent(this, diff));
+	}
+
+	/**
+	 * Asserts that the realm is the current realm.
+	 * 
+	 * @see Realm#isCurrent()
+	 * @throws AssertionFailedException
+	 *             if the realm is not the current realm
+	 */
+	protected void checkRealm() {
+		Assert.isTrue(getRealm().isCurrent(),
+				"This operation must be run within the observable's realm"); //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/BidiObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/BidiObservableMap.java
new file mode 100644
index 0000000..600cd66
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/BidiObservableMap.java
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *         (through BidirectionalMap.java)
+ *     Matthew Hall - bug 233306
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.map;
+
+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;
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * An
+ * <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.2
+ */
+public class BidiObservableMap extends DecoratingObservableMap {
+	/**
+	 * Inverse of wrapped map. When multiple keys map to the same value, they
+	 * are combined into a Set. This field is null when no listeners are
+	 * registered on this observable.
+	 */
+	private Map valuesToKeys;
+
+	/**
+	 * Constructs a BidirectionalMap tracking the given observable map.
+	 * 
+	 * @param wrappedMap
+	 *            the observable map to track
+	 */
+	public BidiObservableMap(IObservableMap wrappedMap) {
+		super(wrappedMap, false);
+	}
+
+	protected void firstListenerAdded() {
+		valuesToKeys = new HashMap();
+		for (Iterator it = entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Entry) it.next();
+			addMapping(entry.getKey(), entry.getValue());
+		}
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+		valuesToKeys.clear();
+		valuesToKeys = null;
+	}
+
+	protected 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));
+		}
+		super.handleMapChange(event);
+	}
+
+	public boolean containsValue(Object value) {
+		getterCalled();
+		// Faster lookup
+		if (valuesToKeys != null)
+			return valuesToKeys.containsKey(value);
+		return super.containsValue(value);
+	}
+
+	/**
+	 * Adds a mapping from value to key in the valuesToKeys map.
+	 * 
+	 * @param key
+	 *            the key being mapped
+	 * @param value
+	 *            the value being mapped
+	 */
+	private void addMapping(Object key, Object value) {
+		if (!valuesToKeys.containsKey(value)) {
+			if (key instanceof Set)
+				key = new HashSet(Collections.singleton(key));
+			valuesToKeys.put(value, key);
+		} else {
+			Object elementOrSet = valuesToKeys.get(value);
+			Set set;
+			if (elementOrSet instanceof Set) {
+				set = (Set) elementOrSet;
+			} else {
+				set = new HashSet(Collections.singleton(elementOrSet));
+				valuesToKeys.put(value, set);
+			}
+			set.add(key);
+		}
+	}
+
+	/**
+	 * Removes a mapping from value to key in the valuesToKeys map.
+	 * 
+	 * @param key
+	 *            the key being unmapped
+	 * @param value
+	 *            the value being unmapped
+	 */
+	private void removeMapping(Object key, Object value) {
+		if (valuesToKeys.containsKey(value)) {
+			Object elementOrSet = valuesToKeys.get(value);
+			if (elementOrSet instanceof Set) {
+				Set set = (Set) elementOrSet;
+				set.remove(key);
+				if (set.isEmpty()) {
+					valuesToKeys.remove(value);
+				}
+			} else if (elementOrSet == key
+					|| (elementOrSet != null && elementOrSet.equals(key))) {
+				valuesToKeys.remove(value);
+			}
+		}
+	}
+
+	/**
+	 * Returns the Set of keys that currently map to the given value.
+	 * 
+	 * @param value
+	 *            the value associated with the keys in the returned Set.
+	 * @return the Set of keys that map to the given value. If no keys map to
+	 *         the given value, an empty set is returned.
+	 */
+	public Set getKeys(Object value) {
+		// valuesToKeys is null when no listeners are registered
+		if (valuesToKeys == null)
+			return findKeys(value);
+
+		if (!valuesToKeys.containsKey(value))
+			return Collections.EMPTY_SET;
+		Object elementOrSet = valuesToKeys.get(value);
+		if (elementOrSet instanceof Set)
+			return Collections.unmodifiableSet((Set) elementOrSet);
+		return Collections.singleton(elementOrSet);
+	}
+
+	/**
+	 * Iterates the map and returns the set of keys which currently map to the
+	 * given value.
+	 * 
+	 * @param value
+	 *            the value to search for
+	 * @return the set of keys which currently map to the specified value.
+	 */
+	private Set findKeys(Object value) {
+		Set keys = new HashSet();
+		for (Iterator it = entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Map.Entry) it.next();
+			if (Util.equals(entry.getValue(), value))
+				keys.add(entry.getKey());
+		}
+		return keys;
+	}
+
+	public synchronized void dispose() {
+		if (valuesToKeys != null) {
+			valuesToKeys.clear();
+			valuesToKeys = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/BidirectionalMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/BidirectionalMap.java
new file mode 100644
index 0000000..788086b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/BidirectionalMap.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 233306
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.map;
+
+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
+ * 
+ * @deprecated This class is deprecated; use {@link BidiObservableMap} instead.
+ */
+public class BidirectionalMap extends ObservableMap {
+	private IMapChangeListener mapListener = new IMapChangeListener() {
+		public void handleMapChange(MapChangeEvent event) {
+			fireMapChange(event.diff);
+		}
+	};
+
+	/**
+	 * @param wrappedMap
+	 */
+	public BidirectionalMap(IObservableMap wrappedMap) {
+		super(wrappedMap.getRealm(), wrappedMap);
+		wrappedMap.addMapChangeListener(mapListener);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/CompositeMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/CompositeMap.java
new file mode 100644
index 0000000..31bc661
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/CompositeMap.java
@@ -0,0 +1,323 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 233306, 226289, 190881
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.map;
+
+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 extends ObservableMap {
+	// 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 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);
+				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);
+				boolean removed = firstMap.getKeys(oldValue).isEmpty();
+				boolean 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 (firstMap.getKeys(oldValue).isEmpty()) {
+					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 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 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 = firstMap.getKeys(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 = firstMap.getKeys(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 != 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 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 BidiObservableMap firstMap;
+	private IObservableMap secondMap;
+
+	private static class WritableSetPlus extends 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 CompositeMap(IObservableMap firstMap,
+			IObservableFactory secondMapFactory) {
+		super(firstMap.getRealm(), new HashMap());
+		this.firstMap = new BidiObservableMap(firstMap);
+		this.firstMap.addMapChangeListener(firstMapListener);
+		rangeSet.addAll(this.firstMap.values());
+		this.secondMap = (IObservableMap) secondMapFactory
+				.createObservable(rangeSet);
+		secondMap.addMapChangeListener(secondMapListener);
+		for (Iterator it = this.firstMap.entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Entry) it.next();
+			wrappedMap.put(entry.getKey(), secondMap.get(entry.getValue()));
+		}
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getKeyType() {
+		return firstMap.getKeyType();
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getValueType() {
+		return secondMap.getValueType();
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+		if (firstMap != null) {
+			firstMap.removeMapChangeListener(firstMapListener);
+			firstMap = null;
+		}
+		if (secondMap != null) {
+			secondMap.dispose();
+			secondMap = null;
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/ComputedObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/ComputedObservableMap.java
new file mode 100644
index 0000000..60aaadd
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/ComputedObservableMap.java
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 241585, 247394, 226289, 194734, 190881, 266754,
+ *                    268688
+ *     Ovidio Mallo - bug 303847
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+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.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ISetChangeListener;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+
+/**
+ * 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 extends AbstractObservableMap {
+
+	private IObservableSet keySet;
+
+	private Set knownKeys;
+
+	private Object valueType;
+
+	private ISetChangeListener setChangeListener = new 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 = null;
+				if (removedKey != null) {
+					oldValue = doGet(removedKey);
+					unhookListener(removedKey);
+					knownKeys.remove(removedKey);
+				}
+				oldValues.put(removedKey, oldValue);
+			}
+			for (Iterator it = addedKeys.iterator(); it.hasNext();) {
+				Object addedKey = it.next();
+				Object newValue = null;
+				if (addedKey != null) {
+					newValue = doGet(addedKey);
+					hookListener(addedKey);
+					knownKeys.add(addedKey);
+				}
+				newValues.put(addedKey, newValue);
+			}
+			fireMapChange(Diffs.createMapDiff(addedKeys, removedKeys,
+					Collections.EMPTY_SET, oldValues, newValues));
+		}
+	};
+
+	private IStaleListener staleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private Set entrySet = new EntrySet();
+
+	private class EntrySet extends AbstractSet {
+
+		public Iterator iterator() {
+			final Iterator keyIterator = keySet.iterator();
+			return new Iterator() {
+
+				public boolean hasNext() {
+					return keyIterator.hasNext();
+				}
+
+				public Object next() {
+					final Object key = keyIterator.next();
+					return new Map.Entry() {
+
+						public Object getKey() {
+							getterCalled();
+							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 ComputedObservableMap(IObservableSet keySet) {
+		this(keySet, null);
+	}
+
+	/**
+	 * @param keySet
+	 * @param valueType
+	 * @since 1.2
+	 */
+	public ComputedObservableMap(IObservableSet keySet, Object valueType) {
+		super(keySet.getRealm());
+		this.keySet = keySet;
+		this.valueType = valueType;
+
+		keySet.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				ComputedObservableMap.this.dispose();
+			}
+		});
+	}
+
+	/**
+	 * @deprecated Subclasses are no longer required to call this method.
+	 */
+	protected void init() {
+	}
+
+	protected void firstListenerAdded() {
+		getRealm().exec(new Runnable() {
+			public void run() {
+				hookListeners();
+			}
+		});
+	}
+
+	protected void lastListenerRemoved() {
+		unhookListeners();
+	}
+
+	private void hookListeners() {
+		if (keySet != null) {
+			knownKeys = new IdentitySet();
+			keySet.addSetChangeListener(setChangeListener);
+			keySet.addStaleListener(staleListener);
+			for (Iterator it = this.keySet.iterator(); it.hasNext();) {
+				Object key = it.next();
+				hookListener(key);
+				knownKeys.add(key);
+			}
+		}
+	}
+
+	private void unhookListeners() {
+		if (keySet != null) {
+			keySet.removeSetChangeListener(setChangeListener);
+			keySet.removeStaleListener(staleListener);
+		}
+		if (knownKeys != null) {
+			Object[] keys = knownKeys.toArray();
+			for (int i = 0; i < keys.length; i++) {
+				unhookListener(keys[i]);
+			}
+			knownKeys.clear();
+			knownKeys = null;
+		}
+	}
+
+	protected final void fireSingleChange(Object key, Object oldValue,
+			Object newValue) {
+		fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue, newValue));
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getKeyType() {
+		return keySet.getElementType();
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getValueType() {
+		return valueType;
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public Object remove(Object key) {
+		checkRealm();
+
+		Object oldValue = get(key);
+		keySet().remove(key);
+
+		return oldValue;
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public boolean containsKey(Object key) {
+		getterCalled();
+		return keySet().contains(key);
+	}
+
+	public Set entrySet() {
+		return entrySet;
+	}
+
+	public Set keySet() {
+		return keySet;
+	}
+
+	final public Object get(Object key) {
+		getterCalled();
+		if (!keySet.contains(key))
+			return null;
+		return doGet(key);
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	final public Object put(Object key, Object value) {
+		checkRealm();
+		if (!keySet.contains(key))
+			return null;
+		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);
+
+	public boolean isStale() {
+		return super.isStale() || keySet.isStale();
+	}
+
+	public synchronized void dispose() {
+		unhookListeners();
+		entrySet = null;
+		keySet = null;
+		setChangeListener = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/DecoratingObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/DecoratingObservableMap.java
new file mode 100644
index 0000000..c0a1bb7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/DecoratingObservableMap.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * 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 237718)
+ *     Matthew Hall - but 246626, 226289
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.DecoratingObservable;
+
+/**
+ * An observable map which decorates another observable map.
+ * 
+ * @since 1.2
+ */
+public class DecoratingObservableMap extends DecoratingObservable implements
+		IObservableMap {
+	private IObservableMap decorated;
+
+	private IMapChangeListener mapChangeListener;
+
+	/**
+	 * Constructs a DecoratingObservableMap which decorates the given
+	 * observable.
+	 * 
+	 * @param decorated
+	 *            the observable map being decorated
+	 * @param disposeDecoratedOnDispose
+	 */
+	public DecoratingObservableMap(IObservableMap decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated, disposeDecoratedOnDispose);
+		this.decorated = decorated;
+	}
+
+	public synchronized void addMapChangeListener(IMapChangeListener listener) {
+		addListener(MapChangeEvent.TYPE, listener);
+	}
+
+	public synchronized void removeMapChangeListener(IMapChangeListener listener) {
+		removeListener(MapChangeEvent.TYPE, listener);
+	}
+
+	public Object getKeyType() {
+		return decorated.getKeyType();
+	}
+
+	public Object getValueType() {
+		return decorated.getValueType();
+	}
+
+	protected void fireMapChange(MapDiff diff) {
+		// fire general change event first
+		super.fireChange();
+		fireEvent(new MapChangeEvent(this, diff));
+	}
+
+	protected void fireChange() {
+		throw new RuntimeException(
+				"fireChange should not be called, use fireListChange() instead"); //$NON-NLS-1$
+	}
+
+	protected void firstListenerAdded() {
+		if (mapChangeListener == null) {
+			mapChangeListener = new IMapChangeListener() {
+				public void handleMapChange(MapChangeEvent event) {
+					DecoratingObservableMap.this.handleMapChange(event);
+				}
+			};
+		}
+		decorated.addMapChangeListener(mapChangeListener);
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+		if (mapChangeListener != null) {
+			decorated.removeMapChangeListener(mapChangeListener);
+			mapChangeListener = null;
+		}
+	}
+
+	/**
+	 * Called whenever a MapChangeEvent is received from the decorated
+	 * observable. By default, this method fires the map change event again,
+	 * with the decorating observable as the event source. Subclasses may
+	 * override to provide different behavior.
+	 * 
+	 * @param event
+	 *            the change event received from the decorated observable
+	 */
+	protected void handleMapChange(final MapChangeEvent event) {
+		fireMapChange(event.diff);
+	}
+
+	public void clear() {
+		checkRealm();
+		decorated.clear();
+	}
+
+	public boolean containsKey(Object key) {
+		getterCalled();
+		return decorated.containsKey(key);
+	}
+
+	public boolean containsValue(Object value) {
+		getterCalled();
+		return decorated.containsValue(value);
+	}
+
+	private class BackedCollection implements Collection {
+		private Collection collection;
+
+		BackedCollection(Collection set) {
+			this.collection = set;
+		}
+
+		public boolean add(Object o) {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean addAll(Collection arg0) {
+			throw new UnsupportedOperationException();
+		}
+
+		public void clear() {
+			checkRealm();
+			collection.clear();
+		}
+
+		public boolean contains(Object o) {
+			getterCalled();
+			return collection.contains(o);
+		}
+
+		public boolean containsAll(Collection c) {
+			getterCalled();
+			return collection.containsAll(c);
+		}
+
+		public boolean isEmpty() {
+			getterCalled();
+			return collection.isEmpty();
+		}
+
+		public Iterator iterator() {
+			final Iterator iterator = collection.iterator();
+			return new Iterator() {
+				public boolean hasNext() {
+					getterCalled();
+					return iterator.hasNext();
+				}
+
+				public Object next() {
+					getterCalled();
+					return iterator.next();
+				}
+
+				public void remove() {
+					checkRealm();
+					iterator.remove();
+				}
+			};
+		}
+
+		public boolean remove(Object o) {
+			getterCalled();
+			return collection.remove(o);
+		}
+
+		public boolean removeAll(Collection c) {
+			getterCalled();
+			return collection.removeAll(c);
+		}
+
+		public boolean retainAll(Collection c) {
+			getterCalled();
+			return collection.retainAll(c);
+		}
+
+		public int size() {
+			getterCalled();
+			return collection.size();
+		}
+
+		public Object[] toArray() {
+			getterCalled();
+			return collection.toArray();
+		}
+
+		public Object[] toArray(Object[] array) {
+			getterCalled();
+			return collection.toArray(array);
+		}
+
+		public boolean equals(Object obj) {
+			getterCalled();
+			return collection.equals(obj);
+		}
+
+		public int hashCode() {
+			getterCalled();
+			return collection.hashCode();
+		}
+
+		public String toString() {
+			getterCalled();
+			return collection.toString();
+		}
+	}
+
+	private class BackedSet extends BackedCollection implements Set {
+		BackedSet(Set set) {
+			super(set);
+		}
+	}
+
+	Set entrySet = null;
+
+	public Set entrySet() {
+		getterCalled();
+		if (entrySet == null) {
+			entrySet = new BackedSet(decorated.entrySet());
+		}
+		return entrySet;
+	}
+
+	public Object get(Object key) {
+		getterCalled();
+		return decorated.get(key);
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return decorated.isEmpty();
+	}
+
+	Set keySet = null;
+
+	public Set keySet() {
+		getterCalled();
+		if (keySet == null) {
+			keySet = new BackedSet(decorated.keySet());
+		}
+		return keySet;
+	}
+
+	public Object put(Object key, Object value) {
+		checkRealm();
+		return decorated.put(key, value);
+	}
+
+	public void putAll(Map m) {
+		checkRealm();
+		decorated.putAll(m);
+	}
+
+	public Object remove(Object key) {
+		checkRealm();
+		return decorated.remove(key);
+	}
+
+	public int size() {
+		getterCalled();
+		return decorated.size();
+	}
+
+	Collection values;
+
+	public Collection values() {
+		getterCalled();
+		if (values == null) {
+			values = new BackedCollection(decorated.values());
+		}
+		return values;
+	}
+
+	public boolean equals(Object obj) {
+		getterCalled();
+		if (this == obj) {
+			return true;
+		}
+		return decorated.equals(obj);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return decorated.hashCode();
+	}
+
+	public String toString() {
+		getterCalled();
+		return decorated.toString();
+	}
+
+	public synchronized void dispose() {
+		if (decorated != null && mapChangeListener != null) {
+			decorated.removeMapChangeListener(mapChangeListener);
+		}
+		decorated = null;
+		mapChangeListener = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/IMapChangeListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/IMapChangeListener.java
new file mode 100644
index 0000000..3bc829e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/IMapChangeListener.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+import org.eclipse.core.databinding.observable.IObservablesListener;
+
+/**
+ * Listener for changes to observable maps.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface IMapChangeListener extends IObservablesListener {
+
+	/**
+	 * Handle a change an observable map. The given event object must only be
+	 * used locally in this method because it may be reused for other change
+	 * notifications. The diff object referenced by the event is immutable and
+	 * may be used non-locally.
+	 * 
+	 * @param event
+	 *            the event
+	 */
+	void handleMapChange(MapChangeEvent event);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/IObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/IObservableMap.java
new file mode 100644
index 0000000..20c0138
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/IObservableMap.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * 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 237718, 226289
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * Observable Map.
+ * 
+ * @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 extends Map, IObservable {
+	
+	/**
+	 * Returns the element type for the {@link #keySet() keyset} of this
+	 * observable map, or <code>null</code> if the keyset is untyped.
+	 * 
+	 * @return the element type for the {@link #keySet() keyset} of this
+	 *         observable map, or <code>null</code> if the keyset is untyped.
+	 * @since 1.2
+	 */
+	public Object getKeyType();
+
+	/**
+	 * Returns the element type for the {@link #values() values} of this
+	 * observable map, or <code>null</code> if the values collection is untyped.
+	 * 
+	 * @return the element type for the {@link #values() values} of this
+	 *         observable map, or <code>null</code> if the values collection is
+	 *         untyped.
+	 * @since 1.2
+	 */
+	public Object getValueType();
+
+	/**
+	 * @param listener
+	 */
+	public void addMapChangeListener(IMapChangeListener listener);
+
+	/**
+	 * @param listener
+	 */
+	public void removeMapChangeListener(IMapChangeListener listener);
+	
+	/**
+	 * @TrackedGetter
+	 */
+	public int size();
+
+	/**
+	 * @TrackedGetter
+	 */
+	public boolean isEmpty();
+
+	/**
+	 * @TrackedGetter
+	 */
+	public boolean containsKey(Object key);
+
+	/**
+	 * @TrackedGetter
+	 */
+	public boolean 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 boolean equals(Object o);
+
+	/**
+	 * @TrackedGetter
+	 */
+	public int hashCode();
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/MapChangeEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/MapChangeEvent.java
new file mode 100644
index 0000000..90bfef9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/MapChangeEvent.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+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 extends 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 MapChangeEvent(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 (IObservableMap) getSource();
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((IMapChangeListener) listener).handleMapChange(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/MapDiff.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/MapDiff.java
new file mode 100644
index 0000000..d710c13
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/MapDiff.java
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 251884, 194734, 301774
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * @since 1.1
+ * 
+ */
+public abstract class MapDiff implements IDiff {
+	/**
+	 * Returns true if the diff has no added, removed or changed entries.
+	 * 
+	 * @return true if the diff has no added, removed or changed entries.
+	 * @since 1.2
+	 */
+	public boolean isEmpty() {
+		return getAddedKeys().isEmpty() && getRemovedKeys().isEmpty()
+				&& getChangedKeys().isEmpty();
+	}
+
+	/**
+	 * Applies the changes in this diff to the given map
+	 * 
+	 * @param map
+	 *            the map to which the diff will be applied
+	 * @since 1.2
+	 */
+	public void applyTo(Map map) {
+		for (Iterator it = getAddedKeys().iterator(); it.hasNext();) {
+			Object key = it.next();
+			map.put(key, getNewValue(key));
+		}
+		for (Iterator it = getChangedKeys().iterator(); it.hasNext();) {
+			Object key = it.next();
+			map.put(key, getNewValue(key));
+		}
+		for (Iterator it = getRemovedKeys().iterator(); it.hasNext();) {
+			map.remove(it.next());
+		}
+	}
+
+	/**
+	 * Returns a map showing what <code>map</code> would look like if this diff
+	 * were applied to it.
+	 * <p>
+	 * <b>Note</b>: the returned map is only valid until structural changes are
+	 * made to the passed-in map.
+	 * 
+	 * @param map
+	 *            the map over which the diff will be simulated
+	 * @return an unmodifiable map showing what <code>map</code> would look like
+	 *         if it were passed to the {@link #applyTo(Map)} method.
+	 * @see #applyTo(Map)
+	 * @since 1.3
+	 */
+	public Map simulateOn(Map map) {
+		return new DeltaMap(map, this);
+	}
+
+	private static class DeltaMap extends AbstractMap {
+
+		private final Map map;
+		private final MapDiff diff;
+
+		private Set entrySet;
+
+		public DeltaMap(Map map, MapDiff diff) {
+			this.map = map;
+			this.diff = diff;
+
+		}
+
+		public void clear() {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean containsKey(Object key) {
+			return diff.getAddedKeys().contains(key)
+					|| (map.containsKey(key) && !diff.getRemovedKeys()
+							.contains(key));
+		}
+
+		public Set entrySet() {
+			if (entrySet == null) {
+				entrySet = new DeltaMapEntrySet(map, diff);
+			}
+			return entrySet;
+		}
+
+		public Object get(Object key) {
+			if (diff.getAddedKeys().contains(key))
+				return diff.getNewValue(key);
+			if (diff.getChangedKeys().contains(key))
+				return diff.getNewValue(key);
+			if (diff.getRemovedKeys().contains(key))
+				return null;
+			return map.get(key);
+		}
+
+		public Object put(Object arg0, Object arg1) {
+			throw new UnsupportedOperationException();
+		}
+
+		public void putAll(Map arg0) {
+			throw new UnsupportedOperationException();
+		}
+
+		public Object remove(Object key) {
+			throw new UnsupportedOperationException();
+		}
+
+	}
+
+	private static class DeltaMapEntrySet extends AbstractSet {
+
+		private final Map map;
+		private final MapDiff diff;
+
+		public DeltaMapEntrySet(Map map, MapDiff diff) {
+			this.map = map;
+			this.diff = diff;
+		}
+
+		public Iterator iterator() {
+			return new Iterator() {
+				Iterator origEntries = map.entrySet().iterator();
+				Iterator addedKeys = diff.getAddedKeys().iterator();
+
+				boolean haveNext = false;
+				Map.Entry next;
+
+				public boolean hasNext() {
+					return findNext();
+				}
+
+				public Object next() {
+					if (!findNext())
+						throw new NoSuchElementException();
+
+					Map.Entry myNext = next;
+					haveNext = false;
+					next = null;
+					return myNext;
+				}
+
+				private boolean findNext() {
+					if (haveNext)
+						return true;
+					while (true) {
+						Object candidateKey;
+						Map.Entry candidateEntry;
+						if (origEntries.hasNext()) {
+							candidateEntry = (Map.Entry) origEntries.next();
+							candidateKey = candidateEntry.getKey();
+
+							if (diff.getRemovedKeys().contains(candidateKey)) {
+								continue;
+							} else if (diff.getChangedKeys().contains(
+									candidateKey)) {
+								candidateEntry = new DeltaMapEntry(
+										candidateKey, diff);
+							} else {
+								candidateEntry = new MapEntryWrapper(
+										candidateEntry);
+							}
+						} else if (addedKeys.hasNext()) {
+							candidateKey = addedKeys.next();
+							candidateEntry = new DeltaMapEntry(candidateKey,
+									diff);
+						} else {
+							return false;
+						}
+
+						haveNext = true;
+						next = candidateEntry;
+						return true;
+					}
+				}
+
+				public void remove() {
+					throw new UnsupportedOperationException();
+				}
+
+			};
+		}
+
+		public int size() {
+			return map.size() + diff.getAddedKeys().size()
+					- diff.getRemovedKeys().size();
+		}
+
+	}
+
+	private abstract static class AbstractMapEntry implements Map.Entry {
+		public Object setValue(Object arg0) {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean equals(Object obj) {
+			if (!(obj instanceof Map.Entry))
+				return false;
+			Map.Entry that = (Map.Entry) obj;
+			return Util.equals(this.getKey(), that.getKey())
+					&& Util.equals(this.getValue(), that.getValue());
+		}
+
+		public int hashCode() {
+			Object key = getKey();
+			Object value = getValue();
+			return hash(key) ^ hash(value);
+		}
+
+		private int hash(Object key) {
+			return key == null ? 0 : key.hashCode();
+		}
+	}
+
+	private static class MapEntryWrapper extends AbstractMapEntry {
+		private final Entry entry;
+
+		public MapEntryWrapper(Map.Entry entry) {
+			this.entry = entry;
+		}
+
+		public Object getKey() {
+			return entry.getKey();
+		}
+
+		public Object getValue() {
+			return entry.getValue();
+		}
+
+	}
+
+	private static class DeltaMapEntry extends AbstractMapEntry {
+		private final Object key;
+		private final MapDiff diff;
+
+		public DeltaMapEntry(Object key, MapDiff diff) {
+			this.key = key;
+			this.diff = diff;
+		}
+
+		public Object getKey() {
+			return key;
+		}
+
+		public Object getValue() {
+			return diff.getNewValue(key);
+		}
+
+	}
+
+	/**
+	 * @return the set of keys which were added
+	 */
+	public abstract Set getAddedKeys();
+
+	/**
+	 * @return the set of keys which were removed
+	 */
+	public abstract Set getRemovedKeys();
+
+	/**
+	 * @return the set of keys for which the value has changed
+	 */
+	public abstract Set getChangedKeys();
+
+	/**
+	 * Returns the old value for the given key, which must be an element of
+	 * {@link #getRemovedKeys()} or {@link #getChangedKeys()}.
+	 * 
+	 * @param key
+	 * @return the old value for the given key.
+	 */
+	public abstract Object getOldValue(Object key);
+
+	/**
+	 * Returns the new value for the given key, which must be an element of
+	 * {@link #getChangedKeys()} or {@link #getAddedKeys()}.
+	 * 
+	 * @param key
+	 * @return the new value for the given key.
+	 */
+	public abstract Object getNewValue(Object key);
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/ObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/ObservableMap.java
new file mode 100644
index 0000000..9f835a1
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/ObservableMap.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 226289, 274450
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+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 extends AbstractObservable implements IObservableMap {
+
+	protected Map wrappedMap;
+
+	private boolean stale = false;
+
+	/**
+	 * @param wrappedMap
+	 */
+	public ObservableMap(Map wrappedMap) {
+		this(Realm.getDefault(), wrappedMap);
+	}
+
+	/**
+	 * @param realm
+	 * @param wrappedMap
+	 */
+	public ObservableMap(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);
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getKeyType() {
+		return null;
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getValueType() {
+		return null;
+	}
+
+	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 boolean containsKey(Object key) {
+		getterCalled();
+		return wrappedMap.containsKey(key);
+	}
+
+	public boolean 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 boolean 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 boolean 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 boolean is true and this observable set was not already
+	 *            stale.
+	 */
+	public void setStale(boolean stale) {
+		checkRealm();
+		boolean 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 boolean equals(Object o) {
+		getterCalled();
+		return o == this || wrappedMap.equals(o);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return wrappedMap.hashCode();
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/WritableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/WritableMap.java
new file mode 100644
index 0000000..8a8a0d9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/WritableMap.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 184830, 233306, 226289, 190881
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.map;
+
+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.observable.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 extends ObservableMap {
+	private final Object keyType;
+	private final Object valueType;
+
+	/**
+	 * Constructs a new WritableMap on the default realm.
+	 */
+	public WritableMap() {
+		this(Realm.getDefault(), null, null);
+	}
+	
+	/**
+	 * Constructs a new WritableMap on the given realm.
+	 * 
+	 * @param realm
+	 *            the realm
+	 */
+	public WritableMap(Realm realm) {
+		this(realm, null, null);
+	}
+
+	/**
+	 * Constructs a new WritableMap on the default realm with the specified key
+	 * and value types.
+	 * 
+	 * @param keyType
+	 * @param valueType
+	 * @since 1.2
+	 */
+	public WritableMap(Object keyType, Object valueType) {
+		this(Realm.getDefault(), keyType, valueType);
+	}
+
+	/**
+	 * Constructs a new WritableMap on the given realm with the specified key
+	 * and value types.
+	 * 
+	 * @param realm
+	 * @param keyType
+	 * @param valueType
+	 * @since 1.2
+	 */
+	public WritableMap(Realm realm, Object keyType, Object valueType) {
+		super(realm, new HashMap());
+		this.keyType = keyType;
+		this.valueType = valueType;
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getKeyType() {
+		return keyType;
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	public Object getValueType() {
+		return valueType;
+	}
+
+	/**
+	 * 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();
+
+		boolean containedKeyBefore = wrappedMap.containsKey(key);
+		Object result = wrappedMap.put(key, value);
+		boolean containedKeyAfter = wrappedMap.containsKey(key);
+
+		if (containedKeyBefore != containedKeyAfter
+				|| !Util.equals(result, value)) {
+			MapDiff diff;
+			if (containedKeyBefore) {
+				if (containedKeyAfter) {
+					diff = Diffs
+							.createMapDiffSingleChange(key, result, value);
+				} else {
+					diff = Diffs.createMapDiffSingleRemove(key, result);
+				}
+			} else {
+				diff = Diffs.createMapDiffSingleAdd(key, value);
+			}
+			fireMapChange(diff);
+		}
+		return result;
+	}
+
+	/**
+	 * Removes the value with the provide <code>key</code>.  Must be invoked from the current realm.
+	 */
+	public Object remove(Object key) {
+		checkRealm();
+		if (wrappedMap.containsKey(key)) {
+			Object result = wrappedMap.remove(key);
+			fireMapChange(Diffs.createMapDiffSingleRemove(key, result));
+			return result;
+		}
+		return null;
+	}
+
+	/**
+	 * 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 = (Entry) it.next();
+			boolean add = !wrappedMap.containsKey(entry.getKey());
+			Object previousValue = wrappedMap.put(entry.getKey(), entry
+					.getValue());
+			if (add) {
+				addedKeys.add(entry.getKey());
+			} else {
+				changes.put(entry.getKey(), previousValue);
+			}
+		}
+		if (!addedKeys.isEmpty() || !changes.isEmpty()) {
+			fireMapChange(Diffs.createMapDiff(addedKeys, Collections.EMPTY_SET,
+					changes.keySet(), changes, wrappedMap));
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/package.html
new file mode 100644
index 0000000..22107ea
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/map/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/IObservableFactory.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/IObservableFactory.java
new file mode 100644
index 0000000..e113f14
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/IObservableFactory.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.masterdetail;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * Generates an {@link IObservable} when passed a target instance.
+ * 
+ * @since 1.0
+ */
+public interface IObservableFactory {
+
+	/**
+	 * Creates an observable for the given target object.
+	 * 
+	 * @param target
+	 * @return the new observable
+	 */
+	public IObservable createObservable(Object target);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java
new file mode 100644
index 0000000..203a1cf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * 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, 226289
+ *     Ovidio Mallo - bugs 305367
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.masterdetail;
+
+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;
+import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList;
+import org.eclipse.core.internal.databinding.observable.masterdetail.MapDetailValueObservableMap;
+import org.eclipse.core.internal.databinding.observable.masterdetail.SetDetailValueObservableMap;
+
+/**
+ * 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 detailMap(master, detailFactory, null, null);
+	}
+
+	/**
+	 * 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
+	 * @param detailKeyType
+	 *            the element type of the detail observable map's key set,
+	 *            typically of type java.lang.Class and can be <code>null</code>
+	 * @param detailValueType
+	 *            the element type of the detail observable map's values
+	 *            collection, typically of type java.lang.Class and can be
+	 *            <code>null</code>
+	 * @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.2
+	 */
+	public static IObservableMap detailMap(IObservableValue master,
+			IObservableFactory detailFactory, Object detailKeyType,
+			Object detailValueType) {
+		return new DetailObservableMap(detailFactory, master, detailKeyType,
+				detailValueType);
+	}
+
+	/**
+	 * Returns a detail observable list where each element is the detail value
+	 * of the element in the master observable list. The provided factory is
+	 * used to create the detail observable values for every master element
+	 * which then define the elements of the detail list. The detail list
+	 * resides in the same realm as the given master list.
+	 * 
+	 * <p>
+	 * Note that since the values of the returned list are detail values of the
+	 * elements of the master list, the only modifications supported are through
+	 * the {@link IObservableList#set(int, Object)} method. Modifications made
+	 * through the returned list are made through the detail observables created
+	 * by the specified observable factory.
+	 * </p>
+	 * 
+	 * @param masterList
+	 *            The master observable list.
+	 * @param detailFactory
+	 *            The factory for creating {@link IObservableValue} instances
+	 *            for the elements of the master list which then define the
+	 *            elements of the new detail list.
+	 * @param detailType
+	 *            The value type of the detail values, typically of type
+	 *            <code>java.lang.Class</code>. May be <code>null</code>.
+	 * @return A detail observable list with elements which correspond to the
+	 *         detail values of the elements of the master list.
+	 * 
+	 * @since 1.4
+	 */
+	public static IObservableList detailValues(IObservableList masterList,
+			IObservableFactory detailFactory, Object detailType) {
+		return new ListDetailValueObservableList(masterList, detailFactory,
+				detailType);
+	}
+
+	/**
+	 * Returns a detail observable map where the map's key set is the same as
+	 * the given observable set, and where each value is the detail value of the
+	 * element in the master observable set. The provided factory is used to
+	 * create the detail observable values for every master key which then
+	 * define the values of the detail map. The detail map resides in the same
+	 * realm as the given master set.
+	 * 
+	 * <p>
+	 * Note that since the values of the returned map are detail values of the
+	 * elements of the master set, the only modifications supported are through
+	 * the {@link IObservableMap#put(Object, Object)} and
+	 * {@link IObservableMap#putAll(java.util.Map)} methods. Therefore, the
+	 * returned map does not add entries for elements not already contained in
+	 * the master set. Modifications made through the returned detail map are
+	 * made through the detail observables created by the specified observable
+	 * factory.
+	 * </p>
+	 * 
+	 * @param masterSet
+	 *            The master observable set.
+	 * @param detailFactory
+	 *            The factory for creating {@link IObservableValue} instances
+	 *            for the elements of the master set which then define the
+	 *            values of the new detail map.
+	 * @param detailType
+	 *            The value type of the detail values, typically of type
+	 *            <code>java.lang.Class</code>. May be <code>null</code>.
+	 * @return A detail observable map with the given master set as key set and
+	 *         with values which correspond to the detail values of the elements
+	 *         of the master set.
+	 * 
+	 * @since 1.4
+	 */
+	public static IObservableMap detailValues(IObservableSet masterSet,
+			IObservableFactory detailFactory, Object detailType) {
+		return new SetDetailValueObservableMap(masterSet, detailFactory,
+				detailType);
+	}
+
+	/**
+	 * Returns a detail observable map where the map's key set is the same as
+	 * the one of the given master observable map, and where each value is the
+	 * detail value of the corresponding value in the master observable map. The
+	 * provided factory is used to create the detail observable values for every
+	 * master value which then define the values of the detail map. The detail
+	 * map resides in the same realm as the given master map.
+	 * 
+	 * <p>
+	 * Note that since the values of the returned map are detail values of the
+	 * values of the master map, the only modifications supported are through
+	 * the {@link IObservableMap#put(Object, Object)} and
+	 * {@link IObservableMap#putAll(java.util.Map)} methods. Therefore, the
+	 * returned map does not add entries for keys not already contained in the
+	 * master map's key set. Modifications made through the returned detail map
+	 * are made through the detail observables created by the specified
+	 * observable factory.
+	 * </p>
+	 * 
+	 * @param masterMap
+	 *            The master observable map.
+	 * @param detailFactory
+	 *            The factory for creating {@link IObservableValue} instances
+	 *            for the values of the master map which then define the values
+	 *            of the new detail map.
+	 * @param detailType
+	 *            The value type of the detail values, typically of type
+	 *            <code>java.lang.Class</code>. May be <code>null</code>.
+	 * @return A detail observable map with the same key set as the given master
+	 *         observable map and with values which correspond to the detail
+	 *         values of the values of the master map.
+	 * 
+	 * @since 1.4
+	 */
+	public static IObservableMap detailValues(IObservableMap masterMap,
+			IObservableFactory detailFactory, Object detailType) {
+		return new MapDetailValueObservableMap(masterMap, detailFactory,
+				detailType);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/package.html
new file mode 100644
index 0000000..07b72e5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/package.html
new file mode 100644
index 0000000..0739c1c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/AbstractObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/AbstractObservableSet.java
new file mode 100644
index 0000000..c446b3f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/AbstractObservableSet.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 194734
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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 AbstractObservableSet extends AbstractObservable implements
+		IObservableSet {
+
+	private boolean stale = false;
+
+	protected AbstractObservableSet() {
+		this(Realm.getDefault());
+	}
+	
+	protected void firstListenerAdded() {
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+	}
+	
+	protected AbstractObservableSet(Realm realm) {
+		super(realm);
+	}
+	
+	public synchronized void addSetChangeListener(ISetChangeListener listener) {
+		addListener(SetChangeEvent.TYPE, listener);
+	}
+
+	public synchronized void removeSetChangeListener(ISetChangeListener listener) {
+		removeListener(SetChangeEvent.TYPE, listener);
+	}
+
+	protected abstract Set getWrappedSet();
+	
+	protected void fireSetChange(SetDiff diff) {
+		// fire general change event first
+		super.fireChange();
+
+		fireEvent(new SetChangeEvent(this, diff));
+	}
+	
+	public boolean contains(Object o) {
+		getterCalled();
+		return getWrappedSet().contains(o);
+	}
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return getWrappedSet().containsAll(c);
+	}
+
+	public boolean equals(Object o) {
+		getterCalled();
+		return getWrappedSet().equals(o);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return getWrappedSet().hashCode();
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return getWrappedSet().isEmpty();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator wrappedIterator = getWrappedSet().iterator();
+		return new Iterator() {
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean hasNext() {
+				ObservableTracker.getterCalled(AbstractObservableSet.this);
+				return wrappedIterator.hasNext();
+			}
+
+			public Object next() {
+				ObservableTracker.getterCalled(AbstractObservableSet.this);
+				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 boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @return Returns the stale state.
+	 */
+	public boolean isStale() {
+		getterCalled();
+		return stale;
+	}
+
+	/**
+	 * @param stale
+	 *            The stale state to set. This will fire a stale event if the
+	 *            given boolean is true and this observable set was not already
+	 *            stale.
+	 */
+	public void setStale(boolean stale) {
+		checkRealm();
+		boolean 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$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java
new file mode 100644
index 0000000..827dc65
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 237703)
+ *     Matthew Hall - bug 274081
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.set;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+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;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * A lazily calculated set that automatically computes and registers listeners
+ * on its dependencies as long as all of its dependencies are
+ * {@link IObservable} objects. Any change to one of the observable dependencies
+ * causes the set to be recomputed.
+ * <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>
+ * <p>
+ * Example: compute the set of all primes greater than 1 and less than the value
+ * of an {@link IObservableValue} &lt; {@link Integer} &gt;.
+ * </p>
+ * 
+ * <pre>
+ * final IObservableValue max = WritableValue.withValueType(Integer.TYPE);
+ * max.setValue(new Integer(0));
+ * IObservableSet primes = new ComputedSet() {
+ * 	protected Set calculate() {
+ * 		int maxVal = ((Integer) max.getValue()).intValue();
+ * 
+ * 		Set result = new HashSet();
+ * 		outer: for (int i = 2; i &lt; maxVal; i++) {
+ * 			for (Iterator it = result.iterator(); it.hasNext();) {
+ * 				Integer knownPrime = (Integer) it.next();
+ * 				if (i % knownPrime.intValue() == 0)
+ * 					continue outer;
+ * 			}
+ * 			result.add(new Integer(i));
+ * 		}
+ * 		return result;
+ * 	}
+ * };
+ * 
+ * System.out.println(primes); // =&gt; &quot;[]&quot;
+ * 
+ * max.setValue(new Integer(20));
+ * System.out.println(primes); // =&gt; &quot;[2, 3, 5, 7, 11, 13, 17, 19]&quot;
+ * </pre>
+ * 
+ * @since 1.2
+ */
+public abstract class ComputedSet extends AbstractObservableSet {
+	private Set cachedSet = new HashSet();
+
+	private boolean dirty = true;
+	private boolean stale = false;
+
+	private IObservable[] dependencies = new IObservable[0];
+
+	/**
+	 * Creates a computed set in the default realm and with an unknown (null)
+	 * element type.
+	 */
+	public ComputedSet() {
+		this(Realm.getDefault(), null);
+	}
+
+	/**
+	 * Creates a computed set 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 ComputedSet(Object elementType) {
+		this(Realm.getDefault(), elementType);
+	}
+
+	/**
+	 * Creates a computed set in given realm and with an unknown (null) element
+	 * type.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * 
+	 */
+	public ComputedSet(Realm realm) {
+		this(realm, null);
+	}
+
+	/**
+	 * Creates a computed set 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 ComputedSet(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 cachedSet.
+	 * </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 implements Runnable, IChangeListener,
+			IStaleListener {
+		public void run() {
+			cachedSet = calculate();
+			if (cachedSet == null)
+				cachedSet = Collections.EMPTY_SET;
+		}
+
+		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 doGetSet().size();
+	}
+
+	private final Set getSet() {
+		getterCalled();
+		return doGetSet();
+	}
+
+	protected Set getWrappedSet() {
+		return doGetSet();
+	}
+
+	final Set doGetSet() {
+		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 set 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 cachedSet;
+	}
+
+	/**
+	 * Subclasses must override this method to calculate the set contents. Any
+	 * dependencies used to calculate the set must be {@link IObservable}, and
+	 * implementers must use one of the interface methods tagged TrackedGetter
+	 * for ComputedSet to recognize it as a dependency.
+	 * 
+	 * @return the object's set.
+	 */
+	protected abstract Set calculate();
+
+	private void makeDirty() {
+		if (!dirty) {
+			dirty = true;
+
+			makeStale();
+
+			stopListening();
+
+			// copy the old set
+			final Set oldSet = new HashSet(cachedSet);
+			// Fire the "dirty" event. This implementation recomputes the new
+			// set lazily.
+			fireSetChange(new SetDiff() {
+				SetDiff delegate;
+
+				private SetDiff getDelegate() {
+					if (delegate == null)
+						delegate = Diffs.computeSetDiff(oldSet, getSet());
+					return delegate;
+				}
+
+				public Set getAdditions() {
+					return getDelegate().getAdditions();
+				}
+
+				public Set getRemovals() {
+					return getDelegate().getRemovals();
+				}
+			});
+		}
+	}
+
+	private void stopListening() {
+		if (dependencies != 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 boolean isStale() {
+		// recalculate set if dirty, to ensure staleness is correct.
+		getSet();
+		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
+		computeSetForListeners();
+	}
+
+	public synchronized void addSetChangeListener(ISetChangeListener listener) {
+		super.addSetChangeListener(listener);
+		// If somebody is listening, we need to make sure we attach our own
+		// listeners
+		computeSetForListeners();
+	}
+
+	private void computeSetForListeners() {
+		// 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 Runnable() {
+			public void run() {
+				if (dependencies == 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.
+					getSet();
+				}
+			}
+		});
+	}
+
+	public synchronized void dispose() {
+		stopListening();
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/DecoratingObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/DecoratingObservableSet.java
new file mode 100644
index 0000000..38092bb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/DecoratingObservableSet.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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 237718)
+ *     Matthew Hall - bug 246626
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+import org.eclipse.core.databinding.observable.DecoratingObservableCollection;
+
+/**
+ * An observable set which decorates another observable set.
+ * 
+ * @since 1.2
+ */
+public class DecoratingObservableSet extends DecoratingObservableCollection
+		implements IObservableSet {
+
+	private IObservableSet decorated;
+
+	private ISetChangeListener setChangeListener;
+
+	/**
+	 * Constructs a DecoratingObservableSet which decorates the given
+	 * observable.
+	 * 
+	 * @param decorated
+	 *            the observable set being decorated
+	 * @param disposeDecoratedOnDispose
+	 */
+	public DecoratingObservableSet(IObservableSet decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated, disposeDecoratedOnDispose);
+		this.decorated = decorated;
+	}
+
+	public void clear() {
+		getterCalled();
+		decorated.clear();
+	}
+
+	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));
+	}
+
+	protected void fireChange() {
+		throw new RuntimeException(
+				"fireChange should not be called, use fireSetChange() instead"); //$NON-NLS-1$
+	}
+
+	protected void firstListenerAdded() {
+		if (setChangeListener == null) {
+			setChangeListener = new ISetChangeListener() {
+				public void handleSetChange(SetChangeEvent event) {
+					DecoratingObservableSet.this.handleSetChange(event);
+				}
+			};
+		}
+		decorated.addSetChangeListener(setChangeListener);
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+		if (setChangeListener != null) {
+			decorated.removeSetChangeListener(setChangeListener);
+			setChangeListener = null;
+		}
+	}
+
+	/**
+	 * Called whenever a SetChangeEvent is received from the decorated
+	 * observable. By default, this method fires the set change event again,
+	 * with the decorating observable as the event source. Subclasses may
+	 * override to provide different behavior.
+	 * 
+	 * @param event
+	 *            the change event received from the decorated observable
+	 */
+	protected void handleSetChange(final SetChangeEvent event) {
+		fireSetChange(event.diff);
+	}
+
+	public synchronized void dispose() {
+		if (decorated != null && setChangeListener != null) {
+			decorated.removeSetChangeListener(setChangeListener);
+		}
+		decorated = null;
+		setChangeListener = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/IObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/IObservableSet.java
new file mode 100644
index 0000000..358cb0c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/IObservableSet.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * 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 237718
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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.
+ * 
+ * @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 extends 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
+	 */
+	boolean isEmpty();
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean contains(Object o);
+
+	/**
+	 * @TrackedGetter
+	 */
+	Iterator iterator();
+
+	/**
+	 * @TrackedGetter
+	 */
+	Object[] toArray();
+
+	/**
+	 * @TrackedGetter
+	 */
+	Object[] toArray(Object a[]);
+
+	// Modification Operations
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean add(Object o);
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean remove(Object o);
+
+	// Bulk Operations
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean containsAll(Collection c);
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean addAll(Collection c);
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean retainAll(Collection c);
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean removeAll(Collection c);
+
+	// Comparison and hashing
+
+	/**
+	 * @TrackedGetter
+	 */
+	boolean equals(Object o);
+
+	/**
+	 * @TrackedGetter
+	 */
+	int hashCode();
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ISetChangeListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ISetChangeListener.java
new file mode 100644
index 0000000..51c429a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ISetChangeListener.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+import org.eclipse.core.databinding.observable.IObservablesListener;
+
+/**
+ * Listener for changes to observable sets.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface ISetChangeListener extends IObservablesListener {
+
+	/**
+	 * Handle a change to an observable set. The given event object must only be
+	 * used locally in this method because it may be reused for other change
+	 * notifications. The diff object referenced by the event is immutable and
+	 * may be used non-locally.
+	 * 
+	 * @param event
+	 *            the event
+	 */
+	void handleSetChange(SetChangeEvent event);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ListToSetAdapter.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ListToSetAdapter.java
new file mode 100644
index 0000000..e145fb3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ListToSetAdapter.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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 extends ObservableSet {
+
+	private final IObservableList list;
+
+	private IListChangeListener listener = new 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 ListToSetAdapter(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 != null && listener != null) {
+			list.removeListChangeListener(listener);
+			listener = null;
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/MappedSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/MappedSet.java
new file mode 100644
index 0000000..9f03e26
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/MappedSet.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 263693
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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
+ * 
+ * @deprecated This class is deprecated.
+ */
+public class MappedSet extends ObservableSet {
+
+	private final IObservableMap wrappedMap;
+
+	/*
+	 * Map from values (range elements) to Integer ref counts
+	 */
+	private Map valueCounts = new HashMap();
+
+	private ISetChangeListener domainListener = new 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 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 MappedSet(IObservableSet input, IObservableMap map) {
+		super(input.getRealm(), Collections.EMPTY_SET, Object.class);
+		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 boolean handleAddition(Object mapValue) {
+		Integer count = (Integer) valueCounts.get(mapValue);
+		if (count == 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 boolean handleRemoval(Object mapValue) {
+		Integer count = (Integer) valueCounts.get(mapValue);
+		if (count.intValue() <= 1) {
+			valueCounts.remove(mapValue);
+			return true;
+		}
+		valueCounts.put(mapValue, new Integer(count.intValue() - 1));
+		return false;
+	}
+
+	public synchronized void dispose() {
+		wrappedMap.removeMapChangeListener(mapChangeListener);
+		input.removeSetChangeListener(domainListener);
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ObservableSet.java
new file mode 100644
index 0000000..4ee5500
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ObservableSet.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208332, 274450
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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 extends AbstractObservable implements
+		IObservableSet {
+
+	protected Set wrappedSet;
+
+	private boolean stale = false;
+
+	protected Object elementType;
+
+	protected ObservableSet(Set wrappedSet, Object elementType) {
+		this(Realm.getDefault(), wrappedSet, elementType);
+	}
+
+	protected ObservableSet(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 boolean contains(Object o) {
+		getterCalled();
+		return wrappedSet.contains(o);
+	}
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return wrappedSet.containsAll(c);
+	}
+
+	public boolean equals(Object o) {
+		getterCalled();
+		return o == this || wrappedSet.equals(o);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return wrappedSet.hashCode();
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return wrappedSet.isEmpty();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator wrappedIterator = wrappedSet.iterator();
+		return new Iterator() {
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean hasNext() {
+				ObservableTracker.getterCalled(ObservableSet.this);
+				return wrappedIterator.hasNext();
+			}
+
+			public Object next() {
+				ObservableTracker.getterCalled(ObservableSet.this);
+				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 boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @return Returns the stale state.
+	 */
+	public boolean isStale() {
+		getterCalled();
+		return stale;
+	}
+
+	/**
+	 * @param stale
+	 *            The stale state to set. This will fire a stale event if the
+	 *            given boolean is true and this observable set was not already
+	 *            stale.
+	 */
+	public void setStale(boolean stale) {
+		checkRealm();
+		boolean 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$
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/SetChangeEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/SetChangeEvent.java
new file mode 100644
index 0000000..4873885
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/SetChangeEvent.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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 extends 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 SetChangeEvent(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 (IObservableSet) getSource();
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((ISetChangeListener) listener).handleSetChange(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/SetDiff.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/SetDiff.java
new file mode 100644
index 0000000..9b93970
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/SetDiff.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 251884, 194734, 301774
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.IDiff;
+
+/**
+ * @since 1.0
+ * 
+ */
+public abstract class SetDiff implements IDiff {
+
+	/**
+	 * @return the set of added elements
+	 */
+	public abstract Set getAdditions();
+
+	/**
+	 * @return the set of removed elements
+	 */
+	public abstract Set getRemovals();
+
+	/**
+	 * Returns true if the diff has no added or removed elements.
+	 * 
+	 * @return true if the diff has no added or removed elements.
+	 * @since 1.2
+	 */
+	public boolean isEmpty() {
+		return getAdditions().isEmpty() && getRemovals().isEmpty();
+	}
+
+	/**
+	 * Applies the changes in this diff to the given set
+	 * 
+	 * @param set
+	 *            the set to which the diff will be applied
+	 * @since 1.2
+	 */
+	public void applyTo(Set set) {
+		set.addAll(getAdditions());
+		set.removeAll(getRemovals());
+	}
+
+	/**
+	 * Returns a {@link Set} showing what <code>set</code> would look like if
+	 * this diff were applied to it. The passed-in list is presumed to contain
+	 * all elements in {@link #getRemovals()}, and none of the elements in
+	 * {@link #getAdditions()}.
+	 * <p>
+	 * <b>Note</b>:the returned list is only guaranteed to be valid while the
+	 * passed in set remains unchanged.
+	 * 
+	 * @param set
+	 *            the set over which the diff will be simulated
+	 * @return a {@link Set} showing what <code>set</code> would look like if it
+	 *         were passed to the {@link #applyTo(Set)} method.
+	 * @since 1.3
+	 */
+	public Set simulateOn(Set set) {
+		return new DeltaSet(set, this);
+	}
+
+	private static class DeltaSet extends AbstractSet {
+		private Set original;
+		private final SetDiff diff;
+
+		public DeltaSet(Set original, SetDiff diff) {
+			this.original = original;
+			this.diff = diff;
+		}
+
+		public Iterator iterator() {
+			return new Iterator() {
+				Iterator orig = original.iterator();
+				Iterator add = diff.getAdditions().iterator();
+
+				boolean haveNext = false;
+				Object next;
+
+				public boolean hasNext() {
+					return findNext();
+				}
+
+				public Object next() {
+					if (!findNext())
+						throw new NoSuchElementException();
+					Object myNext = next;
+					haveNext = false;
+					next = null;
+					return myNext;
+				}
+
+				private boolean findNext() {
+					if (haveNext)
+						return true;
+					while (true) {
+						Object candidate;
+						if (orig.hasNext())
+							candidate = orig.next();
+						else if (add.hasNext())
+							candidate = add.next();
+						else
+							return false;
+
+						if (diff.getRemovals().contains(candidate))
+							continue;
+
+						haveNext = true;
+						next = candidate;
+						return true;
+					}
+				}
+
+				public void remove() {
+					throw new UnsupportedOperationException();
+				}
+			};
+		}
+
+		public boolean contains(Object o) {
+			return (original.contains(o) || diff.getAdditions().contains(o))
+					&& !diff.getRemovals().contains(o);
+		}
+
+		public int size() {
+			return original.size() + diff.getAdditions().size()
+					- diff.getRemovals().size();
+		}
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		StringBuffer buffer = new StringBuffer();
+		buffer.append(getClass().getName()).append("{additions [") //$NON-NLS-1$
+				.append(
+						getAdditions() != null ? getAdditions().toString()
+								: "null") //$NON-NLS-1$
+				.append("], removals [") //$NON-NLS-1$
+				.append(
+						getRemovals() != null ? getRemovals().toString()
+								: "null") //$NON-NLS-1$
+				.append("]}"); //$NON-NLS-1$
+
+		return buffer.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/UnionSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/UnionSet.java
new file mode 100644
index 0000000..533a921
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/UnionSet.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208332, 265727
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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 extends ObservableSet {
+
+	/**
+	 * child sets
+	 */
+	private IObservableSet[] childSets;
+
+	private boolean 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 UnionSet(IObservableSet[] childSets) {
+		this(childSets, childSets[0].getElementType());
+	}
+
+	/**
+	 * @param childSets
+	 * @param elementType
+	 * @since 1.2
+	 */
+	public UnionSet(IObservableSet[] childSets, Object elementType) {
+		super(childSets[0].getRealm(), null, elementType);
+		System.arraycopy(childSets, 0,
+				this.childSets = new IObservableSet[childSets.length], 0,
+				childSets.length);
+		this.stalenessTracker = new StalenessTracker(childSets,
+				stalenessConsumer);
+	}
+
+	private ISetChangeListener childSetChangeListener = new ISetChangeListener() {
+		public void handleSetChange(SetChangeEvent event) {
+			processAddsAndRemoves(event.diff.getAdditions(), event.diff
+					.getRemovals());
+		}
+	};
+
+	private IStalenessConsumer stalenessConsumer = new IStalenessConsumer() {
+		public void setStale(boolean stale) {
+			boolean oldStale = UnionSet.this.stale;
+			UnionSet.this.stale = stale;
+			if (stale && !oldStale) {
+				fireStale();
+			}
+		}
+	};
+
+	public boolean isStale() {
+		getterCalled();
+		if (refCounts != 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 = (Integer) refCounts.get(added);
+			if (refCount == 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 = (Integer) refCounts.get(removed);
+			if (refCount != 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 = (Integer) refCounts.get(next);
+			if (refCount == 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 == null) {
+			// no listeners, recompute
+			setWrappedSet(computeElements());
+		}
+	}
+
+	private Set computeElements() {
+		// If there is no cached value, compute the union from scratch
+		if (refCounts == null) {
+			Set result = new HashSet();
+			for (int i = 0; i < childSets.length; i++) {
+				result.addAll(childSets[i]);
+			}
+			return result;
+		}
+
+		// Else there is a cached value. Return it.
+		return refCounts.keySet();
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java
new file mode 100644
index 0000000..52ded9d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/WritableSet.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.set;
+
+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 extends ObservableSet {
+
+	/**
+	 * Constructs a new empty instance in the default realm with a
+	 * <code>null</code> element type.
+	 * 
+	 */
+	public WritableSet() {
+		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 WritableSet(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 WritableSet(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 WritableSet(Realm realm, Collection c, Object elementType) {
+		super(realm, new HashSet(c), elementType);
+		this.elementType = elementType;
+	}
+
+	public boolean add(Object o) {
+		getterCalled();
+		boolean added = wrappedSet.add(o);
+		if (added) {
+			fireSetChange(Diffs.createSetDiff(Collections.singleton(o), Collections.EMPTY_SET));
+		}
+		return added;
+	}
+
+	public boolean 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 boolean remove(Object o) {
+		getterCalled();
+		boolean removed = wrappedSet.remove(o);
+		if (removed) {
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, Collections
+					.singleton(o)));
+		}
+		return removed;
+	}
+
+	public boolean 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 boolean 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);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/package.html
new file mode 100644
index 0000000..5a57448
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/AbstractObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/AbstractObservableValue.java
new file mode 100644
index 0000000..512dc3b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/AbstractObservableValue.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208332, 263691
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+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 extends AbstractObservable implements IObservableValue {
+	/**
+	 * Constructs a new instance with the default realm.
+	 */
+	public AbstractObservableValue() {
+		this(Realm.getDefault());
+	}
+
+	/**
+	 * @param realm
+	 */
+	public AbstractObservableValue(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 boolean 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$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/AbstractVetoableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/AbstractVetoableValue.java
new file mode 100644
index 0000000..1e99e1c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/AbstractVetoableValue.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 263691
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.internal.databinding.observable.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 extends AbstractObservableValue
+		implements IVetoableValue {
+
+	/**
+	 * Creates a new vetoable value.
+	 */
+	public AbstractVetoableValue() {
+		this(Realm.getDefault());
+	}
+
+	/**
+	 * @param realm
+	 */
+	public AbstractVetoableValue(Realm realm) {
+		super(realm);
+	}
+
+	final protected void doSetValue(Object value) {
+		Object currentValue = doGetValue();
+		ValueDiff diff = Diffs.createValueDiff(currentValue, value);
+		boolean 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 boolean fireValueChanging(ValueDiff diff) {
+		checkRealm();
+
+		ValueChangingEvent event = new ValueChangingEvent(this, diff);
+		fireEvent(event);
+		return !event.veto;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ChangeVetoException.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ChangeVetoException.java
new file mode 100644
index 0000000..3c9261f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ChangeVetoException.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.value;
+
+/**
+ * @since 1.0
+ *
+ */
+public class ChangeVetoException extends RuntimeException {
+	
+	/**
+	 * @param string
+	 */
+	public ChangeVetoException(String string) {
+		super(string);
+	}
+
+	private static final long serialVersionUID = 1L;
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java
new file mode 100644
index 0000000..cbe909e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 116920, 147515
+ *     Matthew Hall - bug 274081
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.value;
+
+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;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+
+/**
+ * A Lazily calculated value that automatically computes and registers listeners
+ * on its dependencies as long as all of its dependencies are
+ * {@link IObservable} objects. Any change to one of the observable dependencies
+ * causes the value to be recomputed.
+ * <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>
+ * <p>
+ * Example: compute the sum of all elements in an {@link IObservableList} &lt;
+ * {@link Integer} &gt;.
+ * </p>
+ * 
+ * <pre>
+ * final IObservableList addends = WritableValue.withValueType(Integer.TYPE);
+ * addends.add(new Integer(0));
+ * addends.add(new Integer(1));
+ * addends.add(new Integer(2));
+ * 
+ * IObservableValue sum = new ComputedValue() {
+ * 	protected Object calculate() {
+ * 		int sum = 0;
+ * 		for (Iterator it = addends.iterator(); it.hasNext();) {
+ * 			Integer addend = (Integer) it.next();
+ * 			sum += addend.intValue();
+ * 		}
+ * 		return sum;
+ * 	}
+ * };
+ * 
+ * System.out.println(sum.getValue()); // =&gt; 3
+ * 
+ * addends.add(new Integer(10));
+ * System.out.println(sum.getValue()); // =&gt; 13
+ * </pre>
+ * 
+ * @since 1.0
+ */
+public abstract class ComputedValue extends AbstractObservableValue {
+
+	private boolean dirty = true;
+
+	private boolean 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 ComputedValue() {
+		this(Realm.getDefault(), null);
+	}
+
+	/**
+	 * @param valueType
+	 *            can be <code>null</code>
+	 */
+	public ComputedValue(Object valueType) {
+		this(Realm.getDefault(), valueType);
+	}
+
+	/**
+	 * @param realm
+	 * 
+	 */
+	public ComputedValue(Realm realm) {
+		this(realm, null);
+	}
+
+	/**
+	 * @param realm
+	 * @param valueType
+	 */
+	public ComputedValue(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 implements 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. Any
+	 * dependencies used to calculate the value must be {@link IObservable}, and
+	 * implementers must use one of the interface methods tagged TrackedGetter
+	 * for ComputedValue to recognize it as a dependency.
+	 * 
+	 * @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 ValueDiff() {
+
+				public Object getOldValue() {
+					return oldValue;
+				}
+
+				public Object getNewValue() {
+					return getValue();
+				}
+			});
+		}
+	}
+
+	/**
+	 * 
+	 */
+	private void stopListening() {
+		// Stop listening for dependency changes.
+		if (dependencies != null) {
+			for (int i = 0; i < dependencies.length; i++) {
+				IObservable observable = dependencies[i];
+
+				observable.removeChangeListener(privateInterface);
+				observable.removeStaleListener(privateInterface);
+			}
+			dependencies = null;
+		}
+	}
+
+	public boolean 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 boolean 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 Runnable() {
+			public void run() {
+				if (dependencies == null) {
+					// We are not currently listening.
+					if (hasListeners()) {
+						// But someone is listening for changes. Call getValue()
+						// to make sure we start listening to the observables we
+						// depend on.
+						getValue();
+					}
+				}
+			}
+		});
+	}
+
+	public synchronized void addValueChangeListener(
+			IValueChangeListener listener) {
+		super.addValueChangeListener(listener);
+		// If somebody is listening, we need to make sure we attach our own
+		// listeners
+		computeValueForListeners();
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+		stopListening();
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DateAndTimeObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DateAndTimeObservableValue.java
new file mode 100644
index 0000000..b2d0d3f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DateAndTimeObservableValue.java
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 169876)
+ *     Elias Volanakis <elias@eclipsesource.com> - bug 271720
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+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.internal.databinding.observable.Util;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * An {@link IObservableValue} &lt; {@link java.util.Date} &gt; which supports
+ * scenarios where the date and time are presented as separate elements in the
+ * user interface. This class combines the year, month, and day portion of the
+ * date observable (an {@link IObservableValue} &lt; {@link java.util.Date}
+ * &gt;) and the hour, minute, second, and millisecond portion of the time
+ * observable (also an {@link IObservableValue} &lt; {@link java.util.Date}
+ * &gt;).
+ * <p>
+ * This observable's value will be null whenever the date observable's value is
+ * null. Otherwise the value is the combination of the date portion of the date
+ * observable and the time portion of the time observable (a time observable
+ * value of null is treated the same as 0:00:00.000).
+ * <p>
+ * When setting the value of this observable, setting a null value will set null
+ * on the date observable, and set a time of 0:00:00.000 on the time observable.
+ * When setting non-null values, the non-applicable fields of each observable
+ * are left intact. That is, the hour, minute, second and millisecond components
+ * of the date observable are preserved, and the year, month and day components
+ * of the time observable are preserved.
+ * <p>
+ * The observables used for the date and time component may impose their own
+ * restrictions with regard to supported values. For example some observables do
+ * not allow a null value, because the underlying widget lacks support for a
+ * null value (example: DateTime).
+ * <p>
+ * One use for this class is binding a date-and-time value to two separate user
+ * interface elements, one for editing date and one for editing time:
+ * 
+ * <pre>
+ * DataBindingContext dbc = new DataBindingContext();
+ * IObservableValue beanValue = BeansObservables.observeValue(...);
+ * IObservableValue dateObservable = WidgetProperties.selection().observe(
+ * 		dateWidget);
+ * IObservableValue timeObservable = WidgetProperties.selection().observe(
+ * 		timeWidget);
+ * dbc.bindValue(new DateAndTimeObservableValue(dateObservable, timeObservable),
+ * 		beanValue);
+ * </pre>
+ * 
+ * A second use is editing only the date or time value of a date-and-time value.
+ * This can be accomplished by using a widget-specific observable for the
+ * editable value and a WritableValue as a container for the fixed value. The
+ * example below allows editing the date while preserving the time:
+ * 
+ * <pre>
+ * DataBindingContext dbc = new DataBindingContext();
+ * IObservableValue beanValue = BeansObservables.observeValue(...);
+ * IObservableValue dateObservable = WidgetProperties.selection().observe(
+ * 		dateWidget);
+ * IObservableValue timeObservable = new WritableValue(dateObservable.getRealm(),
+ * 		beanValue.getValue(), Date.class);
+ * dbc.bindValue(new DateAndTimeObservableValue(dateObservable, timeObservable), beanValue);
+ * 
+ * <pre>
+ * 
+ * @since 1.2
+ */
+public class DateAndTimeObservableValue extends AbstractObservableValue {
+	private IObservableValue dateObservable;
+	private IObservableValue timeObservable;
+	private PrivateInterface privateInterface;
+	private Object cachedValue;
+	private boolean updating;
+
+	private class PrivateInterface implements IChangeListener, IStaleListener,
+			IDisposeListener {
+		public void handleDispose(DisposeEvent staleEvent) {
+			dispose();
+		}
+
+		public void handleChange(ChangeEvent event) {
+			if (!isDisposed() && !updating)
+				notifyIfChanged();
+		}
+
+		public void handleStale(StaleEvent staleEvent) {
+			if (!isDisposed())
+				fireStale();
+		}
+	}
+
+	// One calendar per thread to preserve thread-safety
+	private static final ThreadLocal calendar = new ThreadLocal() {
+		protected Object initialValue() {
+			return Calendar.getInstance();
+		}
+	};
+
+	/**
+	 * Constructs a DateAndTimeObservableValue with the specified constituent
+	 * observables.
+	 * 
+	 * @param dateObservable
+	 *            the observable used for the date component (year, month and
+	 *            day) of the constructed observable.
+	 * @param timeObservable
+	 *            the observable used for the time component (hour, minute,
+	 *            second and millisecond) of the constructed observable.
+	 */
+	public DateAndTimeObservableValue(IObservableValue dateObservable,
+			IObservableValue timeObservable) {
+		super(dateObservable.getRealm());
+		this.dateObservable = dateObservable;
+		this.timeObservable = timeObservable;
+
+		Assert.isTrue(dateObservable.getRealm().equals(
+				timeObservable.getRealm()));
+
+		privateInterface = new PrivateInterface();
+		dateObservable.addDisposeListener(privateInterface);
+		timeObservable.addDisposeListener(privateInterface);
+	}
+
+	public Object getValueType() {
+		return Date.class;
+	}
+
+	protected void firstListenerAdded() {
+		cachedValue = doGetValue();
+
+		dateObservable.addChangeListener(privateInterface);
+		dateObservable.addStaleListener(privateInterface);
+
+		timeObservable.addChangeListener(privateInterface);
+		timeObservable.addStaleListener(privateInterface);
+	}
+
+	protected void lastListenerRemoved() {
+		if (dateObservable != null && !dateObservable.isDisposed()) {
+			dateObservable.removeChangeListener(privateInterface);
+			dateObservable.removeStaleListener(privateInterface);
+		}
+
+		if (timeObservable != null && !timeObservable.isDisposed()) {
+			timeObservable.removeChangeListener(privateInterface);
+			timeObservable.removeStaleListener(privateInterface);
+		}
+
+		cachedValue = null;
+	}
+
+	private void notifyIfChanged() {
+		if (hasListeners()) {
+			Object oldValue = cachedValue;
+			Object newValue = cachedValue = doGetValue();
+			if (!Util.equals(oldValue, newValue))
+				fireValueChange(Diffs.createValueDiff(oldValue, newValue));
+		}
+	}
+
+	protected Object doGetValue() {
+		Date dateValue = (Date) dateObservable.getValue();
+		if (dateValue == null)
+			return null;
+
+		Date timeValue = (Date) timeObservable.getValue();
+
+		Calendar cal = (Calendar) calendar.get();
+
+		cal.setTime(dateValue);
+		int year = cal.get(Calendar.YEAR);
+		int month = cal.get(Calendar.MONTH);
+		int day = cal.get(Calendar.DAY_OF_MONTH);
+
+		if (timeValue == null)
+			cal.clear();
+		else
+			cal.setTime(timeValue);
+		int hour = cal.get(Calendar.HOUR_OF_DAY);
+		int minute = cal.get(Calendar.MINUTE);
+		int second = cal.get(Calendar.SECOND);
+		int millis = cal.get(Calendar.MILLISECOND);
+
+		cal.set(year, month, day, hour, minute, second);
+		cal.set(Calendar.MILLISECOND, millis);
+
+		return cal.getTime();
+	}
+
+	protected void doSetValue(Object value) {
+		Date date = (Date) value;
+
+		Date dateValue;
+		Date timeValue;
+
+		Calendar cal = (Calendar) calendar.get();
+		if (date == null)
+			cal.clear();
+		else
+			cal.setTime(date);
+
+		int year = cal.get(Calendar.YEAR);
+		int month = cal.get(Calendar.MONTH);
+		int day = cal.get(Calendar.DAY_OF_MONTH);
+		int hour = cal.get(Calendar.HOUR_OF_DAY);
+		int minute = cal.get(Calendar.MINUTE);
+		int second = cal.get(Calendar.SECOND);
+		int millis = cal.get(Calendar.MILLISECOND);
+
+		if (date == null) {
+			dateValue = null;
+		} else {
+			dateValue = (Date) dateObservable.getValue();
+			if (dateValue == null)
+				cal.clear();
+			else
+				cal.setTime(dateValue);
+			cal.set(Calendar.YEAR, year);
+			cal.set(Calendar.MONTH, month);
+			cal.set(Calendar.DAY_OF_MONTH, day);
+			dateValue = cal.getTime();
+		}
+
+		timeValue = (Date) timeObservable.getValue();
+		if (timeValue == null)
+			cal.clear();
+		else
+			cal.setTime(timeValue);
+		cal.set(Calendar.HOUR_OF_DAY, hour);
+		cal.set(Calendar.MINUTE, minute);
+		cal.set(Calendar.SECOND, second);
+		cal.set(Calendar.MILLISECOND, millis);
+		timeValue = cal.getTime();
+
+		updating = true;
+		try {
+			dateObservable.setValue(dateValue);
+			timeObservable.setValue(timeValue);
+		} finally {
+			updating = false;
+		}
+
+		notifyIfChanged();
+	}
+
+	public boolean isStale() {
+		ObservableTracker.getterCalled(this);
+		return dateObservable.isStale() || timeObservable.isStale();
+	}
+
+	public synchronized void dispose() {
+		checkRealm();
+		if (!isDisposed()) {
+			if (!dateObservable.isDisposed()) {
+				dateObservable.removeDisposeListener(privateInterface);
+				dateObservable.removeChangeListener(privateInterface);
+				dateObservable.removeStaleListener(privateInterface);
+			}
+			if (!timeObservable.isDisposed()) {
+				timeObservable.removeDisposeListener(privateInterface);
+				timeObservable.removeChangeListener(privateInterface);
+				timeObservable.removeStaleListener(privateInterface);
+			}
+			dateObservable = null;
+			timeObservable = null;
+			privateInterface = null;
+			cachedValue = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DecoratingObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DecoratingObservableValue.java
new file mode 100644
index 0000000..6ab290f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DecoratingObservableValue.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * 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 237718)
+ *     Matthew Hall - but 246626
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.DecoratingObservable;
+
+/**
+ * An observable value which decorates another observable value.
+ * 
+ * @since 1.2
+ */
+public class DecoratingObservableValue extends DecoratingObservable implements
+		IObservableValue {
+	private IObservableValue decorated;
+
+	private IValueChangeListener valueChangeListener;
+
+	/**
+	 * Constructs a DecoratingObservableValue which decorates the given
+	 * observable.
+	 * 
+	 * @param decorated
+	 *            the observable value being decorated
+	 * @param disposeDecoratedOnDispose 
+	 */
+	public DecoratingObservableValue(IObservableValue decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated, disposeDecoratedOnDispose);
+		this.decorated = decorated;
+	}
+
+	public synchronized void addValueChangeListener(
+			IValueChangeListener listener) {
+		addListener(ValueChangeEvent.TYPE, listener);
+	}
+
+	public synchronized void removeValueChangeListener(
+			IValueChangeListener listener) {
+		removeListener(ValueChangeEvent.TYPE, listener);
+	}
+
+	protected void fireValueChange(ValueDiff diff) {
+		// fire general change event first
+		super.fireChange();
+		fireEvent(new ValueChangeEvent(this, diff));
+	}
+
+	protected void fireChange() {
+		throw new RuntimeException(
+				"fireChange should not be called, use fireValueChange() instead"); //$NON-NLS-1$
+	}
+
+	protected void firstListenerAdded() {
+		if (valueChangeListener == null) {
+			valueChangeListener = new IValueChangeListener() {
+				public void handleValueChange(ValueChangeEvent event) {
+					DecoratingObservableValue.this.handleValueChange(event);
+				}
+			};
+		}
+		decorated.addValueChangeListener(valueChangeListener);
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+		if (valueChangeListener != null) {
+			decorated.removeValueChangeListener(valueChangeListener);
+			valueChangeListener = null;
+		}
+	}
+
+	/**
+	 * Called whenever a ValueChangeEvent is received from the decorated
+	 * observable. By default, this method fires the value change event again,
+	 * with the decorating observable as the event source. Subclasses may
+	 * override to provide different behavior.
+	 * 
+	 * @param event
+	 *            the change event received from the decorated observable
+	 */
+	protected void handleValueChange(final ValueChangeEvent event) {
+		fireValueChange(event.diff);
+	}
+
+	public Object getValue() {
+		getterCalled();
+		return decorated.getValue();
+	}
+
+	public void setValue(Object value) {
+		checkRealm();
+		decorated.setValue(value);
+	}
+
+	public Object getValueType() {
+		return decorated.getValueType();
+	}
+
+	public synchronized void dispose() {
+		if (decorated != null && valueChangeListener != null) {
+			decorated.removeValueChangeListener(valueChangeListener);
+		}
+		decorated = null;
+		valueChangeListener = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DecoratingVetoableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DecoratingVetoableValue.java
new file mode 100644
index 0000000..0d7108f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DecoratingVetoableValue.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263691)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.Diffs;
+
+/**
+ * An {@link IVetoableValue} decorator for an observable value.
+ * 
+ * @since 1.2
+ */
+public class DecoratingVetoableValue extends DecoratingObservableValue
+		implements IVetoableValue {
+	/**
+	 * @param decorated
+	 * @param disposeDecoratedOnDispose
+	 */
+	public DecoratingVetoableValue(IObservableValue decorated,
+			boolean disposeDecoratedOnDispose) {
+		super(decorated, disposeDecoratedOnDispose);
+	}
+
+	public void setValue(Object value) {
+		checkRealm();
+		Object currentValue = getValue();
+		ValueDiff diff = Diffs.createValueDiff(currentValue, value);
+		boolean okToProceed = fireValueChanging(diff);
+		if (!okToProceed) {
+			throw new ChangeVetoException("Change not permitted"); //$NON-NLS-1$
+		}
+		super.setValue(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 boolean fireValueChanging(ValueDiff diff) {
+		checkRealm();
+
+		ValueChangingEvent event = new ValueChangingEvent(this, diff);
+		fireEvent(event);
+		return !event.veto;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DuplexingObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DuplexingObservableValue.java
new file mode 100644
index 0000000..4d5ffaf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/DuplexingObservableValue.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 175735)
+ *     Matthew Hall - bug 262407
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IStaleListener;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * @since 1.2
+ */
+public abstract class DuplexingObservableValue extends AbstractObservableValue {
+	/**
+	 * Returns a DuplexingObservableValue implementation with predefined values
+	 * to use if the list is empty or contains multiple different values.
+	 * 
+	 * @param target
+	 *            the observable list
+	 * @param emptyValue
+	 *            the value to use when the target list is empty
+	 * @param multiValue
+	 *            the value to use when the target list contains multiple values
+	 *            that are not equivalent to eachother.
+	 * @return a DuplexingObservableValue implementation with predefined values
+	 *         to use if the list is empty or contains multiple different
+	 *         values.
+	 */
+	public static DuplexingObservableValue withDefaults(IObservableList target,
+			final Object emptyValue, final Object multiValue) {
+		return new DuplexingObservableValue(target) {
+			protected Object coalesceElements(Collection elements) {
+				if (elements.isEmpty())
+					return emptyValue;
+				Iterator it = elements.iterator();
+				Object first = it.next();
+				while (it.hasNext())
+					if (!Util.equals(first, it.next()))
+						return multiValue;
+				return first;
+			}
+		};
+	}
+
+	private IObservableList target;
+	private final Object valueType;
+
+	private boolean dirty = true;
+	private boolean updating = false;
+	private Object cachedValue = null; // applicable only while hasListener()
+
+	private PrivateInterface privateInterface;
+
+	/**
+	 * @param target
+	 */
+	public DuplexingObservableValue(IObservableList target) {
+		this(target, target.getElementType());
+	}
+
+	/**
+	 * @param target
+	 * @param valueType
+	 */
+	public DuplexingObservableValue(IObservableList target, Object valueType) {
+		super(target.getRealm());
+		this.target = target;
+		this.valueType = valueType;
+	}
+
+	private class PrivateInterface implements IChangeListener, IStaleListener {
+		public void handleChange(ChangeEvent event) {
+			if (!updating)
+				makeDirty();
+		}
+
+		public void handleStale(StaleEvent staleEvent) {
+			if (!dirty) {
+				fireStale();
+			}
+		}
+	}
+
+	protected void firstListenerAdded() {
+		if (privateInterface == null)
+			privateInterface = new PrivateInterface();
+		target.addChangeListener(privateInterface);
+		target.addStaleListener(privateInterface);
+	}
+
+	protected void lastListenerRemoved() {
+		target.removeChangeListener(privateInterface);
+		target.removeStaleListener(privateInterface);
+	}
+
+	protected final void makeDirty() {
+		if (hasListeners() && !dirty) {
+			dirty = true;
+
+			// copy the old value
+			final Object oldValue = cachedValue;
+			// Fire the "dirty" event. This implementation recomputes the new
+			// value lazily.
+			fireValueChange(new ValueDiff() {
+				public Object getOldValue() {
+					return oldValue;
+				}
+
+				public Object getNewValue() {
+					return getValue();
+				}
+			});
+		}
+	}
+
+	public boolean isStale() {
+		getValue();
+		return target.isStale();
+	}
+
+	protected Object doGetValue() {
+		if (!hasListeners())
+			return coalesceElements(target);
+
+		if (dirty) {
+			cachedValue = coalesceElements(target);
+			dirty = false;
+			if (target.isStale())
+				fireStale();
+		}
+
+		return cachedValue;
+	}
+
+	protected abstract Object coalesceElements(Collection elements);
+
+	protected void doSetValue(Object value) {
+		final Object oldValue = cachedValue;
+
+		boolean wasUpdating = updating;
+		try {
+			updating = true;
+			for (int i = 0; i < target.size(); i++)
+				target.set(i, value);
+		} finally {
+			updating = wasUpdating;
+		}
+
+		// Fire the "dirty" event. This implementation recomputes the new
+		// value lazily.
+		if (hasListeners()) {
+			fireValueChange(new ValueDiff() {
+				public Object getOldValue() {
+					return oldValue;
+				}
+
+				public Object getNewValue() {
+					return getValue();
+				}
+			});
+		}
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	public synchronized void addChangeListener(IChangeListener listener) {
+		super.addChangeListener(listener);
+		// If somebody is listening, we need to make sure we attach our own
+		// listeners
+		computeValueForListeners();
+	}
+
+	public synchronized void addValueChangeListener(
+			IValueChangeListener listener) {
+		super.addValueChangeListener(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 Runnable() {
+			public void run() {
+				// 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 dispose() {
+		if (privateInterface != null && target != null) {
+			target.removeChangeListener(privateInterface);
+			target.removeStaleListener(privateInterface);
+		}
+		target = null;
+		privateInterface = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IObservableValue.java
new file mode 100644
index 0000000..820fa93
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IObservableValue.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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 237718
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+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.
+ * 
+ * @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 extends 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);
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IValueChangeListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IValueChangeListener.java
new file mode 100644
index 0000000..7af5382
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IValueChangeListener.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.IObservablesListener;
+
+/**
+ * Listener for changes to observable values.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface IValueChangeListener extends IObservablesListener {
+
+	/**
+	 * Handles a change to an observable value. The given event object must only
+	 * be used locally in this method because it may be reused for other change
+	 * notifications. The diff object referenced by the event is immutable and
+	 * may be used non-locally.
+	 * 
+	 * @param event
+	 *            the event
+	 */
+	void handleValueChange(ValueChangeEvent event);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IValueChangingListener.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IValueChangingListener.java
new file mode 100644
index 0000000..ceb07b7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IValueChangingListener.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.IObservablesListener;
+
+/**
+ * Listener for pre-change events for observable values.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface IValueChangingListener extends IObservablesListener {
+
+	/**
+	 * This method is called when the value is about to change and provides an
+	 * opportunity to veto the change. The given event object must only be used
+	 * locally in this method because it may be reused for other change
+	 * notifications. The diff object referenced by the event is immutable and
+	 * may be used non-locally.
+	 * 
+	 * @param event
+	 */
+	public void handleValueChanging(ValueChangingEvent event);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IVetoableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IVetoableValue.java
new file mode 100644
index 0000000..1b795ed
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/IVetoableValue.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.databinding.observable.value;
+
+/**
+ * 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 extends IObservableValue {
+	
+	/**
+	 * @param listener
+	 */
+	public void addValueChangingListener(IValueChangingListener listener);
+	
+	/**
+	 * @param listener
+	 */
+	public void removeValueChangingListener(IValueChangingListener listener);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/SelectObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/SelectObservableValue.java
new file mode 100644
index 0000000..12dbe1e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/SelectObservableValue.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 249992)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * An observable value which behaves similarly to the &lt;select&gt; and
+ * &lt;option&gt; HTML tags. A SelectObservableValue has a number of options
+ * added to it via the {@link #addOption(Object, IObservableValue)} method. The
+ * value of the SelectObservableValue is the value of whichever option's
+ * observable has a value of Boolean.TRUE, or null if none of the observable's
+ * values are Boolean.TRUE.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * 
+ * @since 1.2
+ */
+public class SelectObservableValue extends AbstractObservableValue {
+	private class Option {
+		private final Object value;
+		private final IObservableValue observable;
+
+		public Option(Object value, IObservableValue observable) {
+			this.value = value;
+			this.observable = observable;
+		}
+	}
+
+	private final Object valueType;
+
+	private Option[] options;
+	private int selectionIndex = -1; // n/a while not hasListeners()
+
+	private boolean updating = false;
+
+	private IValueChangeListener listener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (!updating) {
+				IObservableValue observable = event.getObservableValue();
+				if (Boolean.TRUE.equals(observable.getValue())) {
+					notifyIfChanged(indexOfObservable(observable));
+				}
+			}
+		}
+	};
+
+	/**
+	 * Constructs a SelectObservableValue on the default realm.
+	 */
+	public SelectObservableValue() {
+		this(Realm.getDefault(), null);
+	}
+
+	/**
+	 * Constructs a SelectObservableValue on the specified realm.
+	 * 
+	 * @param realm
+	 *            the realm
+	 */
+	public SelectObservableValue(Realm realm) {
+		this(realm, null);
+	}
+
+	/**
+	 * Constructs a SelectObservableValue on the default realm, with the given
+	 * value type.
+	 * 
+	 * @param valueType
+	 *            the value type
+	 */
+	public SelectObservableValue(Object valueType) {
+		this(Realm.getDefault(), valueType);
+	}
+
+	/**
+	 * Constructs a SelectObservableValue on the given realm, with the given
+	 * value type.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * @param valueType
+	 *            the value type
+	 */
+	public SelectObservableValue(Realm realm, Object valueType) {
+		super(realm);
+		this.valueType = valueType;
+		this.options = new Option[0];
+	}
+
+	protected void firstListenerAdded() {
+		super.firstListenerAdded();
+		selectionIndex = indexOfValue(getLiveValue());
+		for (int i = 0; i < options.length; i++) {
+			options[i].observable.addValueChangeListener(listener);
+		}
+	}
+
+	protected void lastListenerRemoved() {
+		for (int i = 0; i < options.length; i++) {
+			options[i].observable.removeValueChangeListener(listener);
+		}
+		selectionIndex = -1;
+		super.lastListenerRemoved();
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	private void notifyIfChanged(int index) {
+		if (hasListeners() && selectionIndex != index) {
+			Object oldValue = valueAtIndex(selectionIndex);
+			Object newValue = valueAtIndex(index);
+			selectionIndex = index;
+			fireValueChange(Diffs.createValueDiff(oldValue, newValue));
+		}
+	}
+
+	/**
+	 * Adds an option to this SelectObservableValue. If the observable contains
+	 * Boolean.TRUE then the selection changes immediately to the given value.
+	 * 
+	 * @param value
+	 *            The value associated with the provided observable
+	 * @param observable
+	 *            an observable of value type Boolean.class or Boolean.TYPE
+	 */
+	public void addOption(Object value, IObservableValue observable) {
+		checkRealm();
+
+		Option option = new Option(value, observable);
+		addOption(option);
+
+		if (hasListeners()) {
+			observable.addValueChangeListener(listener);
+			if (Boolean.TRUE.equals(observable.getValue())) {
+				notifyIfChanged(indexOfObservable(observable));
+			}
+		}
+	}
+
+	private void addOption(Option option) {
+		Option[] newOptions = new Option[options.length + 1];
+		System.arraycopy(options, 0, newOptions, 0, options.length);
+		newOptions[options.length] = option;
+		options = newOptions;
+	}
+
+	protected Object doGetValue() {
+		return hasListeners() ? valueAtIndex(selectionIndex) : getLiveValue();
+	}
+
+	private Object getLiveValue() {
+		for (int i = 0; i < options.length; i++) {
+			if (Boolean.TRUE.equals(options[i].observable.getValue()))
+				return options[i].value;
+		}
+		return null;
+	}
+
+	protected void doSetValue(Object value) {
+		int index = indexOfValue(value);
+
+		try {
+			updating = true;
+			for (int i = 0; i < options.length; i++) {
+				options[i].observable.setValue(i == index ? Boolean.TRUE
+						: Boolean.FALSE);
+			}
+		} finally {
+			updating = false;
+		}
+
+		notifyIfChanged(index);
+	}
+
+	private Object valueAtIndex(int index) {
+		if (index == -1)
+			return null;
+		return options[index].value;
+	}
+
+	private int indexOfValue(Object value) {
+		for (int i = 0; i < options.length; i++)
+			if (Util.equals(options[i].value, value))
+				return i;
+		return -1;
+	}
+
+	private int indexOfObservable(IObservableValue observable) {
+		for (int i = 0; i < options.length; i++)
+			if (options[i].observable == observable)
+				return i;
+		return -1;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueChangeEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueChangeEvent.java
new file mode 100644
index 0000000..3d4fba9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueChangeEvent.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+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 extends 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 ValueChangeEvent(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 (IObservableValue) source;
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((IValueChangeListener) listener).handleValueChange(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueChangingEvent.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueChangingEvent.java
new file mode 100644
index 0000000..f92cde7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueChangingEvent.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+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 extends 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 boolean veto = false;
+
+	/**
+	 * Creates a new value changing event.
+	 * 
+	 * @param source
+	 *            the source observable value
+	 * @param diff
+	 *            the value change
+	 */
+	public ValueChangingEvent(IObservableValue source, ValueDiff diff) {
+		super(source);
+		this.diff = diff;
+	}
+
+	/**
+	 * @return the observable value from which this event originated
+	 */
+	public IObservableValue getObservableValue() {
+		return (IObservableValue) source;
+	}
+
+	protected void dispatch(IObservablesListener listener) {
+		((IValueChangingListener) listener).handleValueChanging(this);
+	}
+
+	protected Object getListenerType() {
+		return TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueDiff.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueDiff.java
new file mode 100644
index 0000000..8e26ef6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ValueDiff.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 194734
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IDiff;
+
+/**
+ * @since 1.0
+ * 
+ */
+public abstract class ValueDiff implements IDiff {
+	/**
+	 * Creates a value diff.
+	 */
+	public ValueDiff() {
+	}
+
+	/**
+	 * @return the old value
+	 */
+	public abstract Object getOldValue();
+
+	/**
+	 * @return the new value
+	 */
+	public abstract Object getNewValue();
+
+	public boolean equals(Object obj) {
+		if (obj instanceof ValueDiff) {
+			ValueDiff val = (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 == null) ? 0 : nv.hashCode());
+		result = prime * result + ((ov == 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() != null ? getOldValue().toString() : "null") //$NON-NLS-1$
+			.append("], newValue [") //$NON-NLS-1$
+			.append(getNewValue() != null ? getNewValue().toString() : "null") //$NON-NLS-1$
+			.append("]}"); //$NON-NLS-1$
+		
+		return buffer.toString();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/WritableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/WritableValue.java
new file mode 100644
index 0000000..45e469e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/WritableValue.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *     Boris Bokowski - bug 256422
+ *     Matthew Hall - bug 256422
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.observable.value;
+
+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 extends 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 WritableValue() {
+		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 WritableValue(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 WritableValue(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 WritableValue(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) {
+        if (this.value != value) {
+            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);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/package.html
new file mode 100644
index 0000000..4ce13df
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/ILogger.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/ILogger.java
new file mode 100644
index 0000000..652d719
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/ILogger.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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)
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.util;
+
+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#setLog(ILogger)
+ * @since 1.1
+ */
+public interface ILogger {
+
+	/**
+	 * Logs the given status.
+	 * 
+	 * @param status
+	 *            the status to log
+	 */
+	public void log(IStatus status);
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/Policy.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/Policy.java
new file mode 100644
index 0000000..4fd3ee9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/Policy.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *******************************************************************************/
+package org.eclipse.core.databinding.util;
+
+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 boolean 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 ILogger() {
+			public void log(IStatus status) {
+				System.err.println(status.toString());
+				if( status.getException() != 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 == null) {
+			log = getDummyLog();
+		}
+		return log;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/package.html b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/package.html
new file mode 100644
index 0000000..0b186a7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/util/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityMap.java
new file mode 100644
index 0000000..334dc2f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityMap.java
@@ -0,0 +1,407 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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
+ *         (through ViewerElementMap.java)
+ *     Matthew Hall - bugs 262269, 303847
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.identity;
+
+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.internal.databinding.observable.Util;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * A {@link Map} whose keys are added, removed and compared by identity. The
+ * keys in the map are compared using <code>==</code> 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.
+ * 
+ * @since 1.2
+ */
+public class IdentityMap implements Map {
+	private Map wrappedMap;
+
+	/**
+	 * Constructs an IdentityMap.
+	 */
+	public IdentityMap() {
+		this.wrappedMap = new HashMap();
+	}
+
+	/**
+	 * Constructs an IdentityMap containing all the entries in the specified
+	 * map.
+	 * 
+	 * @param map
+	 *            the map whose entries are to be added to this map.
+	 */
+	public IdentityMap(Map map) {
+		this();
+		Assert.isNotNull(map);
+		putAll(map);
+	}
+
+	public void clear() {
+		wrappedMap.clear();
+	}
+
+	public boolean containsKey(Object key) {
+		return wrappedMap.containsKey(IdentityWrapper.wrap(key));
+	}
+
+	public boolean containsValue(Object value) {
+		return wrappedMap.containsValue(value);
+	}
+
+	public Set entrySet() {
+		final Set wrappedEntrySet = wrappedMap.entrySet();
+		return new Set() {
+			public boolean add(Object o) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean addAll(Collection c) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void clear() {
+				wrappedEntrySet.clear();
+			}
+
+			public boolean contains(Object o) {
+				for (Iterator iterator = iterator(); iterator.hasNext();)
+					if (iterator.next().equals(o))
+						return true;
+				return false;
+			}
+
+			public boolean containsAll(Collection c) {
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					if (!contains(iterator.next()))
+						return false;
+				return true;
+			}
+
+			public boolean isEmpty() {
+				return wrappedEntrySet.isEmpty();
+			}
+
+			public Iterator iterator() {
+				final Iterator wrappedIterator = wrappedEntrySet.iterator();
+				return new Iterator() {
+					public boolean hasNext() {
+						return wrappedIterator.hasNext();
+					}
+
+					public Object next() {
+						final Map.Entry wrappedEntry = (Map.Entry) wrappedIterator
+								.next();
+						return new Map.Entry() {
+							public Object getKey() {
+								return ((IdentityWrapper) wrappedEntry.getKey())
+										.unwrap();
+							}
+
+							public Object getValue() {
+								return wrappedEntry.getValue();
+							}
+
+							public Object setValue(Object value) {
+								return wrappedEntry.setValue(value);
+							}
+
+							public boolean equals(Object obj) {
+								if (obj == this)
+									return true;
+								if (obj == null || !(obj instanceof Map.Entry))
+									return false;
+								Map.Entry that = (Map.Entry) obj;
+								return this.getKey() == that.getKey()
+										&& Util.equals(this.getValue(), that
+												.getValue());
+							}
+
+							public int hashCode() {
+								return wrappedEntry.hashCode();
+							}
+						};
+					}
+
+					public void remove() {
+						wrappedIterator.remove();
+					}
+				};
+			}
+
+			public boolean remove(Object o) {
+				final Map.Entry unwrappedEntry = (Map.Entry) o;
+				final IdentityWrapper wrappedKey = IdentityWrapper
+						.wrap(unwrappedEntry.getKey());
+				Map.Entry wrappedEntry = new Map.Entry() {
+					public Object getKey() {
+						return wrappedKey;
+					}
+
+					public Object getValue() {
+						return unwrappedEntry.getValue();
+					}
+
+					public Object setValue(Object value) {
+						throw new UnsupportedOperationException();
+					}
+
+					public boolean equals(Object obj) {
+						if (obj == this)
+							return true;
+						if (obj == null || !(obj instanceof Map.Entry))
+							return false;
+						Map.Entry that = (Map.Entry) obj;
+						return Util.equals(wrappedKey, that.getKey())
+								&& Util
+										.equals(this.getValue(), that
+												.getValue());
+					}
+
+					public int hashCode() {
+						return wrappedKey.hashCode()
+								^ (getValue() == null ? 0 : getValue()
+										.hashCode());
+					}
+				};
+				return wrappedEntrySet.remove(wrappedEntry);
+			}
+
+			public boolean removeAll(Collection c) {
+				boolean changed = false;
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					changed |= remove(iterator.next());
+				return changed;
+			}
+
+			public boolean retainAll(Collection c) {
+				boolean 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 = (Object[]) Array.newInstance(a.getClass()
+							.getComponentType(), size);
+				}
+				int i = 0;
+				for (Iterator iterator = iterator(); iterator.hasNext();) {
+					a[i] = iterator.next();
+					i++;
+				}
+				return a;
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this)
+					return true;
+				if (obj == null || !(obj instanceof Set))
+					return false;
+				Set that = (Set) obj;
+				return this.size() == that.size() && containsAll(that);
+			}
+
+			public int hashCode() {
+				return wrappedEntrySet.hashCode();
+			}
+		};
+	}
+
+	public Object get(Object key) {
+		return wrappedMap.get(IdentityWrapper.wrap(key));
+	}
+
+	public boolean isEmpty() {
+		return wrappedMap.isEmpty();
+	}
+
+	public Set keySet() {
+		final Set wrappedKeySet = wrappedMap.keySet();
+		return new Set() {
+			public boolean add(Object o) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean addAll(Collection c) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void clear() {
+				wrappedKeySet.clear();
+			}
+
+			public boolean contains(Object o) {
+				return wrappedKeySet.contains(IdentityWrapper.wrap(o));
+			}
+
+			public boolean containsAll(Collection c) {
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					if (!wrappedKeySet.contains(IdentityWrapper.wrap(iterator
+							.next())))
+						return false;
+				return true;
+			}
+
+			public boolean isEmpty() {
+				return wrappedKeySet.isEmpty();
+			}
+
+			public Iterator iterator() {
+				final Iterator wrappedIterator = wrappedKeySet.iterator();
+				return new Iterator() {
+					public boolean hasNext() {
+						return wrappedIterator.hasNext();
+					}
+
+					public Object next() {
+						return ((IdentityWrapper) wrappedIterator.next())
+								.unwrap();
+					}
+
+					public void remove() {
+						wrappedIterator.remove();
+					}
+				};
+			}
+
+			public boolean remove(Object o) {
+				return wrappedKeySet.remove(IdentityWrapper.wrap(o));
+			}
+
+			public boolean removeAll(Collection c) {
+				boolean changed = false;
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					changed |= wrappedKeySet.remove(IdentityWrapper
+							.wrap(iterator.next()));
+				return changed;
+			}
+
+			public boolean retainAll(Collection c) {
+				boolean 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 (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();
+				IdentityWrapper[] wrappedArray = (IdentityWrapper[]) wrappedKeySet
+						.toArray(new IdentityWrapper[size]);
+				Object[] result = a;
+				if (a.length < size) {
+					result = (Object[]) Array.newInstance(a.getClass()
+							.getComponentType(), size);
+				}
+				for (int i = 0; i < size; i++)
+					result[i] = wrappedArray[i].unwrap();
+				return result;
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this)
+					return true;
+				if (obj == null || !(obj instanceof Set))
+					return false;
+				Set that = (Set) obj;
+				return this.size() == that.size() && containsAll(that);
+			}
+
+			public int hashCode() {
+				return wrappedKeySet.hashCode();
+			}
+		};
+	}
+
+	public Object put(Object key, Object value) {
+		return wrappedMap.put(IdentityWrapper.wrap(key), value);
+	}
+
+	public void putAll(Map other) {
+		for (Iterator iterator = other.entrySet().iterator(); iterator
+				.hasNext();) {
+			Map.Entry entry = (Map.Entry) iterator.next();
+			wrappedMap.put(IdentityWrapper.wrap(entry.getKey()), entry
+					.getValue());
+		}
+	}
+
+	public Object remove(Object key) {
+		return wrappedMap.remove(IdentityWrapper.wrap(key));
+	}
+
+	public int size() {
+		return wrappedMap.size();
+	}
+
+	public Collection values() {
+		return wrappedMap.values();
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null || !(obj instanceof Map))
+			return false;
+		Map that = (Map) obj;
+		return this.entrySet().equals(that.entrySet());
+	}
+
+	public int hashCode() {
+		return wrappedMap.hashCode();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityObservableSet.java
new file mode 100644
index 0000000..15a3431
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityObservableSet.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *         (through ObservableViewerElementSet.java)
+ *     Matthew Hall - bug 262269
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.identity;
+
+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;
+
+/**
+ * An {@link IObservableSet} of elements where elements are added, removed and
+ * compared by identity. Elements of the set are compared using <code>==</code>
+ * 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.
+ * 
+ * @since 1.2
+ */
+public class IdentityObservableSet extends AbstractObservableSet {
+	private Set wrappedSet;
+	private Object elementType;
+
+	/**
+	 * Constructs an IdentityObservableSet on the given {@link Realm}.
+	 * 
+	 * @param realm
+	 *            the realm of the constructed set.
+	 * @param elementType
+	 *            the element type of the constructed set.
+	 */
+	public IdentityObservableSet(Realm realm, Object elementType) {
+		super(realm);
+
+		this.wrappedSet = new IdentitySet();
+		this.elementType = elementType;
+	}
+
+	protected Set getWrappedSet() {
+		return wrappedSet;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator wrappedIterator = wrappedSet.iterator();
+		return new Iterator() {
+			Object last;
+
+			public boolean 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 boolean add(Object o) {
+		getterCalled();
+		boolean changed = wrappedSet.add(o);
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.singleton(o),
+					Collections.EMPTY_SET));
+		return changed;
+	}
+
+	public boolean addAll(Collection c) {
+		getterCalled();
+		Set additions = new IdentitySet();
+		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
+			Object element = iterator.next();
+			if (wrappedSet.add(element))
+				additions.add(element);
+		}
+		boolean changed = !additions.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET));
+		return changed;
+	}
+
+	public boolean remove(Object o) {
+		getterCalled();
+		boolean changed = wrappedSet.remove(o);
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET,
+					Collections.singleton(o)));
+		return changed;
+	}
+
+	public boolean removeAll(Collection c) {
+		getterCalled();
+		Set removals = new IdentitySet();
+		for (Iterator iterator = c.iterator(); iterator.hasNext();) {
+			Object element = iterator.next();
+			if (wrappedSet.remove(element))
+				removals.add(element);
+		}
+		boolean changed = !removals.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+		return changed;
+	}
+
+	public boolean retainAll(Collection c) {
+		getterCalled();
+		Set removals = new IdentitySet();
+		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 (element == toRetain[i])
+					continue outer;
+			}
+			iterator.remove();
+			removals.add(element);
+		}
+		boolean 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 IdentitySet();
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentitySet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentitySet.java
new file mode 100644
index 0000000..4bf6388
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentitySet.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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
+ *         (through ViewerElementSet.java)
+ *     Matthew Hall - bugs 262269, 303847
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.identity;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A {@link Set} of elements where elements are added, removed and compared by
+ * identity. Elements of the set are compared using <code>==</code> 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.
+ * 
+ * @since 1.2
+ */
+public class IdentitySet implements Set {
+	private final Set wrappedSet;
+
+	/**
+	 * Constructs an IdentitySet.
+	 */
+	public IdentitySet() {
+		this.wrappedSet = new HashSet();
+	}
+
+	/**
+	 * Constructs an IdentitySet containing all the unique instances in the
+	 * specified collection.
+	 * 
+	 * @param collection
+	 *            the collection whose elements are to be added to this set.
+	 */
+	public IdentitySet(Collection collection) {
+		this();
+		addAll(collection);
+	}
+
+	public boolean add(Object o) {
+		return wrappedSet.add(IdentityWrapper.wrap(o));
+	}
+
+	public boolean addAll(Collection c) {
+		boolean changed = false;
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			changed |= wrappedSet.add(IdentityWrapper.wrap(iterator.next()));
+		return changed;
+	}
+
+	public void clear() {
+		wrappedSet.clear();
+	}
+
+	public boolean contains(Object o) {
+		return wrappedSet.contains(IdentityWrapper.wrap(o));
+	}
+
+	public boolean containsAll(Collection c) {
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			if (!wrappedSet.contains(IdentityWrapper.wrap(iterator.next())))
+				return false;
+		return true;
+	}
+
+	public boolean isEmpty() {
+		return wrappedSet.isEmpty();
+	}
+
+	public Iterator iterator() {
+		final Iterator wrappedIterator = wrappedSet.iterator();
+		return new Iterator() {
+			public boolean hasNext() {
+				return wrappedIterator.hasNext();
+			}
+
+			public Object next() {
+				return ((IdentityWrapper) wrappedIterator.next()).unwrap();
+			}
+
+			public void remove() {
+				wrappedIterator.remove();
+			}
+		};
+	}
+
+	public boolean remove(Object o) {
+		return wrappedSet.remove(IdentityWrapper.wrap(o));
+	}
+
+	public boolean removeAll(Collection c) {
+		boolean changed = false;
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			changed |= remove(iterator.next());
+		return changed;
+	}
+
+	public boolean 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.
+		boolean 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 (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();
+		IdentityWrapper[] wrappedArray = (IdentityWrapper[]) wrappedSet
+				.toArray(new IdentityWrapper[size]);
+		Object[] result = a;
+		if (a.length < size) {
+			result = (Object[]) Array.newInstance(a.getClass()
+					.getComponentType(), size);
+		}
+		for (int i = 0; i < size; i++)
+			result[i] = wrappedArray[i].unwrap();
+		return result;
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (!(obj instanceof Set))
+			return false;
+		Set that = (Set) obj;
+		return size() == that.size() && containsAll(that);
+	}
+
+	public int hashCode() {
+		int hash = 0;
+		for (Iterator iterator = iterator(); iterator.hasNext();) {
+			Object element = iterator.next();
+			hash += element == null ? 0 : element.hashCode();
+		}
+		return hash;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityWrapper.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityWrapper.java
new file mode 100644
index 0000000..9e140c5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/identity/IdentityWrapper.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *     Matthew Hall - bug 303847
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.identity;
+
+/**
+ * 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 {
+	private static final IdentityWrapper NULL_WRAPPER = new IdentityWrapper(
+			null);
+
+	/**
+	 * @param o
+	 *            the object to wrap
+	 * @return an IdentityWrapper wrapping the specified object
+	 */
+	public static IdentityWrapper wrap(Object o) {
+		return o == null ? NULL_WRAPPER : new IdentityWrapper(o);
+	}
+
+	final Object o;
+
+	/**
+	 * @param o
+	 */
+	private IdentityWrapper(Object o) {
+		this.o = o;
+	}
+
+	/**
+	 * @return the unwrapped object
+	 */
+	public Object unwrap() {
+		return o;
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == null || obj.getClass() != IdentityWrapper.class) {
+			return false;
+		}
+		return o == ((IdentityWrapper) obj).o;
+	}
+
+	public int hashCode() {
+		return System.identityHashCode(o);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Activator.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Activator.java
new file mode 100644
index 0000000..b8b78b0
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Activator.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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 implements 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 Activator() {
+	}
+
+	public void start(BundleContext context) throws Exception {
+		_frameworkLogTracker = new ServiceTracker(context, FrameworkLog.class.getName(), null);
+		_frameworkLogTracker.open();
+
+		Policy.setLog(new ILogger() {
+
+			public void log(IStatus status) {
+				ServiceTracker frameworkLogTracker = _frameworkLogTracker;
+				FrameworkLog log = frameworkLogTracker == null ? null : (FrameworkLog) frameworkLogTracker.getService();
+				if (log != 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() != 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 = t instanceof CoreException ? 1 : 0;
+		// ensure a substatus inside a CoreException is properly logged 
+		if (stackCode == 1) {
+			IStatus coreStatus = ((CoreException) t).getStatus();
+			if (coreStatus != 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 = (FrameworkLogEntry[]) (childlist.size() == 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) throws Exception {
+		if (_frameworkLogTracker != null) {
+			_frameworkLogTracker.close();
+			_frameworkLogTracker = null;
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/ConstantObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/ConstantObservableValue.java
new file mode 100644
index 0000000..f971420
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/ConstantObservableValue.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 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, 146397, 249526
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable;
+
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+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 implements 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 ConstantObservableValue(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 ConstantObservableValue(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;
+		ObservableTracker.observableCreated(this);
+	}
+
+	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 addDisposeListener(IDisposeListener listener) {
+		// ignore
+	}
+
+	public void addStaleListener(IStaleListener listener) {
+		// ignore
+	}
+
+	public boolean isDisposed() {
+		return false;
+	}
+
+	public void dispose() {
+		// nothing to dispose
+	}
+
+	public Realm getRealm() {
+		return realm;
+	}
+
+	public boolean isStale() {
+		return false;
+	}
+
+	public void removeChangeListener(IChangeListener listener) {
+		// ignore
+	}
+
+	public void removeDisposeListener(IDisposeListener listener) {
+		// ignore
+	}
+
+	public void removeStaleListener(IStaleListener listener) {
+		// ignore
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/DelayedObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/DelayedObservableValue.java
new file mode 100644
index 0000000..1741c35
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/DelayedObservableValue.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 - bugs 212223, 208332, 245647
+ *  	Will Horn - bug 215297
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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.databinding.observable.value.ValueDiff;
+
+/**
+ * {@link IObservableValue} implementation that wraps an
+ * {@link IObservableValue} and delays notification of value change events from
+ * the wrapped observable value until a certain time has passed since the last
+ * change event. This class helps to boost performance in bindings (both in
+ * validation and in event firing) when the observed value is rapidly changing.
+ * A common use of this class is to delay validation until the user stops typing
+ * in an UI field. To notify about pending changes, a DelayedObservableValue
+ * fires a stale event when the wrapped observable value fires a change event,
+ * and remains stale as long as a value change is pending.
+ * 
+ * Note that this class will not forward {@link ValueChangingEvent} events from
+ * a wrapped {@link IVetoableValue}.
+ * 
+ * @since 1.2
+ */
+public class DelayedObservableValue extends AbstractObservableValue implements
+		IStaleListener, IValueChangeListener {
+	class ValueUpdater implements Runnable {
+		private final Object oldValue;
+
+		boolean cancel = false;
+		boolean running = false;
+
+		ValueUpdater(Object oldValue) {
+			this.oldValue = oldValue;
+		}
+
+		void cancel() {
+			cancel = true;
+		}
+
+		public void run() {
+			if (!cancel)
+				try {
+					running = true;
+					internalFireValueChange(oldValue);
+				} finally {
+					running = false;
+				}
+		}
+	}
+
+	private final int delay;
+	private IObservableValue observable;
+
+	private boolean dirty = true;
+	private Object cachedValue = null;
+
+	private boolean 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 DelayedObservableValue(int delayMillis, IObservableValue observable) {
+		super(observable.getRealm());
+		this.delay = delayMillis;
+		this.observable = observable;
+
+		observable.addValueChangeListener(this);
+		observable.addStaleListener(this);
+
+		cachedValue = doGetValue();
+	}
+
+	public void handleValueChange(ValueChangeEvent event) {
+		if (!updating)
+			makeDirty();
+	}
+
+	public void handleStale(StaleEvent staleEvent) {
+		if (!updating)
+			fireStale();
+	}
+
+	protected Object doGetValue() {
+		if (dirty) {
+			cachedValue = observable.getValue();
+			dirty = false;
+
+			if (updater != 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 boolean isStale() {
+		ObservableTracker.getterCalled(this);
+		return (dirty && updater != 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 synchronized void dispose() {
+		cancelScheduledUpdate();
+		if (observable != null) {
+			observable.dispose();
+			observable = null;
+		}
+		super.dispose();
+	}
+
+	private void makeDirty() {
+		if (!dirty) {
+			dirty = true;
+			fireStale();
+		}
+		cancelScheduledUpdate(); // if any
+		scheduleUpdate();
+	}
+
+	private void cancelScheduledUpdate() {
+		if (updater != null) {
+			updater.cancel();
+			updater = null;
+		}
+	}
+
+	private void scheduleUpdate() {
+		updater = new ValueUpdater(cachedValue);
+		getRealm().timerExec(delay, updater);
+	}
+
+	private void internalFireValueChange(final Object oldValue) {
+		cancelScheduledUpdate();
+		fireValueChange(new ValueDiff() {
+			public Object getOldValue() {
+				return oldValue;
+			}
+
+			public Object getNewValue() {
+				return getValue();
+			}
+		});
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/EmptyObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/EmptyObservableList.java
new file mode 100644
index 0000000..f97a7f2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/EmptyObservableList.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208858, 208332, 146397, 249526
+ *******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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.IDisposeListener;
+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.list.IListChangeListener;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Singleton empty list
+ */
+public class EmptyObservableList implements IObservableList {
+
+	private static final List emptyList = Collections.EMPTY_LIST;
+
+	private final 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 EmptyObservableList(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 EmptyObservableList(Realm realm, Object elementType) {
+		this.realm = realm;
+		this.elementType = elementType;
+		ObservableTracker.observableCreated(this);
+	}
+
+	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 boolean isEmpty() {
+		checkRealm();
+		return true;
+	}
+
+	public boolean 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 boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean containsAll(Collection c) {
+		checkRealm();
+		return c.isEmpty();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean 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 void addDisposeListener(IDisposeListener listener) {
+	}
+
+	public void removeDisposeListener(IDisposeListener listener) {
+	}
+
+	public boolean isStale() {
+		checkRealm();
+		return false;
+	}
+
+	public boolean isDisposed() {
+		return false;
+	}
+
+	public void dispose() {
+	}
+
+	public boolean 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 boolean equals(Object obj) {
+		checkRealm();
+		if (obj == this)
+			return true;
+		if (obj == null)
+			return false;
+		if (!(obj instanceof List))
+			return false;
+
+		return ((List) obj).isEmpty();
+	}
+
+	public int hashCode() {
+		checkRealm();
+		return 1;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/EmptyObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/EmptyObservableSet.java
new file mode 100644
index 0000000..1bd7325
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/EmptyObservableSet.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208332, 146397, 249526
+ *******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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.IDisposeListener;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ISetChangeListener;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Singleton empty set
+ */
+public class EmptyObservableSet implements IObservableSet {
+
+	private static final Set emptySet = Collections.EMPTY_SET;
+
+	private final 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 EmptyObservableSet(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 EmptyObservableSet(Realm realm, Object elementType) {
+		this.realm = realm;
+		this.elementType = elementType;
+		ObservableTracker.observableCreated(this);
+	}
+
+	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 boolean isEmpty() {
+		checkRealm();
+		return true;
+	}
+
+	public boolean 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 boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean containsAll(Collection c) {
+		checkRealm();
+		return c.isEmpty();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean 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 void addDisposeListener(IDisposeListener listener) {
+	}
+
+	public void removeDisposeListener(IDisposeListener listener) {
+	}
+
+	public boolean isStale() {
+		checkRealm();
+		return false;
+	}
+
+	public boolean isDisposed() {
+		return false;
+	}
+
+	public void dispose() {
+	}
+
+	public Realm getRealm() {
+		return realm;
+	}
+
+	public boolean equals(Object obj) {
+		checkRealm();
+		if (obj == this)
+			return true;
+		if (obj == null)
+			return false;
+		if (!(obj instanceof Set))
+			return false;
+
+		return ((Set) obj).isEmpty();
+	}
+
+	public int hashCode() {
+		checkRealm();
+		return 0;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/IStalenessConsumer.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/IStalenessConsumer.java
new file mode 100644
index 0000000..cfe5c61
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/IStalenessConsumer.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+/**
+ * @since 1.0
+ * 
+ */
+public interface IStalenessConsumer {
+	/**
+	 * @param stale
+	 * 
+	 */
+	public void setStale(boolean stale);
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/MapEntryObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/MapEntryObservableValue.java
new file mode 100644
index 0000000..3340eb6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/MapEntryObservableValue.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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 extends AbstractObservableValue {
+	private IObservableMap map;
+	private Object key;
+	private Object valueType;
+
+	private IMapChangeListener changeListener = new IMapChangeListener() {
+		public void handleMapChange(final MapChangeEvent event) {
+			if (event.diff.getAddedKeys().contains(key)) {
+				final Object newValue = event.diff.getNewValue(key);
+				if (newValue != 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 != null) {
+					fireValueChange(Diffs.createValueDiff(oldValue, null));
+				}
+			}
+		}
+	};
+
+	private IStaleListener staleListener = new 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 MapEntryObservableValue(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 boolean isStale() {
+		ObservableTracker.getterCalled(this);
+		return map.isStale();
+	}
+
+	public synchronized void dispose() {
+		if (map != 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);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Queue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Queue.java
new file mode 100644
index 0000000..45c2b1d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Queue.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable;
+
+/**
+ * Created to avoid a dependency on java.util.LinkedList, see bug 205224.
+ * 
+ * @since 1.1
+ * 
+ */
+public class Queue {
+
+	static class Entry {
+		Object object;
+
+		Entry(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 != 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 == null) {
+			throw new IllegalStateException();
+		}
+		first = oldFirst.next;
+		if (first == 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 boolean isEmpty() {
+		return first == null;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/StalenessObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/StalenessObservableValue.java
new file mode 100644
index 0000000..6a07fad
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/StalenessObservableValue.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable;
+
+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 extends AbstractObservableValue {
+
+	private class MyListener implements 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 boolean 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 StalenessObservableValue(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 != null) {
+			tracked.removeChangeListener(listener);
+			tracked.removeStaleListener(listener);
+			tracked = null;
+			listener = null;
+		}
+		super.dispose();
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/StalenessTracker.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/StalenessTracker.java
new file mode 100644
index 0000000..d5c64ca
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/StalenessTracker.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 262269
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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.identity.IdentityMap;
+
+/**
+ * @since 1.0
+ * 
+ */
+public class StalenessTracker {
+
+	private Map staleMap = new IdentityMap();
+
+	private int staleCount = 0;
+
+	private final IStalenessConsumer stalenessConsumer;
+
+	private class ChildListener implements IStaleListener, IChangeListener {
+		public void handleStale(StaleEvent event) {
+			processStalenessChange((IObservable) event.getSource(), true);
+		}
+
+		public void handleChange(ChangeEvent event) {
+			processStalenessChange((IObservable) event.getSource(), true);
+		}
+	}
+
+	private ChildListener childListener = new ChildListener();
+
+	/**
+	 * @param observables
+	 * @param stalenessConsumer 
+	 */
+	public StalenessTracker(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, boolean callback) {
+		boolean oldStale = staleCount > 0;
+		boolean oldChildStale = getOldChildStale(child);
+		boolean newChildStale = child.isStale();
+		if (oldChildStale != newChildStale) {
+			if (oldChildStale) {
+				staleCount--;
+			} else {
+				staleCount++;
+			}
+			staleMap.put(child, newChildStale ? Boolean.TRUE : Boolean.FALSE);
+		}
+		boolean newStale = staleCount > 0;
+		if (callback && (newStale != oldStale)) {
+			stalenessConsumer.setStale(newStale);
+		}
+	}
+
+	/**
+	 * @param child
+	 */
+	private boolean getOldChildStale(IObservable child) {
+		Object oldChildValue = staleMap.get(child);
+		boolean oldChildStale = oldChildValue == null ? false
+				: ((Boolean) oldChildValue).booleanValue();
+		return oldChildStale;
+	}
+
+	/**
+	 * @param observable
+	 */
+	public void addObservable(IObservable observable) {
+		doAddObservable(observable, true);
+	}
+
+	private void doAddObservable(IObservable observable, boolean callback) {
+		processStalenessChange(observable, callback);
+		observable.addChangeListener(childListener);
+		observable.addStaleListener(childListener);
+	}
+
+	/**
+	 * @param observable
+	 */
+	public void removeObservable(IObservable observable) {
+		boolean oldStale = staleCount > 0;
+		boolean oldChildStale = getOldChildStale(observable);
+		if (oldChildStale) {
+			staleCount--;
+		}
+		staleMap.remove(observable);
+		observable.removeChangeListener(childListener);
+		observable.removeStaleListener(childListener);
+		boolean newStale = staleCount > 0;
+		if (newStale != oldStale) {
+			stalenessConsumer.setStale(newStale);
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableList.java
new file mode 100644
index 0000000..6a11d68
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableList.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * 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, 237718
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+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.list.DecoratingObservableList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+
+/**
+ * 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
+ */
+public class UnmodifiableObservableList extends DecoratingObservableList {
+	private List unmodifiableList;
+
+	/**
+	 * @param decorated
+	 */
+	public UnmodifiableObservableList(IObservableList decorated) {
+		super(decorated, false);
+		this.unmodifiableList = Collections.unmodifiableList(decorated);
+	}
+
+	public void add(int index, Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(int index, Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return unmodifiableList.iterator();
+	}
+
+	public ListIterator listIterator() {
+		return listIterator(0);
+	}
+
+	public ListIterator listIterator(int index) {
+		getterCalled();
+		return unmodifiableList.listIterator(index);
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object remove(int index) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object set(int index, Object element) {
+		throw new UnsupportedOperationException();
+	}
+
+	public List subList(int fromIndex, int toIndex) {
+		getterCalled();
+		return unmodifiableList.subList(fromIndex, toIndex);
+	}
+
+	public synchronized void dispose() {
+		unmodifiableList = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableMap.java
new file mode 100644
index 0000000..ac6beb2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableMap.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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 - bug 237718
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.map.DecoratingObservableMap;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+
+/**
+ * IObservableMap implementation that prevents modification by consumers. Events
+ * in the originating wrapped map are propagated and thrown from this instance
+ * when appropriate. All mutators throw an UnsupportedOperationException.
+ * 
+ * @since 1.0
+ */
+public class UnmodifiableObservableMap extends DecoratingObservableMap {
+	Map unmodifiableMap;
+
+	/**
+	 * @param decorated
+	 */
+	public UnmodifiableObservableMap(IObservableMap decorated) {
+		super(decorated, false);
+		this.unmodifiableMap = Collections.unmodifiableMap(decorated);
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	public Set entrySet() {
+		getterCalled();
+		return unmodifiableMap.entrySet();
+	}
+
+	public Set keySet() {
+		getterCalled();
+		return unmodifiableMap.keySet();
+	}
+
+	public Object put(Object key, Object value) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void putAll(Map m) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object remove(Object key) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Collection values() {
+		getterCalled();
+		return unmodifiableMap.values();
+	}
+
+	public synchronized void dispose() {
+		unmodifiableMap = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableSet.java
new file mode 100644
index 0000000..b367c03
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableSet.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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 208332)
+ *     Brad Reynolds - initial API and implementation
+ *         (through UnmodifiableObservableList.java)
+ *     Matthew Hall - bug 237718
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.set.DecoratingObservableSet;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+
+/**
+ * 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 extends DecoratingObservableSet {
+	private Set unmodifiableSet;
+
+	/**
+	 * @param decorated
+	 */
+	public UnmodifiableObservableSet(IObservableSet decorated) {
+		super(decorated, false);
+
+		this.unmodifiableSet = Collections.unmodifiableSet(decorated);
+	}
+
+	public boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return unmodifiableSet.iterator();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public synchronized void dispose() {
+		unmodifiableSet = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.java
new file mode 100644
index 0000000..ae7ed17
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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)
+ *     Matthew Hall - bugs 237884, 237718
+ *     Ovidio Mallo - bug 237163
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+import org.eclipse.core.databinding.observable.value.DecoratingObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * An unmodifiable wrapper class for IObservableValue instances.
+ * @since 1.1
+ */
+public class UnmodifiableObservableValue extends DecoratingObservableValue {
+	/**
+	 * Constructs an UnmodifiableObservableValue which wraps the given
+	 * observable value
+	 * 
+	 * @param wrappedValue
+	 *            the observable value to wrap in an unmodifiable instance.
+	 */
+	public UnmodifiableObservableValue(IObservableValue wrappedValue) {
+		super(wrappedValue, false);
+	}
+
+	public void setValue(Object value) {
+		throw new UnsupportedOperationException();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Util.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Util.java
new file mode 100644
index 0000000..11b7231
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/Util.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable;
+
+/**
+ * @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 boolean equals(final Object left, final Object right) {
+		return left == null ? right == null : ((right != null) && left
+				.equals(right));
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableHelper.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableHelper.java
new file mode 100644
index 0000000..84af2cb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableHelper.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 246782)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.observable.Util;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/* package */class DetailObservableHelper {
+	/* package */static void warnIfDifferentRealms(Realm detailRealm,
+			Realm innerObservableRealm) {
+		if (!Util.equals(detailRealm, innerObservableRealm)) {
+			Throwable throwable = new Throwable();
+			throwable.fillInStackTrace();
+			String message = "Inner observable realm (" + innerObservableRealm //$NON-NLS-1$
+					+ ") not equal to detail realm (" //$NON-NLS-1$
+					+ detailRealm + ")"; //$NON-NLS-1$
+			Policy.getLog().log(
+					new Status(IStatus.WARNING, Policy.JFACE_DATABINDING,
+							message, throwable));
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableList.java
new file mode 100755
index 0000000..f3fc946
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableList.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 247875, 246782, 249526, 268022, 251424
+ *     Ovidio Mallo - bug 241318
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+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.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObserving;
+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.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 extends ObservableList implements IObserving {
+
+	private boolean updating = false;
+
+	private IListChangeListener innerChangeListener = new 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 DetailObservableList(IObservableFactory factory,
+			IObservableValue outerObservableValue, Object detailType) {
+		super(outerObservableValue.getRealm(), Collections.EMPTY_LIST,
+				detailType);
+		Assert.isTrue(!outerObservableValue.isDisposed(),
+				"Master observable is disposed"); //$NON-NLS-1$
+
+		this.factory = factory;
+		this.outerObservableValue = outerObservableValue;
+		this.detailType = detailType;
+
+		outerObservableValue.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				dispose();
+			}
+		});
+
+		ObservableTracker.setIgnore(true);
+		try {
+			updateInnerObservableList();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+		outerObservableValue.addValueChangeListener(outerChangeListener);
+	}
+
+	IValueChangeListener outerChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (isDisposed())
+				return;
+			ObservableTracker.setIgnore(true);
+			try {
+				List oldList = new ArrayList(wrappedList);
+				updateInnerObservableList();
+				fireListChange(Diffs.computeListDiff(oldList, wrappedList));
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+	};
+
+	private void updateInnerObservableList() {
+		if (innerObservableList != null) {
+			innerObservableList.removeListChangeListener(innerChangeListener);
+			innerObservableList.dispose();
+		}
+		currentOuterValue = outerObservableValue.getValue();
+		if (currentOuterValue == null) {
+			innerObservableList = null;
+			wrappedList = Collections.EMPTY_LIST;
+		} else {
+			ObservableTracker.setIgnore(true);
+			try {
+				innerObservableList = (IObservableList) factory
+						.createObservable(currentOuterValue);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+			DetailObservableHelper.warnIfDifferentRealms(getRealm(),
+					innerObservableList.getRealm());
+			wrappedList = innerObservableList;
+
+			if (detailType != 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 boolean add(final Object o) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.add(o);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public void add(final int index, final Object element) {
+		ObservableTracker.setIgnore(true);
+		try {
+			wrappedList.add(index, element);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean remove(final Object o) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.remove(o);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public Object set(final int index, final Object element) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.set(index, element);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public Object move(final int oldIndex, final int newIndex) {
+		if (innerObservableList != null) {
+			ObservableTracker.setIgnore(true);
+			try {
+				return innerObservableList.move(oldIndex, newIndex);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+		return super.move(oldIndex, newIndex);
+	}
+
+	public Object remove(final int index) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.remove(index);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean addAll(final Collection c) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.addAll(c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean addAll(final int index, final Collection c) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.addAll(index, c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean removeAll(final Collection c) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.removeAll(c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean retainAll(final Collection c) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedList.retainAll(c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public void clear() {
+		ObservableTracker.setIgnore(true);
+		try {
+			wrappedList.clear();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+
+		if (outerObservableValue != null) {
+			outerObservableValue.removeValueChangeListener(outerChangeListener);
+		}
+		if (innerObservableList != null) {
+			innerObservableList.removeListChangeListener(innerChangeListener);
+			innerObservableList.dispose();
+		}
+		outerObservableValue = null;
+		outerChangeListener = null;
+		currentOuterValue = null;
+		factory = null;
+		innerObservableList = null;
+		innerChangeListener = null;
+	}
+
+	public Object getObserved() {
+		if (innerObservableList instanceof IObserving) {
+			return ((IObserving) innerObservableList).getObserved();
+		}
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableMap.java
new file mode 100644
index 0000000..fbfc9d3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableMap.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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, 226289, 247875, 246782, 249526, 268022,
+ *                    251424
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+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.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * @since 1.1
+ * 
+ */
+public class DetailObservableMap extends ObservableMap implements IObserving {
+	private boolean updating = false;
+
+	private IObservableValue master;
+	private IObservableFactory detailFactory;
+
+	private IObservableMap detailMap;
+
+	private Object detailKeyType;
+	private Object detailValueType;
+
+	private IValueChangeListener masterChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (isDisposed())
+				return;
+			ObservableTracker.setIgnore(true);
+			try {
+				Map oldMap = new HashMap(wrappedMap);
+				updateDetailMap();
+				fireMapChange(Diffs.computeMapDiff(oldMap, wrappedMap));
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+	};
+
+	private IMapChangeListener detailChangeListener = new 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
+	 * @param keyType
+	 * @param valueType
+	 * 
+	 */
+	public DetailObservableMap(IObservableFactory detailFactory,
+			IObservableValue master, Object keyType, Object valueType) {
+		super(master.getRealm(), Collections.EMPTY_MAP);
+		Assert.isTrue(!master.isDisposed(), "Master observable is disposed"); //$NON-NLS-1$
+
+		this.master = master;
+		this.detailFactory = detailFactory;
+		this.detailKeyType = keyType;
+		this.detailValueType = valueType;
+
+		master.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				dispose();
+			}
+		});
+
+		ObservableTracker.setIgnore(true);
+		try {
+			updateDetailMap();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+		master.addValueChangeListener(masterChangeListener);
+	}
+
+	private void updateDetailMap() {
+		final Object masterValue = master.getValue();
+		if (detailMap != null) {
+			detailMap.removeMapChangeListener(detailChangeListener);
+			detailMap.dispose();
+		}
+
+		if (masterValue == null) {
+			detailMap = null;
+			wrappedMap = Collections.EMPTY_MAP;
+		} else {
+			ObservableTracker.setIgnore(true);
+			try {
+				detailMap = (IObservableMap) detailFactory
+						.createObservable(masterValue);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+			DetailObservableHelper.warnIfDifferentRealms(getRealm(), detailMap
+					.getRealm());
+			wrappedMap = detailMap;
+
+			if (detailKeyType != null) {
+				Object innerKeyType = detailMap.getKeyType();
+
+				Assert.isTrue(detailKeyType.equals(innerKeyType),
+						"Cannot change key type in a nested observable map"); //$NON-NLS-1$
+			}
+
+			if (detailValueType != null) {
+				Object innerValueType = detailMap.getValueType();
+
+				Assert.isTrue(detailValueType.equals(innerValueType),
+						"Cannot change value type in a nested observable map"); //$NON-NLS-1$
+			}
+
+			detailMap.addMapChangeListener(detailChangeListener);
+		}
+	}
+
+	public Object getKeyType() {
+		return detailKeyType;
+	}
+
+	public Object getValueType() {
+		return detailValueType;
+	}
+
+	public Object put(final Object key, final Object value) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return detailMap.put(key, value);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public void putAll(final Map map) {
+		ObservableTracker.setIgnore(true);
+		try {
+			detailMap.putAll(map);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public Object remove(final Object key) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return detailMap.remove(key);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public void clear() {
+		ObservableTracker.setIgnore(true);
+		try {
+			detailMap.clear();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public synchronized void dispose() {
+		if (master != null) {
+			master.removeValueChangeListener(masterChangeListener);
+			master = null;
+			masterChangeListener = null;
+		}
+		detailFactory = null;
+		if (detailMap != null) {
+			detailMap.removeMapChangeListener(detailChangeListener);
+			detailMap.dispose();
+			detailMap = null;
+		}
+		detailChangeListener = null;
+		super.dispose();
+	}
+
+	public Object getObserved() {
+		if (detailMap instanceof IObserving) {
+			return ((IObserving) detailMap).getObserved();
+		}
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableSet.java
new file mode 100755
index 0000000..d218e2f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableSet.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 247875, 246782, 249526, 268022, 251424
+ *     Ovidio Mallo - bug 241318
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+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.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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 extends ObservableSet implements IObserving {
+
+	private boolean updating = false;
+
+	private ISetChangeListener innerChangeListener = new 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 DetailObservableSet(IObservableFactory factory,
+			IObservableValue outerObservableValue, Object detailType) {
+		super(outerObservableValue.getRealm(), Collections.EMPTY_SET,
+				detailType);
+		Assert.isTrue(!outerObservableValue.isDisposed(),
+				"Master observable is disposed"); //$NON-NLS-1$
+
+		this.factory = factory;
+		this.outerObservableValue = outerObservableValue;
+
+		outerObservableValue.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				dispose();
+			}
+		});
+
+		ObservableTracker.setIgnore(true);
+		try {
+			updateInnerObservableSet();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+		outerObservableValue.addValueChangeListener(outerChangeListener);
+	}
+
+	IValueChangeListener outerChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (isDisposed())
+				return;
+			ObservableTracker.setIgnore(true);
+			try {
+				Set oldSet = new HashSet(wrappedSet);
+				updateInnerObservableSet();
+				fireSetChange(Diffs.computeSetDiff(oldSet, wrappedSet));
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+	};
+
+	private void updateInnerObservableSet() {
+		currentOuterValue = outerObservableValue.getValue();
+		if (innerObservableSet != null) {
+			innerObservableSet.removeSetChangeListener(innerChangeListener);
+			innerObservableSet.dispose();
+		}
+		if (currentOuterValue == null) {
+			innerObservableSet = null;
+			wrappedSet = Collections.EMPTY_SET;
+		} else {
+			ObservableTracker.setIgnore(true);
+			try {
+				innerObservableSet = (IObservableSet) factory
+						.createObservable(currentOuterValue);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+			DetailObservableHelper.warnIfDifferentRealms(getRealm(),
+					innerObservableSet.getRealm());
+			wrappedSet = innerObservableSet;
+
+			if (elementType != 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 boolean add(final Object o) {
+		getterCalled();
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedSet.add(o);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean remove(final Object o) {
+		getterCalled();
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedSet.remove(o);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean addAll(final Collection c) {
+		getterCalled();
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedSet.addAll(c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean removeAll(final Collection c) {
+		getterCalled();
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedSet.removeAll(c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public boolean retainAll(final Collection c) {
+		getterCalled();
+		ObservableTracker.setIgnore(true);
+		try {
+			return wrappedSet.retainAll(c);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public void clear() {
+		getterCalled();
+		ObservableTracker.setIgnore(true);
+		try {
+			wrappedSet.clear();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+
+		if (outerObservableValue != null) {
+			outerObservableValue.removeValueChangeListener(outerChangeListener);
+		}
+		if (innerObservableSet != null) {
+			innerObservableSet.removeSetChangeListener(innerChangeListener);
+			innerObservableSet.dispose();
+		}
+		outerObservableValue = null;
+		outerChangeListener = null;
+		currentOuterValue = null;
+		factory = null;
+		innerObservableSet = null;
+		innerChangeListener = null;
+	}
+
+	public Object getObserved() {
+		if (innerObservableSet instanceof IObserving) {
+			return ((IObserving) innerObservableSet).getObserved();
+		}
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableValue.java
new file mode 100755
index 0000000..be548b1
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableValue.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164653, 147515
+ *     Ovidio Mallo - bug 241318
+ *     Matthew Hall - bugs 247875, 246782, 249526, 268022, 251424
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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 extends AbstractObservableValue implements
+		IObserving {
+
+	private boolean updating = false;
+
+	private IValueChangeListener innerChangeListener = new 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 DetailObservableValue(IObservableValue outerObservableValue,
+			IObservableFactory factory, Object detailType) {
+		super(outerObservableValue.getRealm());
+		Assert.isTrue(!outerObservableValue.isDisposed(),
+				"Master observable is disposed"); //$NON-NLS-1$
+
+		this.factory = factory;
+		this.detailType = detailType;
+		this.outerObservableValue = outerObservableValue;
+
+		outerObservableValue.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				dispose();
+			}
+		});
+
+		ObservableTracker.setIgnore(true);
+		try {
+			updateInnerObservableValue();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+		outerObservableValue.addValueChangeListener(outerChangeListener);
+	}
+
+	IValueChangeListener outerChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (isDisposed())
+				return;
+			ObservableTracker.setIgnore(true);
+			try {
+				Object oldValue = doGetValue();
+				updateInnerObservableValue();
+				fireValueChange(Diffs.createValueDiff(oldValue, doGetValue()));
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+	};
+
+	private void updateInnerObservableValue() {
+		currentOuterValue = outerObservableValue.getValue();
+		if (innerObservableValue != null) {
+			innerObservableValue.removeValueChangeListener(innerChangeListener);
+			innerObservableValue.dispose();
+		}
+		if (currentOuterValue == null) {
+			innerObservableValue = null;
+		} else {
+			ObservableTracker.setIgnore(true);
+			try {
+				innerObservableValue = (IObservableValue) factory
+						.createObservable(currentOuterValue);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+			DetailObservableHelper.warnIfDifferentRealms(getRealm(),
+					innerObservableValue.getRealm());
+
+			if (detailType != null) {
+				Object innerValueType = innerObservableValue.getValueType();
+				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(final Object value) {
+		if (innerObservableValue != null) {
+			ObservableTracker.setIgnore(true);
+			try {
+				innerObservableValue.setValue(value);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+	}
+
+	public Object doGetValue() {
+		if (innerObservableValue == null)
+			return null;
+		ObservableTracker.setIgnore(true);
+		try {
+			return innerObservableValue.getValue();
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	public Object getValueType() {
+		return detailType;
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+
+		if (outerObservableValue != null) {
+			outerObservableValue.removeValueChangeListener(outerChangeListener);
+		}
+		if (innerObservableValue != null) {
+			innerObservableValue.removeValueChangeListener(innerChangeListener);
+			innerObservableValue.dispose();
+		}
+		outerObservableValue = null;
+		outerChangeListener = null;
+		currentOuterValue = null;
+		factory = null;
+		innerObservableValue = null;
+		innerChangeListener = null;
+	}
+
+	public Object getObserved() {
+		if (innerObservableValue instanceof IObserving) {
+			return ((IObserving) innerObservableValue).getObserved();
+		}
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java
new file mode 100644
index 0000000..df985d8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java
@@ -0,0 +1,351 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 305367)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.RandomAccess;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObserving;
+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.AbstractObservableList;
+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.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.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+
+/**
+ * @since 1.4
+ */
+public class ListDetailValueObservableList extends AbstractObservableList
+		implements IObserving, RandomAccess {
+
+	private IObservableList masterList;
+
+	private IObservableFactory detailFactory;
+
+	private Object detailType;
+
+	// The list of detail observables.
+	private ArrayList detailList;
+
+	// Maps every master to a DetailEntry containing the detail observable. This
+	// map is used to avoid that multiple detail observables are created for the
+	// same master.
+	private IdentityMap masterDetailMap = new IdentityMap();
+
+	private IdentitySet staleDetailObservables = new IdentitySet();
+
+	private IListChangeListener masterListListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			handleMasterListChange(event.diff);
+		}
+	};
+
+	private IValueChangeListener detailValueListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (!event.getObservable().isStale()) {
+				staleDetailObservables.remove(event.getObservable());
+			}
+			handleDetailValueChange(event);
+		}
+	};
+
+	private IStaleListener masterStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private IStaleListener detailStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			boolean wasStale = isStale();
+			staleDetailObservables.add((staleEvent.getObservable()));
+			if (!wasStale) {
+				fireStale();
+			}
+		}
+	};
+
+	/**
+	 * 
+	 * @param masterList
+	 * @param detailFactory
+	 * @param detailType
+	 */
+	public ListDetailValueObservableList(IObservableList masterList,
+			IObservableFactory detailFactory, Object detailType) {
+		super(masterList.getRealm());
+		this.masterList = masterList;
+		this.detailFactory = detailFactory;
+		this.detailType = detailType;
+		this.detailList = new ArrayList();
+
+		// Add change/stale/dispose listeners on the master list.
+		masterList.addListChangeListener(masterListListener);
+		masterList.addStaleListener(masterStaleListener);
+		masterList.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent event) {
+				ListDetailValueObservableList.this.dispose();
+			}
+		});
+
+		ListDiff initMasterDiff = Diffs.computeListDiff(Collections.EMPTY_LIST,
+				masterList);
+		handleMasterListChange(initMasterDiff);
+	}
+
+	protected synchronized void firstListenerAdded() {
+		for (int i = 0; i < detailList.size(); i++) {
+			IObservableValue detail = (IObservableValue) detailList.get(i);
+			detail.addValueChangeListener(detailValueListener);
+			detail.addStaleListener(detailStaleListener);
+			if (detail.isStale()) {
+				staleDetailObservables.add(detail);
+			}
+		}
+	}
+
+	protected synchronized void lastListenerRemoved() {
+		if (isDisposed()) {
+			return;
+		}
+
+		for (int i = 0; i < detailList.size(); i++) {
+			IObservableValue detail = (IObservableValue) detailList.get(i);
+			detail.removeValueChangeListener(detailValueListener);
+			detail.removeStaleListener(detailStaleListener);
+		}
+		staleDetailObservables.clear();
+	}
+
+	private void handleMasterListChange(ListDiff masterListDiff) {
+		boolean wasStale = isStale();
+
+		boolean hasListeners = hasListeners();
+		ListDiffEntry[] masterEntries = masterListDiff.getDifferences();
+		ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
+		for (int i = 0; i < masterEntries.length; i++) {
+			ListDiffEntry masterEntry = masterEntries[i];
+			int index = masterEntry.getPosition();
+
+			Object masterElement = masterEntry.getElement();
+			Object detailValue;
+			if (masterEntry.isAddition()) {
+				detailValue = addDetailObservable(masterElement, index);
+			} else {
+				detailValue = removeDetailObservable(masterElement, index);
+			}
+
+			if (hasListeners) {
+				// Create the corresponding diff for the detail list.
+				detailEntries[i] = Diffs.createListDiffEntry(index,
+						masterEntry.isAddition(), detailValue);
+			}
+		}
+
+		if (hasListeners) {
+			if (!wasStale && isStale()) {
+				fireStale();
+			}
+
+			// Fire a list change event with the adapted diff.
+			fireListChange(Diffs.createListDiff(detailEntries));
+		}
+	}
+
+	private Object addDetailObservable(Object masterElement, int index) {
+		DetailEntry detailEntry = (DetailEntry) masterDetailMap
+				.get(masterElement);
+		if (detailEntry != null) {
+			// If we already have a detail observable for the given
+			// masterElement, we increment the reference count.
+			detailEntry.masterReferenceCount++;
+			detailList.add(index, detailEntry.detailObservable);
+			return detailEntry.detailObservable.getValue();
+		}
+
+		IObservableValue detail = createDetailObservable(masterElement);
+		masterDetailMap.put(masterElement, new DetailEntry(detail));
+
+		detailList.add(index, detail);
+
+		if (hasListeners()) {
+			detail.addValueChangeListener(detailValueListener);
+			detail.addStaleListener(detailStaleListener);
+			if (detail.isStale()) {
+				staleDetailObservables.add(detail);
+			}
+		}
+
+		return detail.getValue();
+	}
+
+	private Object removeDetailObservable(Object masterElement, int index) {
+		IObservableValue detail = (IObservableValue) detailList.remove(index);
+		Object detailValue = detail.getValue();
+
+		DetailEntry detailEntry = (DetailEntry) masterDetailMap
+				.get(masterElement);
+
+		// We may only dispose the detail observable ASA there are no more
+		// masters referencing it.
+		detailEntry.masterReferenceCount--;
+		if (detailEntry.masterReferenceCount == 0) {
+			masterDetailMap.remove(masterElement);
+			staleDetailObservables.remove(detail);
+			detail.dispose();
+		}
+
+		return detailValue;
+	}
+
+	private void handleDetailValueChange(ValueChangeEvent event) {
+		IObservableValue detail = event.getObservableValue();
+
+		// When we get a change event on a detail observable, we must find its
+		// position while there may also be duplicate entries.
+		BitSet detailIndexes = new BitSet();
+		for (int i = 0; i < detailList.size(); i++) {
+			if (detailList.get(i) == detail) {
+				detailIndexes.set(i);
+			}
+		}
+
+		// Create the diff for every found position.
+		Object oldValue = event.diff.getOldValue();
+		Object newValue = event.diff.getNewValue();
+		ListDiffEntry[] diffEntries = new ListDiffEntry[2 * detailIndexes
+				.cardinality()];
+		int diffIndex = 0;
+		for (int b = detailIndexes.nextSetBit(0); b != -1; b = detailIndexes
+				.nextSetBit(b + 1)) {
+			diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, false,
+					oldValue);
+			diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, true,
+					newValue);
+		}
+		fireListChange(Diffs.createListDiff(diffEntries));
+	}
+
+	private IObservableValue createDetailObservable(Object masterElement) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return (IObservableValue) detailFactory
+					.createObservable(masterElement);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	protected int doGetSize() {
+		return detailList.size();
+	}
+
+	public Object get(int index) {
+		ObservableTracker.getterCalled(this);
+		return ((IObservableValue) detailList.get(index)).getValue();
+	}
+
+	public Object set(int index, Object element) {
+		IObservableValue detail = (IObservableValue) detailList.get(index);
+		Object oldElement = detail.getValue();
+		detail.setValue(element);
+		return oldElement;
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object getElementType() {
+		return detailType;
+	}
+
+	public boolean isStale() {
+		return super.isStale()
+				|| (masterList != null && masterList.isStale())
+				|| (staleDetailObservables != null && !staleDetailObservables
+						.isEmpty());
+	}
+
+	public Object getObserved() {
+		return masterList;
+	}
+
+	public synchronized void dispose() {
+		if (masterList != null) {
+			masterList.removeListChangeListener(masterListListener);
+			masterList.removeStaleListener(masterStaleListener);
+		}
+
+		if (detailList != null) {
+			for (Iterator iter = detailList.iterator(); iter.hasNext();) {
+				IObservableValue detailValue = (IObservableValue) iter.next();
+				detailValue.dispose();
+			}
+			detailList.clear();
+		}
+
+		masterList = null;
+		detailFactory = null;
+		detailType = null;
+		masterListListener = null;
+		detailValueListener = null;
+		masterDetailMap = null;
+		staleDetailObservables = null;
+
+		super.dispose();
+	}
+
+	private static final class DetailEntry {
+
+		private final IObservableValue detailObservable;
+
+		private int masterReferenceCount = 1;
+
+		public DetailEntry(IObservableValue detailObservable) {
+			this.detailObservable = detailObservable;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java
new file mode 100644
index 0000000..fc3cec1
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/MapDetailValueObservableMap.java
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 305367)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+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.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObserving;
+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.AbstractObservableMap;
+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.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.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.core.internal.databinding.observable.Util;
+
+/**
+ * @since 1.4
+ */
+public class MapDetailValueObservableMap extends AbstractObservableMap
+		implements IObserving {
+
+	private IObservableMap masterMap;
+
+	private IObservableFactory observableValueFactory;
+
+	private Object detailValueType;
+
+	private Set entrySet;
+
+	private IdentityHashMap keyDetailMap = new IdentityHashMap();
+
+	private IdentitySet staleDetailObservables = new IdentitySet();
+
+	private IMapChangeListener masterMapListener = new IMapChangeListener() {
+		public void handleMapChange(MapChangeEvent event) {
+			handleMasterMapChange(event.diff);
+		}
+	};
+
+	private IStaleListener masterStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private IStaleListener detailStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			addStaleDetailObservable((IObservableValue) staleEvent
+					.getObservable());
+		}
+	};
+
+	/**
+	 * @param masterMap
+	 * @param observableValueFactory
+	 * @param detailValueType
+	 */
+	public MapDetailValueObservableMap(IObservableMap masterMap,
+			IObservableFactory observableValueFactory, Object detailValueType) {
+		super(masterMap.getRealm());
+		this.masterMap = masterMap;
+		this.observableValueFactory = observableValueFactory;
+		this.detailValueType = detailValueType;
+
+		// Add change/stale/dispose listeners on the master map.
+		masterMap.addMapChangeListener(masterMapListener);
+		masterMap.addStaleListener(masterStaleListener);
+		masterMap.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent event) {
+				MapDetailValueObservableMap.this.dispose();
+			}
+		});
+
+		// Initialize the map with the current state of the master map.
+		MapDiff initMasterDiff = Diffs.computeMapDiff(Collections.EMPTY_MAP,
+				masterMap);
+		handleMasterMapChange(initMasterDiff);
+	}
+
+	private void handleMasterMapChange(MapDiff diff) {
+		// Collect the detail values for the master values in the input diff.
+		IdentityMap oldValues = new IdentityMap();
+		IdentityMap newValues = new IdentityMap();
+
+		// Handle added master values.
+		Set addedKeys = diff.getAddedKeys();
+		for (Iterator iter = addedKeys.iterator(); iter.hasNext();) {
+			Object addedKey = iter.next();
+
+			// For added master values, we set up a new detail observable.
+			addDetailObservable(addedKey);
+
+			// Get the value of the created detail observable for the new diff.
+			IObservableValue detailValue = getDetailObservableValue(addedKey);
+			newValues.put(addedKey, detailValue.getValue());
+		}
+
+		// Handle removed master values.
+		Set removedKeys = diff.getRemovedKeys();
+		for (Iterator iter = removedKeys.iterator(); iter.hasNext();) {
+			Object removedKey = iter.next();
+
+			// First of all, get the current detail value and add it to the set
+			// of old values of the new diff.
+			IObservableValue detailValue = getDetailObservableValue(removedKey);
+			oldValues.put(removedKey, detailValue.getValue());
+
+			// For removed master values, we dispose the detail observable.
+			removeDetailObservable(removedKey);
+		}
+
+		// Handle changed master values.
+		Set changedKeys = diff.getChangedKeys();
+		for (Iterator iter = changedKeys.iterator(); iter.hasNext();) {
+			Object changedKey = iter.next();
+
+			// Get the detail value prior to the change and add it to the set of
+			// old values of the new diff.
+			IObservableValue oldDetailValue = getDetailObservableValue(changedKey);
+			oldValues.put(changedKey, oldDetailValue.getValue());
+
+			// Remove the old detail value for the old master value and add it
+			// again for the new master value.
+			removeDetailObservable(changedKey);
+			addDetailObservable(changedKey);
+
+			// Get the new detail value and add it to the set of new values.
+			IObservableValue newDetailValue = getDetailObservableValue(changedKey);
+			newValues.put(changedKey, newDetailValue.getValue());
+		}
+
+		// The different key sets are the same, only the values change.
+		fireMapChange(Diffs.createMapDiff(addedKeys, removedKeys, changedKeys,
+				oldValues, newValues));
+	}
+
+	private void addDetailObservable(final Object addedKey) {
+		Object masterElement = masterMap.get(addedKey);
+
+		IObservableValue detailValue = (IObservableValue) keyDetailMap
+				.get(addedKey);
+
+		if (detailValue == null) {
+			detailValue = createDetailObservable(masterElement);
+
+			keyDetailMap.put(addedKey, detailValue);
+
+			detailValue.addValueChangeListener(new IValueChangeListener() {
+				public void handleValueChange(ValueChangeEvent event) {
+					if (!event.getObservableValue().isStale()) {
+						staleDetailObservables.remove(event.getSource());
+					}
+
+					fireMapChange(Diffs.createMapDiffSingleChange(addedKey,
+							event.diff.getOldValue(), event.diff.getNewValue()));
+				}
+			});
+
+			if (detailValue.isStale()) {
+				addStaleDetailObservable(detailValue);
+			}
+		}
+
+		detailValue.addStaleListener(detailStaleListener);
+	}
+
+	private IObservableValue createDetailObservable(Object masterElement) {
+		ObservableTracker.setIgnore(true);
+		try {
+			return (IObservableValue) observableValueFactory
+					.createObservable(masterElement);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	private void removeDetailObservable(Object removedKey) {
+		if (isDisposed()) {
+			return;
+		}
+
+		IObservableValue detailValue = (IObservableValue) keyDetailMap
+				.remove(removedKey);
+		staleDetailObservables.remove(detailValue);
+		detailValue.dispose();
+	}
+
+	private IObservableValue getDetailObservableValue(Object masterKey) {
+		return (IObservableValue) keyDetailMap.get(masterKey);
+	}
+
+	private void addStaleDetailObservable(IObservableValue detailObservable) {
+		boolean wasStale = isStale();
+		staleDetailObservables.add(detailObservable);
+		if (!wasStale) {
+			fireStale();
+		}
+	}
+
+	public Set keySet() {
+		getterCalled();
+
+		return masterMap.keySet();
+	}
+
+	public Object get(Object key) {
+		getterCalled();
+
+		if (!containsKey(key)) {
+			return null;
+		}
+
+		IObservableValue detailValue = getDetailObservableValue(key);
+		return detailValue.getValue();
+	}
+
+	public Object put(Object key, Object value) {
+		if (!containsKey(key)) {
+			return null;
+		}
+
+		IObservableValue detailValue = getDetailObservableValue(key);
+		Object oldValue = detailValue.getValue();
+		detailValue.setValue(value);
+		return oldValue;
+	}
+
+	public boolean containsKey(Object key) {
+		getterCalled();
+
+		return masterMap.containsKey(key);
+	}
+
+	public Object remove(Object key) {
+		checkRealm();
+
+		if (!containsKey(key)) {
+			return null;
+		}
+
+		IObservableValue detailValue = getDetailObservableValue(key);
+		Object oldValue = detailValue.getValue();
+
+		masterMap.remove(key);
+
+		return oldValue;
+	}
+
+	public int size() {
+		getterCalled();
+
+		return masterMap.size();
+	}
+
+	public boolean isStale() {
+		return super.isStale()
+				|| (masterMap != null && masterMap.isStale())
+				|| (staleDetailObservables != null && !staleDetailObservables
+						.isEmpty());
+	}
+
+	public Object getKeyType() {
+		return masterMap.getKeyType();
+	}
+
+	public Object getValueType() {
+		return detailValueType;
+	}
+
+	public Object getObserved() {
+		return masterMap;
+	}
+
+	public synchronized void dispose() {
+		if (masterMap != null) {
+			masterMap.removeMapChangeListener(masterMapListener);
+			masterMap.removeStaleListener(masterStaleListener);
+		}
+
+		if (keyDetailMap != null) {
+			for (Iterator iter = keyDetailMap.values().iterator(); iter
+					.hasNext();) {
+				IObservableValue detailValue = (IObservableValue) iter.next();
+				detailValue.dispose();
+			}
+			keyDetailMap.clear();
+		}
+
+		masterMap = null;
+		observableValueFactory = null;
+		detailValueType = null;
+		keyDetailMap = null;
+		masterStaleListener = null;
+		detailStaleListener = null;
+		staleDetailObservables = null;
+
+		super.dispose();
+	}
+
+	public Set entrySet() {
+		getterCalled();
+
+		if (entrySet == null) {
+			entrySet = new EntrySet();
+		}
+		return entrySet;
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	private class EntrySet extends AbstractSet {
+
+		public Iterator iterator() {
+			final Iterator keyIterator = keySet().iterator();
+			return new Iterator() {
+
+				public boolean hasNext() {
+					return keyIterator.hasNext();
+				}
+
+				public Object next() {
+					Object key = keyIterator.next();
+					return new MapEntry(key);
+				}
+
+				public void remove() {
+					keyIterator.remove();
+				}
+			};
+		}
+
+		public int size() {
+			return MapDetailValueObservableMap.this.size();
+		}
+	}
+
+	private final class MapEntry implements Map.Entry {
+
+		private final Object key;
+
+		private MapEntry(Object key) {
+			this.key = key;
+		}
+
+		public Object getKey() {
+			MapDetailValueObservableMap.this.getterCalled();
+			return key;
+		}
+
+		public Object getValue() {
+			return MapDetailValueObservableMap.this.get(getKey());
+		}
+
+		public Object setValue(Object value) {
+			return MapDetailValueObservableMap.this.put(getKey(), value);
+		}
+
+		public boolean equals(Object o) {
+			MapDetailValueObservableMap.this.getterCalled();
+			if (o == this)
+				return true;
+			if (o == null)
+				return false;
+			if (!(o instanceof Map.Entry))
+				return false;
+			Map.Entry that = (Map.Entry) o;
+			return Util.equals(this.getKey(), that.getKey())
+					&& Util.equals(this.getValue(), that.getValue());
+		}
+
+		public int hashCode() {
+			MapDetailValueObservableMap.this.getterCalled();
+			Object value = getValue();
+			return (getKey() == null ? 0 : getKey().hashCode())
+					^ (value == null ? 0 : value.hashCode());
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java
new file mode 100644
index 0000000..f66a1c9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 305367)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.IObserving;
+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.ComputedObservableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+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.internal.databinding.identity.IdentitySet;
+
+/**
+ * @since 1.4
+ */
+public class SetDetailValueObservableMap extends ComputedObservableMap
+		implements IObserving {
+
+	private IObservableFactory observableValueFactory;
+
+	private Map detailObservableValueMap = new HashMap();
+
+	private IdentitySet staleDetailObservables = new IdentitySet();
+
+	private IStaleListener detailStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			addStaleDetailObservable((IObservableValue) staleEvent
+					.getObservable());
+		}
+	};
+
+	/**
+	 * @param masterKeySet
+	 * @param observableValueFactory
+	 * @param detailValueType
+	 */
+	public SetDetailValueObservableMap(IObservableSet masterKeySet,
+			IObservableFactory observableValueFactory, Object detailValueType) {
+		super(masterKeySet, detailValueType);
+		this.observableValueFactory = observableValueFactory;
+	}
+
+	protected void hookListener(final Object addedKey) {
+		final IObservableValue detailValue = getDetailObservableValue(addedKey);
+
+		detailValue.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				if (!event.getObservableValue().isStale()) {
+					staleDetailObservables.remove(detailValue);
+				}
+
+				fireSingleChange(addedKey, event.diff.getOldValue(),
+						event.diff.getNewValue());
+			}
+		});
+
+		detailValue.addStaleListener(detailStaleListener);
+	}
+
+	protected void unhookListener(Object removedKey) {
+		if (isDisposed()) {
+			return;
+		}
+
+		IObservableValue detailValue = (IObservableValue) detailObservableValueMap
+				.remove(removedKey);
+		staleDetailObservables.remove(detailValue);
+		detailValue.dispose();
+	}
+
+	private IObservableValue getDetailObservableValue(Object masterKey) {
+		IObservableValue detailValue = (IObservableValue) detailObservableValueMap
+				.get(masterKey);
+
+		if (detailValue == null) {
+			ObservableTracker.setIgnore(true);
+			try {
+				detailValue = (IObservableValue) observableValueFactory
+						.createObservable(masterKey);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+
+			detailObservableValueMap.put(masterKey, detailValue);
+
+			if (detailValue.isStale()) {
+				addStaleDetailObservable(detailValue);
+			}
+		}
+
+		return detailValue;
+	}
+
+	private void addStaleDetailObservable(IObservableValue detailObservable) {
+		boolean wasStale = isStale();
+		staleDetailObservables.add(detailObservable);
+		if (!wasStale) {
+			fireStale();
+		}
+	}
+
+	protected Object doGet(Object key) {
+		IObservableValue detailValue = getDetailObservableValue(key);
+		return detailValue.getValue();
+	}
+
+	protected Object doPut(Object key, Object value) {
+		IObservableValue detailValue = getDetailObservableValue(key);
+		Object oldValue = detailValue.getValue();
+		detailValue.setValue(value);
+		return oldValue;
+	}
+
+	public boolean containsKey(Object key) {
+		getterCalled();
+
+		return keySet().contains(key);
+	}
+
+	public Object remove(Object key) {
+		checkRealm();
+
+		if (!containsKey(key)) {
+			return null;
+		}
+
+		IObservableValue detailValue = getDetailObservableValue(key);
+		Object oldValue = detailValue.getValue();
+
+		keySet().remove(key);
+
+		return oldValue;
+	}
+
+	public int size() {
+		getterCalled();
+
+		return keySet().size();
+	}
+
+	public boolean isStale() {
+		return super.isStale() || staleDetailObservables != null
+				&& !staleDetailObservables.isEmpty();
+	}
+
+	public Object getObserved() {
+		return keySet();
+	}
+
+	public synchronized void dispose() {
+		super.dispose();
+
+		observableValueFactory = null;
+		detailObservableValueMap = null;
+		detailStaleListener = null;
+		staleDetailObservables = null;
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/.classpath b/bundles/org.eclipse.core.databinding.property/.classpath
new file mode 100644
index 0000000..6f3b481
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.core.databinding.property/.cvsignore b/bundles/org.eclipse.core.databinding.property/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.core.databinding.property/.project b/bundles/org.eclipse.core.databinding.property/.project
new file mode 100644
index 0000000..e0533ed
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.core.databinding.property</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..9e52c97
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,354 @@
+#Thu Feb 05 11:35:38 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..f590c4e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:05:48 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.3\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.pde.api.tools.prefs b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..60843cb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,145 @@
+#Wed Apr 02 17:10:39 EDT 2008
+ANNOTATION_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITH_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_LEAK=Warning
+API_PROFILE_ELEMENT_TYPE_REMOVED_API_COMPONENT=Error
+CLASS_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERCLASS_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_CHANGED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+CLASS_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+ENUM_ELEMENT_TYPE_ADDED_FIELD=Error
+ENUM_ELEMENT_TYPE_ADDED_METHOD=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+ENUM_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Ignore
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENTS=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETERS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+METHOD_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
diff --git a/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.pde.prefs b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..4a56680
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:49:44 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=0
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=0
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.core.databinding.property/.settings/org.moreunit.prefs b/bundles/org.eclipse.core.databinding.property/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..28e5d12
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/.settings/org.moreunit.prefs
@@ -0,0 +1,5 @@
+#Tue Feb 02 23:24:09 MST 2010
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=org.eclipse.core.databinding.property\:src\:org.eclipse.jface.tests.databinding\:src
+org.moreunit.useprojectsettings=true
diff --git a/bundles/org.eclipse.core.databinding.property/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.databinding.property/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..11b5608
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/META-INF/MANIFEST.MF
@@ -0,0 +1,23 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.core.databinding.property
+Bundle-Version: 1.4.0.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.core.databinding.property,
+ org.eclipse.core.databinding.property.list,
+ org.eclipse.core.databinding.property.map,
+ org.eclipse.core.databinding.property.set,
+ org.eclipse.core.databinding.property.value,
+ org.eclipse.core.internal.databinding.property;x-internal:=true,
+ org.eclipse.core.internal.databinding.property.list;x-internal:=true,
+ org.eclipse.core.internal.databinding.property.map;x-internal:=true,
+ org.eclipse.core.internal.databinding.property.set;x-internal:=true,
+ org.eclipse.core.internal.databinding.property.value;x-internal:=true
+Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.databinding.observable;bundle-version="[1.3.0,2.0.0)"
+Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,
+ J2SE-1.4
+Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.core.databinding.property/about.html b/bundles/org.eclipse.core.databinding.property/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/build.properties b/bundles/org.eclipse.core.databinding.property/build.properties
new file mode 100644
index 0000000..f09f95b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+src.includes = about.html
diff --git a/bundles/org.eclipse.core.databinding.property/plugin.properties b/bundles/org.eclipse.core.databinding.property/plugin.properties
new file mode 100644
index 0000000..8d13557
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2000, 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+pluginName = JFace Data Binding
+providerName = Eclipse.org
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/INativePropertyListener.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/INativePropertyListener.java
new file mode 100644
index 0000000..a46b83c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/INativePropertyListener.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 265561, 278311
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+import org.eclipse.core.databinding.property.map.SimpleMapProperty;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * A listener capable of adding or removing itself as a listener on a source
+ * object using the source's "native" listener API. Events received from the
+ * source objects are parlayed to the {@link ISimplePropertyListener} provided
+ * to the method that constructed this native listener instance.
+ * 
+ * @since 1.2
+ * @see NativePropertyListener
+ * @see SimpleValueProperty#adaptListener(ISimplePropertyListener)
+ * @see SimpleListProperty#adaptListener(ISimplePropertyListener)
+ * @see SimpleSetProperty#adaptListener(ISimplePropertyListener)
+ * @see SimpleMapProperty#adaptListener(ISimplePropertyListener)
+ */
+public interface INativePropertyListener {
+	/**
+	 * Adds the receiver as a listener for property events on the specified
+	 * property source.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public void addTo(Object source);
+
+	/**
+	 * Removes the receiver as a listener for property events on the specified
+	 * property source.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public void removeFrom(Object source);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/IProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/IProperty.java
new file mode 100644
index 0000000..7604cc0
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/IProperty.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+/**
+ * Marker interface for all property types in the properties framework.
+ * 
+ * @since 1.2
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IProperty {
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/IPropertyObservable.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/IPropertyObservable.java
new file mode 100644
index 0000000..e13084a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/IPropertyObservable.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+import org.eclipse.core.databinding.observable.IObserving;
+
+/**
+ * Provides access to the details of property observables
+ * 
+ * @since 1.2
+ */
+public interface IPropertyObservable extends IObserving {
+	/**
+	 * Returns the property being observed
+	 * 
+	 * @return the property being observed
+	 */
+	IProperty getProperty();
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/ISimplePropertyListener.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/ISimplePropertyListener.java
new file mode 100644
index 0000000..1ca171d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/ISimplePropertyListener.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 194734, 262287
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+/**
+ * Listener for changes to properties on a particular source object
+ * 
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @since 1.2
+ */
+public interface ISimplePropertyListener {
+	/**
+	 * Handle the described property event.
+	 * 
+	 * @param event
+	 *            the event which occured
+	 */
+	public void handleEvent(SimplePropertyEvent event);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/NativePropertyListener.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/NativePropertyListener.java
new file mode 100644
index 0000000..f0208ce
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/NativePropertyListener.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 (bugs 265561, 262287)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+import org.eclipse.core.databinding.observable.IDiff;
+
+/**
+ * Abstract INativePropertyListener implementation
+ * 
+ * @since 1.2
+ */
+public abstract class NativePropertyListener implements INativePropertyListener {
+	private final IProperty property;
+	private final ISimplePropertyListener listener;
+
+	/**
+	 * Constructs a NativePropertyListener with the specified arguments
+	 * 
+	 * @param property
+	 *            the property that this listener listens to
+	 * @param listener
+	 *            the listener to receive property change notifications
+	 */
+	public NativePropertyListener(IProperty property,
+			ISimplePropertyListener listener) {
+		this.property = property;
+		this.listener = listener;
+	}
+
+	public final void addTo(Object source) {
+		if (source != null)
+			doAddTo(source);
+	}
+
+	protected abstract void doAddTo(Object source);
+
+	public final void removeFrom(Object source) {
+		if (source != null)
+			doRemoveFrom(source);
+	}
+
+	protected abstract void doRemoveFrom(Object source);
+
+	/**
+	 * Notifies the listener that a property change occured on the source
+	 * object.
+	 * 
+	 * @param source
+	 *            the source object whose property changed
+	 * @param diff
+	 *            a diff describing the change in state
+	 */
+	protected void fireChange(Object source, IDiff diff) {
+		listener.handleEvent(new SimplePropertyEvent(
+				SimplePropertyEvent.CHANGE, source, property, diff));
+	}
+
+	/**
+	 * Notifies the listener that the property became stale on the source
+	 * object.
+	 * 
+	 * @param source
+	 *            the source object whose property became stale
+	 */
+	protected void fireStale(Object source) {
+		listener.handleEvent(new SimplePropertyEvent(SimplePropertyEvent.STALE,
+				source, property, null));
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/Properties.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/Properties.java
new file mode 100644
index 0000000..e5ed604
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/Properties.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 195222, 263868, 264954
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Realm;
+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.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.property.list.SelfListProperty;
+import org.eclipse.core.internal.databinding.property.map.SelfMapProperty;
+import org.eclipse.core.internal.databinding.property.set.SelfSetProperty;
+import org.eclipse.core.internal.databinding.property.value.ObservableValueProperty;
+import org.eclipse.core.internal.databinding.property.value.SelfValueProperty;
+
+/**
+ * Contains static methods to operate on or return IProperty objects.
+ * 
+ * @since 1.2
+ */
+public class Properties {
+	/**
+	 * Returns an array of observable maps where each map observes the
+	 * corresponding value property on all elements in the given domain set, for
+	 * each property in the given array.
+	 * 
+	 * @param domainSet
+	 *            the set of elements whose properties will be observed
+	 * @param properties
+	 *            array of value properties to observe on each element in the
+	 *            domain set.
+	 * @return an array of observable maps where each map observes the
+	 *         corresponding value property of the given domain set.
+	 */
+	public static IObservableMap[] observeEach(IObservableSet domainSet,
+			IValueProperty[] properties) {
+		IObservableMap[] maps = new IObservableMap[properties.length];
+		for (int i = 0; i < maps.length; i++)
+			maps[i] = properties[i].observeDetail(domainSet);
+		return maps;
+	}
+
+	/**
+	 * Returns an array of observable maps where each maps observes the
+	 * corresponding value property on all elements in the given domain map's
+	 * {@link Map#values() values} collection, for each property in the given
+	 * array.
+	 * 
+	 * @param domainMap
+	 *            the map of elements whose properties will be observed
+	 * @param properties
+	 *            array of value properties to observe on each element in the
+	 *            domain map's {@link Map#values() values} collection.
+	 * @return an array of observable maps where each maps observes the
+	 *         corresponding value property on all elements in the given domain
+	 *         map's {@link Map#values() values} collection, for each property
+	 *         in the given array.
+	 */
+	public static IObservableMap[] observeEach(IObservableMap domainMap,
+			IValueProperty[] properties) {
+		IObservableMap[] maps = new IObservableMap[properties.length];
+		for (int i = 0; i < maps.length; i++)
+			maps[i] = properties[i].observeDetail(domainMap);
+		return maps;
+	}
+
+	/**
+	 * Returns a value property which takes the source object itself as the
+	 * property value. This property may be used to wrap an object in an
+	 * unmodifiable {@link IObservableValue}.
+	 * 
+	 * @param valueType
+	 *            the value type of the property
+	 * @return a value property which takes the source object itself as the
+	 *         property value.
+	 */
+	public static IValueProperty selfValue(Object valueType) {
+		return new SelfValueProperty(valueType);
+	}
+
+	/**
+	 * Returns a list property which takes the source object (a {@link List}) as
+	 * the property list. This property may be used to wrap an arbitrary List
+	 * instance in an {@link IObservableList}.
+	 * 
+	 * @param elementType
+	 *            the element type of the property
+	 * @return a list property which takes the source object (a {@link List}) as
+	 *         the property list.
+	 */
+	public static IListProperty selfList(Object elementType) {
+		return new SelfListProperty(elementType);
+	}
+
+	/**
+	 * Returns a set property which takes the source object (a {@link Set}) as
+	 * the property set. This property may be used to wrap an arbitrary Set
+	 * instance in an {@link IObservableSet}.
+	 * 
+	 * @param elementType
+	 *            the element type of the property
+	 * @return a set property which takes the source object (a {@link Set}) as
+	 *         the property set.
+	 */
+	public static ISetProperty selfSet(Object elementType) {
+		return new SelfSetProperty(elementType);
+	}
+
+	/**
+	 * Returns a map property which takes the source object (a {@link Map}) as
+	 * the property map. This property may be used to wrap an arbitrary Map
+	 * instance in an {@link IObservableMap}.
+	 * 
+	 * @param keyType
+	 *            the key type of the property
+	 * @param valueType
+	 *            the value type of the property
+	 * @return a map property which takes the source object (a {@link Map} as
+	 *         the property map.
+	 */
+	public static IMapProperty selfMap(Object keyType, Object valueType) {
+		return new SelfMapProperty(keyType, valueType);
+	}
+
+	/**
+	 * Returns a value property which observes the value of an
+	 * {@link IObservableValue}. This property may be used e.g. for observing
+	 * the respective values of an {@link IObservableList} &lt;
+	 * {@link IObservableValue} &gt;.
+	 * <p>
+	 * Calls to {@link IValueProperty#observe(Object)} or
+	 * {@link IValueProperty#observe(Realm, Object)} just cast the argument to
+	 * {@link IObservableValue} and return it (the realm argument is ignored).
+	 * 
+	 * @param valueType
+	 *            the value type of the property
+	 * @return a value property which observes the value of an
+	 *         {@link IObservableValue}.
+	 */
+	public static IValueProperty observableValue(Object valueType) {
+		return new ObservableValueProperty(valueType);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/SimplePropertyEvent.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/SimplePropertyEvent.java
new file mode 100644
index 0000000..f811d2b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/SimplePropertyEvent.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262287
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property;
+
+import java.util.EventObject;
+
+import org.eclipse.core.databinding.observable.IDiff;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * Event object events in the properties API
+ * 
+ * @since 1.2
+ */
+public final class SimplePropertyEvent extends EventObject {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Event type constant indicating that the property changed
+	 */
+	public static final int CHANGE = notInlined(1);
+
+	/**
+	 * Event type constant indicating that the property became stale
+	 */
+	public static final int STALE = notInlined(2);
+
+	private static int notInlined(int i) {
+		return i;
+	}
+
+	/**
+	 * The type of property event that occured
+	 */
+	public final int type;
+
+	/**
+	 * The property on which the event took place
+	 */
+	public final IProperty property;
+
+	/**
+	 * If event == CHANGE, a diff object describing the change in state, or null
+	 * for an unknown change.
+	 */
+	public final IDiff diff;
+
+	/**
+	 * Constructs a PropertyChangeEvent with the given attributes
+	 * 
+	 * @param type
+	 *            the property type
+	 * @param source
+	 *            the property source
+	 * @param property
+	 *            the property that changed on the source
+	 * @param diff
+	 *            a diff describing the change in state, or null if the change
+	 *            is unknown or not applicable.
+	 */
+	public SimplePropertyEvent(int type, Object source, IProperty property,
+			IDiff diff) {
+		super(source);
+		this.type = type;
+		this.property = property;
+		this.diff = diff;
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+
+		SimplePropertyEvent that = (SimplePropertyEvent) obj;
+		return Util.equals(getSource(), that.getSource())
+				&& Util.equals(this.property, that.property)
+				&& Util.equals(this.diff, that.diff);
+	}
+
+	public int hashCode() {
+		int hash = 17;
+		hash = hash * 37 + getSource().hashCode();
+		hash = hash * 37 + property.hashCode();
+		hash = hash * 37 + (diff == null ? 0 : diff.hashCode());
+		return hash;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/DelegatingListProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/DelegatingListProperty.java
new file mode 100644
index 0000000..103ab46
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/DelegatingListProperty.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 247997)
+ *     Matthew Hall - bug 264306
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+
+/**
+ * @since 1.2
+ * 
+ */
+public abstract class DelegatingListProperty extends ListProperty {
+	private final IListProperty nullProperty;
+	private final Object elementType;
+
+	protected DelegatingListProperty() {
+		this(null);
+	}
+
+	protected DelegatingListProperty(Object elementType) {
+		this.elementType = elementType;
+		this.nullProperty = new NullListProperty();
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Repeated calls to this method with the same source object returns the
+	 * same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return the property to delegate to for the specified source object.
+	 */
+	public final IListProperty getDelegate(Object source) {
+		if (source == null)
+			return nullProperty;
+		IListProperty delegate = doGetDelegate(source);
+		if (delegate == null)
+			delegate = nullProperty;
+		return delegate;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Implementers must ensure that repeated calls to this method with the same
+	 * source object returns the same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return the property to delegate to for the specified source object.
+	 */
+	protected abstract IListProperty doGetDelegate(Object source);
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected List doGetList(Object source) {
+		return getDelegate(source).getList(source);
+	}
+
+	protected void doSetList(Object source, List list) {
+		getDelegate(source).setList(source, list);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		getDelegate(source).updateList(source, diff);
+	}
+
+	public IObservableList observe(Object source) {
+		return getDelegate(source).observe(source);
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return getDelegate(source).observe(realm, source);
+	}
+
+	private class NullListProperty extends SimpleListProperty {
+		public Object getElementType() {
+			return elementType;
+		}
+
+		protected List doGetList(Object source) {
+			return Collections.EMPTY_LIST;
+		}
+
+		protected void doSetList(Object source, List list, ListDiff diff) {
+		}
+
+		protected void doSetList(Object source, List list) {
+		}
+
+		protected void doUpdateList(Object source, ListDiff diff) {
+		}
+
+		public INativePropertyListener adaptListener(
+				ISimplePropertyListener listener) {
+			return null;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/IListProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/IListProperty.java
new file mode 100644
index 0000000..7dc79b5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/IListProperty.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * Interface for list-typed properties.
+ * 
+ * @since 1.2
+ * @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 ListProperty
+ * @see SimpleListProperty
+ */
+public interface IListProperty extends IProperty {
+	/**
+	 * Returns the type of the elements in the collection or <code>null</code>
+	 * if untyped
+	 * 
+	 * @return the type of the elements in the collection or <code>null</code>
+	 *         if untyped
+	 */
+	public Object getElementType();
+
+	/**
+	 * Returns an unmodifiable List with the current contents of the source's
+	 * list property
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return an unmodifiable List with the current contents of the source's
+	 *         list property
+	 * @since 1.3
+	 */
+	public List getList(Object source);
+
+	/**
+	 * Updates the property on the source with the specified change
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to modify the source
+	 * through an {@link IObservableList} than through the property directly.
+	 * </p>
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param list
+	 *            the new list
+	 * @since 1.3
+	 */
+	public void setList(Object source, List list);
+
+	/**
+	 * Updates the property on the source with the specified change
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to modify the source
+	 * through an {@link IObservableList} than through the property directly.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param diff
+	 *            a diff describing the change
+	 * @since 1.3
+	 */
+	public void updateList(Object source, ListDiff diff);
+
+	/**
+	 * Returns an observable list observing this list property on the given
+	 * property source
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return an observable list observing this list property on the given
+	 *         property source
+	 */
+	public IObservableList observe(Object source);
+
+	/**
+	 * Returns an observable list observing this list property on the given
+	 * property source
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param source
+	 *            the property source
+	 * @return an observable list observing this list property on the given
+	 *         property source
+	 */
+	public IObservableList observe(Realm realm, Object source);
+
+	/**
+	 * Returns a factory for creating observable lists tracking this property of
+	 * a particular property source.
+	 * 
+	 * @return a factory for creating observable lists tracking this property of
+	 *         a particular property source.
+	 */
+	public IObservableFactory listFactory();
+
+	/**
+	 * Returns a factory for creating observable lists in the given realm,
+	 * tracking this property of a particular property source.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * 
+	 * @return a factory for creating observable lists in the given realm,
+	 *         tracking this property of a particular property source.
+	 */
+	public IObservableFactory listFactory(Realm realm);
+
+	/**
+	 * Returns an observable list on the master observable's realm which tracks
+	 * this property of the current value of <code>master</code>.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable list on the given realm which tracks this property
+	 *         of the current value of <code>master</code>.
+	 */
+	public IObservableList observeDetail(IObservableValue master);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * value property. Note that because this property is a projection of value
+	 * properties over a list, the only modification supported is through the
+	 * {@link IObservableList#set(int, Object)} method. Modifications made
+	 * through the returned property are delegated to the detail property, using
+	 * the corresponding list element from the master property as the source.
+	 * 
+	 * @param detailValue
+	 *            the detail property
+	 * @return the nested combination of the master list and detail value
+	 *         properties
+	 */
+	public IListProperty values(IValueProperty detailValue);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/ListProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/ListProperty.java
new file mode 100644
index 0000000..cf9590b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/ListProperty.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ *     Ovidio Mallo - bug 331348
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+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.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.property.ListPropertyDetailValuesList;
+
+/**
+ * Abstract implementation of IListProperty.
+ * 
+ * @since 1.2
+ */
+public abstract class ListProperty implements IListProperty {
+
+	/**
+	 * By default, this method returns <code>Collections.EMPTY_LIST</code> in
+	 * case the source object is <code>null</code>. Otherwise, this method
+	 * delegates to {@link #doGetList(Object)}.
+	 * 
+	 * <p>
+	 * Clients may override this method if they e.g. want to return a specific
+	 * default list in case the source object is <code>null</code>.
+	 * </p>
+	 * 
+	 * @see #doGetList(Object)
+	 * 
+	 * @since 1.3
+	 */
+	public List getList(Object source) {
+		if (source == null) {
+			return Collections.EMPTY_LIST;
+		}
+		return Collections.unmodifiableList(doGetList(source));
+	}
+
+	/**
+	 * Returns a List with the current contents of the source's list property
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return a List with the current contents of the source's list property
+	 * @noreference This method is not intended to be referenced by clients.
+	 * @since 1.3
+	 */
+	protected List doGetList(Object source) {
+		IObservableList observable = observe(source);
+		try {
+			return new ArrayList(observable);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void setList(Object source, List list) {
+		if (source != null) {
+			doSetList(source, list);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param list
+	 *            the new list
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void doSetList(Object source, List list) {
+		doUpdateList(source, Diffs.computeListDiff(doGetList(source), list));
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void updateList(Object source, ListDiff diff) {
+		if (source != null) {
+			doUpdateList(source, diff);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param diff
+	 *            a diff describing the change
+	 * @since 1.3
+	 */
+	protected void doUpdateList(Object source, ListDiff diff) {
+		IObservableList observable = observe(source);
+		try {
+			diff.applyTo(observable);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	public IObservableList observe(Object source) {
+		return observe(Realm.getDefault(), source);
+	}
+
+	public IObservableFactory listFactory() {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(target);
+			}
+		};
+	}
+
+	public IObservableFactory listFactory(final Realm realm) {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(realm, target);
+			}
+		};
+	}
+
+	public IObservableList observeDetail(IObservableValue master) {
+		return MasterDetailObservables.detailList(master,
+				listFactory(master.getRealm()), getElementType());
+	}
+
+	public final IListProperty values(IValueProperty detailValue) {
+		return new ListPropertyDetailValuesList(this, detailValue);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/MultiListProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/MultiListProperty.java
new file mode 100644
index 0000000..2a488d9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/MultiListProperty.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 265727)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import java.util.ArrayList;
+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.IObservableList;
+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.MultiList;
+import org.eclipse.core.internal.databinding.property.PropertyObservableUtil;
+
+/**
+ * A list property for observing multiple list properties in sequence in a
+ * combined list.
+ * 
+ * @since 1.2
+ */
+public class MultiListProperty extends ListProperty {
+	private IListProperty[] properties;
+	private Object elementType;
+
+	/**
+	 * Constructs a MultiListProperty for observing the specified list
+	 * properties in sequence
+	 * 
+	 * @param properties
+	 *            the list properties
+	 */
+	public MultiListProperty(IListProperty[] properties) {
+		this(properties, null);
+	}
+
+	/**
+	 * Constructs a MultiListProperty for observing the specified list
+	 * properties in sequence.
+	 * 
+	 * @param properties
+	 *            the list properties
+	 * @param elementType
+	 *            the element type of the MultiListProperty
+	 */
+	public MultiListProperty(IListProperty[] properties, Object elementType) {
+		this.properties = properties;
+		this.elementType = elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected List doGetList(Object source) {
+		List list = new ArrayList();
+		for (int i = 0; i < properties.length; i++)
+			list.addAll(properties[i].getList(source));
+		return list;
+	}
+
+	protected void doUpdateList(final Object source, ListDiff diff) {
+		diff.accept(new ListDiffVisitor() {
+			public void handleAdd(int index, Object element) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void handleMove(int oldIndex, int newIndex, Object element) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				int offset = 0;
+				for (int i = 0; i < properties.length; i++) {
+					List subList = properties[i].getList(source);
+					if (index - offset < subList.size()) {
+						int subListIndex = index - offset;
+						ListDiffEntry[] entries = new ListDiffEntry[] {
+								Diffs.createListDiffEntry(subListIndex, false,
+										oldElement),
+								Diffs.createListDiffEntry(subListIndex, true,
+										newElement) };
+						ListDiff diff = Diffs.createListDiff(entries);
+						properties[i].updateList(source, diff);
+						return;
+					}
+					offset += subList.size();
+				}
+				throw new IndexOutOfBoundsException("index: " + index //$NON-NLS-1$
+						+ ", size: " + offset); //$NON-NLS-1$
+			}
+
+			public void handleRemove(int index, Object element) {
+				int offset = 0;
+				for (int i = 0; i < properties.length; i++) {
+					List subList = properties[i].getList(source);
+					int subListIndex = index - offset;
+					if (subListIndex < subList.size()) {
+						ListDiff diff = Diffs.createListDiff(Diffs
+								.createListDiffEntry(subListIndex, false,
+										element));
+						properties[i].updateList(source, diff);
+						return;
+					}
+					offset += subList.size();
+				}
+				throw new IndexOutOfBoundsException("index: " + index //$NON-NLS-1$
+						+ ", size: " + offset); //$NON-NLS-1$
+			}
+		});
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		IObservableList[] lists = new IObservableList[properties.length];
+		for (int i = 0; i < lists.length; i++)
+			lists[i] = properties[i].observe(realm, source);
+		IObservableList multiList = new MultiList(lists, elementType);
+
+		for (int i = 0; i < lists.length; i++)
+			PropertyObservableUtil.cascadeDispose(multiList, lists[i]);
+
+		return multiList;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/SimpleListProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/SimpleListProperty.java
new file mode 100644
index 0000000..7cbece0
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/SimpleListProperty.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 247997, 265561
+ *     Ovidio Mallo - bug 301774
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import java.util.ArrayList;
+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.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.internal.databinding.property.list.SimplePropertyObservableList;
+
+/**
+ * Simplified abstract implementation of IListProperty. This class takes care of
+ * most of the functional requirements for an IListProperty implementation,
+ * leaving only the property-specific details to subclasses.
+ * <p>
+ * Subclasses must implement these methods:
+ * <ul>
+ * <li> {@link #getElementType()}
+ * <li> {@link #doGetList(Object)}
+ * <li> {@link #doSetList(Object, List, ListDiff)}
+ * <li> {@link #adaptListener(ISimplePropertyListener)}
+ * </ul>
+ * <p>
+ * In addition, we recommended overriding {@link #toString()} to return a
+ * description suitable for debugging purposes.
+ * 
+ * @since 1.2
+ */
+public abstract class SimpleListProperty extends ListProperty {
+	public IObservableList observe(Realm realm, Object source) {
+		return new SimplePropertyObservableList(realm, source, this);
+	}
+
+	// Accessors
+
+	protected abstract List doGetList(Object source);
+
+	// Mutators
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param list
+	 *            the new list
+	 * @param diff
+	 *            a diff describing the change
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public final void setList(Object source, List list, ListDiff diff) {
+		if (source != null && !diff.isEmpty()) {
+			doSetList(source, list, diff);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param list
+	 *            the new list
+	 * @param diff
+	 *            a diff describing the change
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected abstract void doSetList(Object source, List list, ListDiff diff);
+
+	protected void doSetList(Object source, List list) {
+		ListDiff diff = Diffs.computeLazyListDiff(doGetList(source), list);
+		doSetList(source, list, diff);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		List list = new ArrayList(doGetList(source));
+		diff.applyTo(list);
+		doSetList(source, list, diff);
+	}
+
+	/**
+	 * Returns a listener capable of adding or removing itself as a listener on
+	 * a source object using the the source's "native" listener API. Events
+	 * received from the source objects are parlayed to the specified listener
+	 * argument.
+	 * <p>
+	 * This method returns null if the source object has no listener APIs for
+	 * this property.
+	 * 
+	 * @param listener
+	 *            the property listener to receive events
+	 * @return a native listener which parlays property change events to the
+	 *         specified listener, or null if the source object has no listener
+	 *         APIs for this property.
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public abstract INativePropertyListener adaptListener(
+			ISimplePropertyListener listener);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/DelegatingMapProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/DelegatingMapProperty.java
new file mode 100644
index 0000000..dc338f6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/DelegatingMapProperty.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 247997)
+ *     Matthew Hall - bug 264306
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.map;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+
+/**
+ * @since 1.2
+ * 
+ */
+public abstract class DelegatingMapProperty extends MapProperty {
+	private final Object keyType;
+	private final Object valueType;
+	private final IMapProperty nullProperty = new NullMapProperty();
+
+	protected DelegatingMapProperty() {
+		this(null, null);
+	}
+
+	protected DelegatingMapProperty(Object keyType, Object valueType) {
+		this.keyType = keyType;
+		this.valueType = valueType;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Repeated calls to this method with the same source object returns the
+	 * same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return the property to delegate to for the specified source object.
+	 */
+	public final IMapProperty getDelegate(Object source) {
+		if (source == null)
+			return nullProperty;
+		IMapProperty delegate = doGetDelegate(source);
+		if (delegate == null)
+			delegate = nullProperty;
+		return delegate;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Implementers must ensure that repeated calls to this method with the same
+	 * source object returns the same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return the property to delegate to for the specified source object.
+	 */
+	protected abstract IMapProperty doGetDelegate(Object source);
+
+	public Object getKeyType() {
+		return keyType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Map doGetMap(Object source) {
+		return getDelegate(source).getMap(source);
+	}
+
+	protected void doSetMap(Object source, Map map) {
+		getDelegate(source).setMap(source, map);
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		getDelegate(source).updateMap(source, diff);
+	}
+
+	public IObservableMap observe(Object source) {
+		return getDelegate(source).observe(source);
+	}
+
+	public IObservableMap observe(Realm realm, Object source) {
+		return getDelegate(source).observe(realm, source);
+	}
+
+	private class NullMapProperty extends SimpleMapProperty {
+		protected Map doGetMap(Object source) {
+			return Collections.EMPTY_MAP;
+		}
+
+		protected void doSetMap(Object source, Map map, MapDiff diff) {
+		}
+
+		protected void doSetMap(Object source, Map map) {
+		}
+
+		protected void doUpdateMap(Object source, MapDiff diff) {
+		}
+
+		public INativePropertyListener adaptListener(
+				ISimplePropertyListener listener) {
+			return null;
+		}
+
+		public Object getKeyType() {
+			return keyType;
+		}
+
+		public Object getValueType() {
+			return valueType;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/IMapProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/IMapProperty.java
new file mode 100644
index 0000000..018cbac
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/IMapProperty.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.map;
+
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * Interface for map-typed properties
+ * 
+ * @since 1.2
+ * @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 MapProperty
+ * @see SimpleMapProperty
+ */
+public interface IMapProperty extends IProperty {
+	/**
+	 * Returns the element type of the map's key set or <code>null</code> if the
+	 * key set is untyped.
+	 * 
+	 * @return the element type of the map's key set or <code>null</code> if the
+	 *         key set is untyped.
+	 */
+	public Object getKeyType();
+
+	/**
+	 * Returns the element type of the map's values collection or
+	 * <code>null</code> if the collection is untyped.
+	 * 
+	 * @return the element type of the map's values collection or
+	 *         <code>null</code> if the collection is untyped.
+	 */
+	public Object getValueType();
+
+	/**
+	 * Returns an unmodifiable Map with the current contents of the source's map
+	 * property.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return a Map with the current contents of the source's map property
+	 * @since 1.3
+	 */
+	public Map getMap(Object source);
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to modify the source
+	 * through an {@link IObservableMap} than through the property directly.
+	 * </p>
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param map
+	 *            the new map
+	 * @since 1.3
+	 */
+	public void setMap(Object source, Map map);
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to modify the source
+	 * through an {@link IObservableMap} than through the property directly.
+	 * </p>
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param diff
+	 *            a diff describing the change
+	 * @since 1.3
+	 */
+	public void updateMap(Object source, MapDiff diff);
+
+	/**
+	 * Returns an observable map observing this map property on the given
+	 * property source
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return an observable map observing this map-typed property on the given
+	 *         property source
+	 */
+	public IObservableMap observe(Object source);
+
+	/**
+	 * Returns an observable map observing this map property on the given
+	 * property source
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param source
+	 *            the property source
+	 * @return an observable map observing this map-typed property on the given
+	 *         property source
+	 */
+	public IObservableMap observe(Realm realm, Object source);
+
+	/**
+	 * Returns a factory for creating observable maps tracking this property of
+	 * a particular property source.
+	 * 
+	 * @return a factory for creating observable maps tracking this property of
+	 *         a particular property source.
+	 */
+	public IObservableFactory mapFactory();
+
+	/**
+	 * Returns a factory for creating observable maps in the given realm,
+	 * tracking this property of a particular property source.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * 
+	 * @return a factory for creating observable maps in the given realm,
+	 *         tracking this property of a particular property source.
+	 */
+	public IObservableFactory mapFactory(Realm realm);
+
+	/**
+	 * Returns an observable map on the master observable's realm which tracks
+	 * this property of the values in the entry set of <code>master</code>.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable map on the master observable's realm which tracks
+	 *         this property of the values in the entry set of
+	 *         <code>master</code>.
+	 */
+	public IObservableMap observeDetail(IObservableValue master);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * value property. Note that because this property is a projection of value
+	 * properties over a values collection, the only modifications supported are
+	 * through the {@link IObservableMap#put(Object, Object)} and
+	 * {@link IObservableMap#putAll(java.util.Map)} methods. In the latter case,
+	 * this property does not entries for keys not already contained in the
+	 * master map's key set. Modifications made through the returned property
+	 * are delegated to the detail property, using the corresponding entry value
+	 * from the master property as the source.
+	 * 
+	 * @param detailValues
+	 *            the detail property
+	 * @return the nested combination of the master map and detail value
+	 *         properties.
+	 */
+	public IMapProperty values(IValueProperty detailValues);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/MapProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/MapProperty.java
new file mode 100644
index 0000000..2b1d5d6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/MapProperty.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ *     Ovidio Mallo - bug 331348
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.map;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.property.MapPropertyDetailValuesMap;
+
+/**
+ * Abstract implementation of IMapProperty
+ * 
+ * @since 1.2
+ */
+public abstract class MapProperty implements IMapProperty {
+
+	/**
+	 * By default, this method returns <code>Collections.EMPTY_MAP</code> in
+	 * case the source object is <code>null</code>. Otherwise, this method
+	 * delegates to {@link #doGetMap(Object)}.
+	 * 
+	 * <p>
+	 * Clients may override this method if they e.g. want to return a specific
+	 * default map in case the source object is <code>null</code>.
+	 * </p>
+	 * 
+	 * @see #doGetMap(Object)
+	 * 
+	 * @since 1.3
+	 */
+	public Map getMap(Object source) {
+		if (source == null) {
+			return Collections.EMPTY_MAP;
+		}
+		return Collections.unmodifiableMap(doGetMap(source));
+	}
+
+	/**
+	 * Returns a Map with the current contents of the source's map property
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return a Map with the current contents of the source's map property
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected Map doGetMap(Object source) {
+		IObservableMap observable = observe(source);
+		try {
+			return new IdentityMap(observable);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void setMap(Object source, Map map) {
+		if (source != null) {
+			doSetMap(source, map);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param map
+	 *            the new map
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void doSetMap(Object source, Map map) {
+		MapDiff diff = Diffs.computeMapDiff(doGetMap(source), map);
+		doUpdateMap(source, diff);
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void updateMap(Object source, MapDiff diff) {
+		if (source != null) {
+			doUpdateMap(source, diff);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param diff
+	 *            a diff describing the change
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		IObservableMap observable = observe(source);
+		try {
+			diff.applyTo(observable);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	public IObservableMap observe(Object source) {
+		return observe(Realm.getDefault(), source);
+	}
+
+	public IObservableFactory mapFactory() {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(target);
+			}
+		};
+	}
+
+	public IObservableFactory mapFactory(final Realm realm) {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(realm, target);
+			}
+		};
+	}
+
+	public IObservableMap observeDetail(IObservableValue master) {
+		return MasterDetailObservables.detailMap(master,
+				mapFactory(master.getRealm()), getKeyType(), getValueType());
+	}
+
+	public final IMapProperty values(IValueProperty detailValues) {
+		return new MapPropertyDetailValuesMap(this, detailValues);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/SimpleMapProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/SimpleMapProperty.java
new file mode 100644
index 0000000..d0ab799
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/map/SimpleMapProperty.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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
+ *     Matthew Hall - bugs 195222, 247997, 265561
+ *     Ovidio Mallo - bug 301774
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.map;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.internal.databinding.property.map.SimplePropertyObservableMap;
+
+/**
+ * Simplified abstract implementation of IMapProperty. This class takes care of
+ * most of the functional requirements for an IMapProperty implementation,
+ * leaving only the property-specific details to subclasses.
+ * <p>
+ * Subclasses must implement these methods:
+ * <ul>
+ * <li> {@link #getKeyType()}
+ * <li> {@link #getValueType()}
+ * <li> {@link #doGetMap(Object)}
+ * <li> {@link #doSetMap(Object, Map, MapDiff)}
+ * <li> {@link #adaptListener(ISimplePropertyListener)}
+ * </ul>
+ * <p>
+ * In addition, we recommended overriding {@link #toString()} to return a
+ * description suitable for debugging purposes.
+ * 
+ * @since 1.2
+ */
+public abstract class SimpleMapProperty extends MapProperty {
+	public IObservableMap observe(Realm realm, Object source) {
+		return new SimplePropertyObservableMap(realm, source, this);
+	}
+
+	// Accessors
+
+	protected abstract Map doGetMap(Object source);
+
+	// Mutators
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param map
+	 *            the new map
+	 * @param diff
+	 *            a diff describing the change
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public final void setMap(Object source, Map map, MapDiff diff) {
+		if (source != null && !diff.isEmpty())
+			doSetMap(source, map, diff);
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param map
+	 *            the new map
+	 * @param diff
+	 *            a diff describing the change
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected abstract void doSetMap(Object source, Map map, MapDiff diff);
+
+	protected void doSetMap(Object source, Map map) {
+		MapDiff diff = Diffs.computeLazyMapDiff(doGetMap(source), map);
+		doSetMap(source, map, diff);
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		Map map = new HashMap(doGetMap(source));
+		diff.applyTo(map);
+		doSetMap(source, map, diff);
+	}
+
+	// Listeners
+
+	/**
+	 * Returns a listener capable of adding or removing itself as a listener on
+	 * a source object using the the source's "native" listener API. Events
+	 * received from the source objects are parlayed to the specified listener
+	 * argument.
+	 * <p>
+	 * This method returns null if the source object has no listener APIs for
+	 * this property.
+	 * 
+	 * @param listener
+	 *            the property listener to receive events
+	 * @return a native listener which parlays property change events to the
+	 *         specified listener, or null if the source object has no listener
+	 *         APIs for this property.
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public abstract INativePropertyListener adaptListener(
+			ISimplePropertyListener listener);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/package.html b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/package.html
new file mode 100644
index 0000000..6eddb4f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/package.html
@@ -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="Matthew Hall">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Interfaces and classes for representing and observing properties of objects.
+<h2>
+Package Specification</h2>
+<p>
+This package and its subpackages provide the <tt>IProperty</tt>,
+<tt>IValueProperty</tt>, <tt>IListProperty</tt>, <tt>ISetProperty</tt> and
+<tt>IMapProperty</tt> interfaces, along with classes
+which serve as base implementations of each interface.    
+<p>
+Properties are intended to serve as a convenient path to creating observables
+for observing specific attributes of source objects.  The main goals of this
+API are:
+<ul>
+<li>Simplify the process of creating custom observables.  Developing custom
+observables correctly can be tricky, so the properties API tries to ease this
+burden by providing all the observable implementations.  Property implementers
+only need to extend one of the provided base classes
+(<tt>SimpleValueProperty</tt>, <tt>SimpleListProperty</tt>,
+<tt>SimpleSetProperty</tt> or <tt>SimpleMapProperty</tt>) and implement a
+handful of abstract methods which the observables use to function.
+<li>Simplify observation of nested properties.  Traditionally observing a
+nested property required creating an observable for the first property, then
+wrapping that observable in a master-detail observable for each successive
+property in the chain.  Using property chaining it is trivial to define a
+nested property and to observe that property on a particular source object.
+</ul>
+<p>
+A set of delegating properties are also provided
+(<tt>DelegatingValueProperty</tt>, <tt>DelegatingListProperty</tt>,
+<tt>DelegatingSetProperty</tt> and <tt>DelegatingMapProperty</tt>) which
+may be used to implement properties where the property behavior depends on the
+type of source object.
+</body>
+</html>
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/DelegatingSetProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/DelegatingSetProperty.java
new file mode 100644
index 0000000..d65d5bb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/DelegatingSetProperty.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 247997)
+ *     Matthew Hall - bug 264306
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.set;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+
+/**
+ * @since 1.2
+ * 
+ */
+public abstract class DelegatingSetProperty extends SetProperty {
+	private final Object elementType;
+	private final ISetProperty nullProperty = new NullSetProperty();
+
+	protected DelegatingSetProperty() {
+		this(null);
+	}
+
+	protected DelegatingSetProperty(Object elementType) {
+		this.elementType = elementType;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Repeated calls to this method with the same source object returns the
+	 * same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return the property to delegate to for the specified source object.
+	 */
+	protected final ISetProperty getDelegate(Object source) {
+		if (source == null)
+			return nullProperty;
+		ISetProperty delegate = doGetDelegate(source);
+		if (delegate == null)
+			delegate = nullProperty;
+		return delegate;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Implementers must ensure that repeated calls to this method with the same
+	 * source object returns the same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return the property to delegate to for the specified source object.
+	 */
+	protected abstract ISetProperty doGetDelegate(Object source);
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected Set doGetSet(Object source) {
+		return getDelegate(source).getSet(source);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		getDelegate(source).setSet(source, set);
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		getDelegate(source).updateSet(source, diff);
+	}
+
+	public IObservableSet observe(Object source) {
+		return getDelegate(source).observe(source);
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		return getDelegate(source).observe(realm, source);
+	}
+
+	private class NullSetProperty extends SimpleSetProperty {
+		public Object getElementType() {
+			return elementType;
+		}
+
+		protected Set doGetSet(Object source) {
+			return Collections.EMPTY_SET;
+		}
+
+		protected void doSetSet(Object source, Set set, SetDiff diff) {
+		}
+
+		protected void doSetSet(Object source, Set set) {
+		}
+
+		protected void doUpdateSet(Object source, SetDiff diff) {
+		}
+
+		public INativePropertyListener adaptListener(
+				ISimplePropertyListener listener) {
+			return null;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/ISetProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/ISetProperty.java
new file mode 100644
index 0000000..fc593f2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/ISetProperty.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.set;
+
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * Interface for set-typed properties
+ * 
+ * @since 1.2
+ * @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 SetProperty
+ * @see SimpleSetProperty
+ */
+public interface ISetProperty extends IProperty {
+	/**
+	 * Returns the type of the elements in the collection or <code>null</code>
+	 * if untyped
+	 * 
+	 * @return the type of the elements in the collection or <code>null</code>
+	 *         if untyped
+	 */
+	public Object getElementType();
+
+	/**
+	 * Returns an unmodifiable Set with the current contents of the source's set
+	 * property
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return an unmodifiable Set with the current contents of the source's set
+	 *         property
+	 * @since 1.3
+	 */
+	public Set getSet(Object source);
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to modify the source
+	 * through an {@link IObservableSet} than through the property directly.
+	 * </p>
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param set
+	 *            the new set
+	 * @since 1.3
+	 */
+	public void setSet(Object source, Set set);
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to modify the source
+	 * through an {@link IObservableSet} than through the property directly.
+	 * </p>
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param diff
+	 *            a diff describing the change
+	 * @since 1.3
+	 */
+	public void updateSet(Object source, SetDiff diff);
+
+	/**
+	 * Returns an observable set observing this set property on the given
+	 * property source
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return an observable set observing this set property on the given
+	 *         property source
+	 */
+	public IObservableSet observe(Object source);
+
+	/**
+	 * Returns an observable set observing this set property on the given
+	 * property source
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param source
+	 *            the property source
+	 * @return an observable set observing this set property on the given
+	 *         property source
+	 */
+	public IObservableSet observe(Realm realm, Object source);
+
+	/**
+	 * Returns a factory for creating observable sets tracking this property of
+	 * a particular property source.
+	 * 
+	 * @return a factory for creating observable sets tracking this property of
+	 *         a particular property source.
+	 */
+	public IObservableFactory setFactory();
+
+	/**
+	 * Returns a factory for creating observable sets in the given realm,
+	 * tracking this property of a particular property source.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * 
+	 * @return a factory for creating observable sets in the given realm,
+	 *         tracking this property of a particular property source.
+	 */
+	public IObservableFactory setFactory(Realm realm);
+
+	/**
+	 * Returns an observable set on the master observable's realm which tracks
+	 * this property of the current value of <code>master</code>.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable set on the given realm which tracks this property
+	 *         of the current value of <code>master</code>.
+	 */
+	public IObservableSet observeDetail(IObservableValue master);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * value property. Note that because this property is a projection of value
+	 * properties over a set, the only modifications supported are through the
+	 * {@link IObservableMap#put(Object, Object)} and
+	 * {@link IObservableMap#putAll(java.util.Map)} methods. In the latter case,
+	 * this property does not put entries for keys not already in the master key
+	 * set. Modifications made through the returned property are delegated to
+	 * the detail property, using the corresponding set element from the master
+	 * property as the source.
+	 * 
+	 * @param detailValues
+	 *            the detail property
+	 * @return the nested combination of the master set and detail value
+	 *         properties
+	 */
+	public IMapProperty values(IValueProperty detailValues);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/SetProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/SetProperty.java
new file mode 100644
index 0000000..52c68c8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/SetProperty.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ *     Ovidio Mallo - bug 331348
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.set;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+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.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.core.internal.databinding.property.SetPropertyDetailValuesMap;
+
+/**
+ * Abstract implementation of ISetProperty
+ * 
+ * @since 1.2
+ */
+public abstract class SetProperty implements ISetProperty {
+
+	/**
+	 * By default, this method returns <code>Collections.EMPTY_SET</code> in
+	 * case the source object is <code>null</code>. Otherwise, this method
+	 * delegates to {@link #doGetSet(Object)}.
+	 * 
+	 * <p>
+	 * Clients may override this method if they e.g. want to return a specific
+	 * default set in case the source object is <code>null</code>.
+	 * </p>
+	 * 
+	 * @see #doGetSet(Object)
+	 * 
+	 * @since 1.3
+	 */
+	public Set getSet(Object source) {
+		if (source == null) {
+			return Collections.EMPTY_SET;
+		}
+		return Collections.unmodifiableSet(doGetSet(source));
+	}
+
+	/**
+	 * Returns a Set with the current contents of the source's set property
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return a Set with the current contents of the source's set property
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected Set doGetSet(Object source) {
+		IObservableSet observable = observe(source);
+		try {
+			return new IdentitySet(observable);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void setSet(Object source, Set set) {
+		if (source != null) {
+			doSetSet(source, set);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param set
+	 *            the new set
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void doSetSet(Object source, Set set) {
+		doUpdateSet(source, Diffs.computeSetDiff(doGetSet(source), set));
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void updateSet(Object source, SetDiff diff) {
+		if (source != null && !diff.isEmpty()) {
+			doUpdateSet(source, diff);
+		}
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param diff
+	 *            a diff describing the change
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		IObservableSet observable = observe(source);
+		try {
+			diff.applyTo(observable);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	public IObservableSet observe(Object source) {
+		return observe(Realm.getDefault(), source);
+	}
+
+	public IObservableFactory setFactory() {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(target);
+			}
+		};
+	}
+
+	public IObservableFactory setFactory(final Realm realm) {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(realm, target);
+			}
+		};
+	}
+
+	public IObservableSet observeDetail(IObservableValue master) {
+		return MasterDetailObservables.detailSet(master,
+				setFactory(master.getRealm()), getElementType());
+	}
+
+	public final IMapProperty values(IValueProperty detailValues) {
+		return new SetPropertyDetailValuesMap(this, detailValues);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/SimpleSetProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/SimpleSetProperty.java
new file mode 100644
index 0000000..85273c7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/SimpleSetProperty.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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
+ *     Matthew Hall - bugs 195222, 247997, 265561
+ *     Ovidio Mallo - bug 301774
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.set;
+
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.internal.databinding.property.set.SimplePropertyObservableSet;
+
+/**
+ * Simplified abstract implementation of ISetProperty. This class takes care of
+ * most of the functional requirements for an ISetProperty implementation,
+ * leaving only the property-specific details to subclasses.
+ * <p>
+ * Subclasses must implement these methods:
+ * <ul>
+ * <li> {@link #getElementType()}
+ * <li> {@link #doGetSet(Object)}
+ * <li> {@link #doSetSet(Object, Set, SetDiff)}
+ * <li> {@link #adaptListener(ISimplePropertyListener)}
+ * </ul>
+ * <p>
+ * In addition, we recommended overriding {@link #toString()} to return a
+ * description suitable for debugging purposes.
+ * 
+ * @since 1.2
+ */
+public abstract class SimpleSetProperty extends SetProperty {
+	public IObservableSet observe(Realm realm, Object source) {
+		return new SimplePropertyObservableSet(realm, source, this);
+	}
+
+	// Accessors
+
+	protected abstract Set doGetSet(Object source);
+
+	// Mutators
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param set
+	 *            the new set
+	 * @param diff
+	 *            a diff describing the change
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public final void setSet(Object source, Set set, SetDiff diff) {
+		if (source != null && !diff.isEmpty())
+			doSetSet(source, set, diff);
+	}
+
+	/**
+	 * Updates the property on the source with the specified change.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param set
+	 *            the new set
+	 * @param diff
+	 *            a diff describing the change
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected abstract void doSetSet(Object source, Set set, SetDiff diff);
+
+	protected void doSetSet(Object source, Set set) {
+		SetDiff diff = Diffs.computeLazySetDiff(doGetSet(source), set);
+		doSetSet(source, set, diff);
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		Set set = new HashSet(doGetSet(source));
+		diff.applyTo(set);
+		doSetSet(source, set, diff);
+	}
+
+	// Listeners
+
+	/**
+	 * Returns a listener capable of adding or removing itself as a listener on
+	 * a source object using the the source's "native" listener API. Events
+	 * received from the source objects are parlayed to the specified listener
+	 * argument.
+	 * <p>
+	 * This method returns null if the source object has no listener APIs for
+	 * this property.
+	 * 
+	 * @param listener
+	 *            the property listener to receive events
+	 * @return a native listener which parlays property change events to the
+	 *         specified listener, or null if the source object has no listener
+	 *         APIs for this property.
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public abstract INativePropertyListener adaptListener(
+			ISimplePropertyListener listener);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/UnionSetProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/UnionSetProperty.java
new file mode 100644
index 0000000..3c384cc
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/set/UnionSetProperty.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 265727)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.set;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.set.UnionSet;
+import org.eclipse.core.internal.databinding.property.PropertyObservableUtil;
+
+/**
+ * A set property for observing the union of multiple set properties a combined
+ * set.
+ * 
+ * @since 1.2
+ */
+public class UnionSetProperty extends SetProperty {
+	private final ISetProperty[] properties;
+	private final Object elementType;
+
+	/**
+	 * @param properties
+	 */
+	public UnionSetProperty(ISetProperty[] properties) {
+		this(properties, null);
+	}
+
+	/**
+	 * @param properties
+	 * @param elementType
+	 */
+	public UnionSetProperty(ISetProperty[] properties, Object elementType) {
+		this.properties = properties;
+		this.elementType = elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected Set doGetSet(Object source) {
+		Set set = new HashSet();
+		for (int i = 0; i < properties.length; i++)
+			set.addAll(properties[i].getSet(source));
+		return set;
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		throw new UnsupportedOperationException(
+				"UnionSetProperty is unmodifiable"); //$NON-NLS-1$
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		throw new UnsupportedOperationException(
+				"UnionSetProperty is unmodifiable"); //$NON-NLS-1$
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		IObservableSet[] sets = new IObservableSet[properties.length];
+		for (int i = 0; i < sets.length; i++)
+			sets[i] = properties[i].observe(realm, source);
+		IObservableSet unionSet = new UnionSet(sets, elementType);
+
+		for (int i = 0; i < sets.length; i++)
+			PropertyObservableUtil.cascadeDispose(unionSet, sets[i]);
+
+		return unionSet;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/DelegatingValueProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/DelegatingValueProperty.java
new file mode 100644
index 0000000..3dac917
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/DelegatingValueProperty.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 247997)
+ *     Matthew Hall - bug 264306
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.Realm;
+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.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.internal.databinding.property.value.ListDelegatingValueObservableList;
+import org.eclipse.core.internal.databinding.property.value.MapDelegatingValueObservableMap;
+import org.eclipse.core.internal.databinding.property.value.SetDelegatingValueObservableMap;
+
+/**
+ * @since 1.2
+ * 
+ */
+public abstract class DelegatingValueProperty extends ValueProperty {
+	private final Object valueType;
+	private final IValueProperty nullProperty = new NullValueProperty();
+
+	protected DelegatingValueProperty() {
+		this(null);
+	}
+
+	protected DelegatingValueProperty(Object valueType) {
+		this.valueType = valueType;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Repeated calls to this method with the same source object returns the
+	 * same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return the property to delegate to for the specified source object.
+	 */
+	public final IValueProperty getDelegate(Object source) {
+		if (source == null)
+			return nullProperty;
+		IValueProperty delegate = doGetDelegate(source);
+		if (delegate == null)
+			delegate = nullProperty;
+		return delegate;
+	}
+
+	/**
+	 * Returns the property to delegate to for the specified source object.
+	 * Implementers must ensure that repeated calls to this method with the same
+	 * source object returns the same delegate instance.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return the property to delegate to for the specified source object.
+	 */
+	protected abstract IValueProperty doGetDelegate(Object source);
+
+	protected Object doGetValue(Object source) {
+		return getDelegate(source).getValue(source);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		getDelegate(source).setValue(source, value);
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	public IObservableValue observe(Object source) {
+		return getDelegate(source).observe(source);
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		return getDelegate(source).observe(realm, source);
+	}
+
+	public IObservableList observeDetail(IObservableList master) {
+		return new ListDelegatingValueObservableList(master, this);
+	}
+
+	public IObservableMap observeDetail(IObservableSet master) {
+		return new SetDelegatingValueObservableMap(master, this);
+	}
+
+	public IObservableMap observeDetail(IObservableMap master) {
+		return new MapDelegatingValueObservableMap(master, this);
+	}
+
+	private class NullValueProperty extends SimpleValueProperty {
+		public Object getValueType() {
+			return valueType;
+		}
+
+		protected Object doGetValue(Object source) {
+			return null;
+		}
+
+		protected void doSetValue(Object source, Object value) {
+		}
+
+		public INativePropertyListener adaptListener(
+				ISimplePropertyListener listener) {
+			return null;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/IValueProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/IValueProperty.java
new file mode 100644
index 0000000..162ccb7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/IValueProperty.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+
+/**
+ * Interface for value-typed properties
+ * 
+ * @since 1.2
+ * @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 ValueProperty
+ * @see SimpleValueProperty
+ */
+public interface IValueProperty extends IProperty {
+	/**
+	 * Returns the value type of the property, or <code>null</code> if untyped.
+	 * 
+	 * @return the value type of the property, or <code>null</code> if untyped.
+	 */
+	public Object getValueType();
+
+	/**
+	 * Returns the current value of this property on the specified property
+	 * source.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @return the current value of this property on the specified property
+	 *         source.
+	 * @since 1.3
+	 */
+	public Object getValue(Object source);
+
+	/**
+	 * Sets this property on the specified property source to the specified
+	 * value.
+	 * <p>
+	 * <b>Note:</b> This method is made available to facilitate basic property
+	 * access. However if the property source lacks property change
+	 * notification, then observables on the source object may not be notified
+	 * of the change. In most cases it is preferable to call
+	 * {@link IObservableValue#setValue(Object)} on the observable instead.
+	 * 
+	 * @param source
+	 *            the property source (may be null)
+	 * @param value
+	 *            the new property value
+	 * @since 1.3
+	 */
+	public void setValue(Object source, Object value);
+
+	/**
+	 * Returns an observable value observing this value property on the given
+	 * property source.
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return an observable value observing this value property on the given
+	 *         property source
+	 */
+	public IObservableValue observe(Object source);
+
+	/**
+	 * Returns an observable value observing this value property on the given
+	 * property source
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param source
+	 *            the property source
+	 * @return an observable value observing this value property on the given
+	 *         property source
+	 */
+	public IObservableValue observe(Realm realm, Object source);
+
+	/**
+	 * Returns a factory for creating observable values tracking this property
+	 * of a particular property source.
+	 * 
+	 * @return a factory for creating observable values tracking this property
+	 *         of a particular property source.
+	 */
+	public IObservableFactory valueFactory();
+
+	/**
+	 * Returns a factory for creating observable values in the given realm,
+	 * tracking this property of a particular property source.
+	 * 
+	 * @param realm
+	 *            the realm
+	 * 
+	 * @return a factory for creating observable values in the given realm,
+	 *         tracking this property of a particular property source.
+	 */
+	public IObservableFactory valueFactory(Realm realm);
+
+	/**
+	 * Returns an observable value on the master observable's realm which tracks
+	 * this property on the current value of <code>master</code>.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable value which tracks this property of the current
+	 *         value of <code>master</code>.
+	 */
+	public IObservableValue observeDetail(IObservableValue master);
+
+	/**
+	 * Returns an observable list on the master observable's realm which tracks
+	 * this property on each element of <code>master</code>.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable list which tracks this property on each element of
+	 *         the master observable.
+	 */
+	public IObservableList observeDetail(IObservableList master);
+
+	/**
+	 * Returns an observable map on the master observable's realm where the
+	 * map's key set is the specified master set, and where each key maps to the
+	 * current property value for each element.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable map that tracks the current value of this property
+	 *         for the elements in the given set.
+	 */
+	public IObservableMap observeDetail(IObservableSet master);
+
+	/**
+	 * Returns an observable map on the master observable's realm where the
+	 * map's key set is the same as the master observable map, and where each
+	 * value is the property value of the corresponding value in the master
+	 * observable map.
+	 * 
+	 * @param master
+	 *            the master observable
+	 * @return an observable map on the master observable's realm which tracks
+	 *         the current value of this property for the elements in the given
+	 *         map's values collection
+	 */
+	public IObservableMap observeDetail(IObservableMap master);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * value property. Value modifications made through the returned property
+	 * are delegated to the detail property, using the value of this property as
+	 * the source.
+	 * 
+	 * @param detailValue
+	 *            the detail property
+	 * @return the nested combination of the master and detail properties
+	 */
+	public IValueProperty value(IValueProperty detailValue);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * list property. List modifications made through the returned property are
+	 * delegated to the detail property, using the value of the master property
+	 * as the source.
+	 * 
+	 * @param detailList
+	 *            the detail property
+	 * @return the nested combination of the master value and detail list
+	 *         properties
+	 */
+	public IListProperty list(IListProperty detailList);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * set property. Set modifications made through the returned property are
+	 * delegated to the detail property, using the value of the master property
+	 * as the source.
+	 * 
+	 * @param detailSet
+	 *            the detail property
+	 * @return the nested combination of the master value and detail set
+	 *         properties
+	 */
+	public ISetProperty set(ISetProperty detailSet);
+
+	/**
+	 * Returns the nested combination of this property and the specified detail
+	 * map property. Map modifications made through the returned property are
+	 * delegated to the detail property, using the value of the master property
+	 * as the source.
+	 * 
+	 * @param detailMap
+	 *            the detail property
+	 * @return the nested combination of the master value and detial map
+	 *         properties
+	 */
+	public IMapProperty map(IMapProperty detailMap);
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/SimpleValueProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/SimpleValueProperty.java
new file mode 100644
index 0000000..9293c85
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/SimpleValueProperty.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 247997, 265561
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.Realm;
+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.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.internal.databinding.property.value.ListSimpleValueObservableList;
+import org.eclipse.core.internal.databinding.property.value.MapSimpleValueObservableMap;
+import org.eclipse.core.internal.databinding.property.value.SetSimpleValueObservableMap;
+import org.eclipse.core.internal.databinding.property.value.SimplePropertyObservableValue;
+
+/**
+ * Simplified abstract implementation of IValueProperty. This class takes care
+ * of most of the functional requirements for an IValueProperty implementation,
+ * leaving only the property-specific details to subclasses.
+ * <p>
+ * Subclasses must implement these methods:
+ * <ul>
+ * <li> {@link #getValueType()}
+ * <li> {@link #doGetValue(Object)}
+ * <li> {@link #doSetValue(Object, Object)}
+ * <li> {@link #adaptListener(ISimplePropertyListener)}
+ * </ul>
+ * <p>
+ * In addition, we recommended overriding {@link #toString()} to return a
+ * description suitable for debugging purposes.
+ * 
+ * @since 1.2
+ */
+public abstract class SimpleValueProperty extends ValueProperty {
+	protected abstract Object doGetValue(Object source);
+
+	protected abstract void doSetValue(Object source, Object value);
+
+	/**
+	 * Returns a listener capable of adding or removing itself as a listener on
+	 * a source object using the the source's "native" listener API. Events
+	 * received from the source objects are parlayed to the specified listener
+	 * argument.
+	 * <p>
+	 * This method returns null if the source object has no listener APIs for
+	 * this property.
+	 * 
+	 * @param listener
+	 *            the property listener to receive events
+	 * @return a native listener which parlays property change events to the
+	 *         specified listener, or null if the source object has no listener
+	 *         APIs for this property.
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public abstract INativePropertyListener adaptListener(
+			ISimplePropertyListener listener);
+
+	public IObservableValue observe(Realm realm, Object source) {
+		return new SimplePropertyObservableValue(realm, source, this);
+	}
+
+	public IObservableList observeDetail(IObservableList master) {
+		return new ListSimpleValueObservableList(master, this);
+	}
+
+	public IObservableMap observeDetail(IObservableSet master) {
+		return new SetSimpleValueObservableMap(master, this);
+	}
+
+	public IObservableMap observeDetail(IObservableMap master) {
+		return new MapSimpleValueObservableMap(master, this);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/ValueProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/ValueProperty.java
new file mode 100644
index 0000000..5024b3b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/ValueProperty.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222
+ *     Ovidio Mallo - bugs 331348, 305367
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+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.value.IObservableValue;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.internal.databinding.property.ValuePropertyDetailList;
+import org.eclipse.core.internal.databinding.property.ValuePropertyDetailMap;
+import org.eclipse.core.internal.databinding.property.ValuePropertyDetailSet;
+import org.eclipse.core.internal.databinding.property.ValuePropertyDetailValue;
+
+/**
+ * Abstract implementation of IValueProperty
+ * 
+ * @since 1.2
+ */
+public abstract class ValueProperty implements IValueProperty {
+
+	/**
+	 * By default, this method returns <code>null</code> in case the source
+	 * object is itself <code>null</code>. Otherwise, this method delegates to
+	 * {@link #doGetValue(Object)}.
+	 * 
+	 * <p>
+	 * Clients may override this method if they e.g. want to return a specific
+	 * default value in case the source object is <code>null</code>.
+	 * </p>
+	 * 
+	 * @see #doGetValue(Object)
+	 * 
+	 * @since 1.3
+	 */
+	public Object getValue(Object source) {
+		if (source == null) {
+			return null;
+		}
+		return doGetValue(source);
+	}
+
+	/**
+	 * Returns the value of the property on the specified source object
+	 * 
+	 * @param source
+	 *            the property source
+	 * @return the current value of the source's value property
+	 * @noreference This method is not intended to be referenced by clients.
+	 * @since 1.3
+	 */
+	protected Object doGetValue(Object source) {
+		IObservableValue observable = observe(source);
+		try {
+			return observable.getValue();
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	/**
+	 * @since 1.3
+	 */
+	public final void setValue(Object source, Object value) {
+		if (source != null) {
+			doSetValue(source, value);
+		}
+	}
+
+	/**
+	 * Sets the source's value property to the specified vlaue
+	 * 
+	 * @param source
+	 *            the property source
+	 * @param value
+	 *            the new value
+	 * @since 1.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void doSetValue(Object source, Object value) {
+		IObservableValue observable = observe(source);
+		try {
+			observable.setValue(value);
+		} finally {
+			observable.dispose();
+		}
+	}
+
+	public IObservableValue observe(Object source) {
+		return observe(Realm.getDefault(), source);
+	}
+
+	public IObservableFactory valueFactory() {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(target);
+			}
+		};
+	}
+
+	public IObservableFactory valueFactory(final Realm realm) {
+		return new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return observe(realm, target);
+			}
+		};
+	}
+
+	public IObservableValue observeDetail(IObservableValue master) {
+		return MasterDetailObservables.detailValue(master,
+				valueFactory(master.getRealm()), getValueType());
+	}
+
+	/**
+	 * @since 1.4
+	 */
+	public IObservableList observeDetail(IObservableList master) {
+		return MasterDetailObservables.detailValues(master,
+				valueFactory(master.getRealm()), getValueType());
+	}
+
+	/**
+	 * @since 1.4
+	 */
+	public IObservableMap observeDetail(IObservableSet master) {
+		return MasterDetailObservables.detailValues(master,
+				valueFactory(master.getRealm()), getValueType());
+	}
+
+	/**
+	 * @since 1.4
+	 */
+	public IObservableMap observeDetail(IObservableMap master) {
+		return MasterDetailObservables.detailValues(master,
+				valueFactory(master.getRealm()), getValueType());
+	}
+
+	public final IValueProperty value(IValueProperty detailValue) {
+		return new ValuePropertyDetailValue(this, detailValue);
+	}
+
+	public final IListProperty list(IListProperty detailList) {
+		return new ValuePropertyDetailList(this, detailList);
+	}
+
+	public final ISetProperty set(ISetProperty detailSet) {
+		return new ValuePropertyDetailSet(this, detailSet);
+	}
+
+	public final IMapProperty map(IMapProperty detailMap) {
+		return new ValuePropertyDetailMap(this, detailMap);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ListPropertyDetailValuesList.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ListPropertyDetailValuesList.java
new file mode 100644
index 0000000..15928f3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ListPropertyDetailValuesList.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.list.ListProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ListPropertyDetailValuesList extends ListProperty {
+	private final IListProperty masterProperty;
+	private final IValueProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public ListPropertyDetailValuesList(IListProperty masterProperty,
+			IValueProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getElementType() {
+		return detailProperty.getValueType();
+	}
+
+	protected List doGetList(Object source) {
+		List masterList = masterProperty.getList(source);
+		List detailList = new ArrayList(masterList.size());
+		for (Iterator it = masterList.iterator(); it.hasNext();)
+			detailList.add(detailProperty.getValue(it.next()));
+		return detailList;
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		final List masterList = masterProperty.getList(source);
+		diff.accept(new ListDiffVisitor() {
+			public void handleAdd(int index, Object element) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void handleRemove(int index, Object element) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void handleMove(int oldIndex, int newIndex, Object element) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				detailProperty.setValue(masterList.get(index), newElement);
+			}
+		});
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		IObservableList masterList;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterList = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableList detailList = detailProperty.observeDetail(masterList);
+		PropertyObservableUtil.cascadeDispose(detailList, masterList);
+		return detailList;
+	}
+
+	public IObservableList observeDetail(IObservableValue master) {
+		IObservableList masterList;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterList = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableList detailList = detailProperty.observeDetail(masterList);
+		PropertyObservableUtil.cascadeDispose(detailList, masterList);
+		return detailList;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/MapPropertyDetailValuesMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/MapPropertyDetailValuesMap.java
new file mode 100644
index 0000000..1d69985
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/MapPropertyDetailValuesMap.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.map.MapProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class MapPropertyDetailValuesMap extends MapProperty {
+	private final IMapProperty masterProperty;
+	private final IValueProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public MapPropertyDetailValuesMap(IMapProperty masterProperty,
+			IValueProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getKeyType() {
+		return masterProperty.getKeyType();
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	protected Map doGetMap(Object source) {
+		Map masterMap = masterProperty.getMap(source);
+		Map detailMap = new IdentityMap();
+		for (Iterator it = masterMap.entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Map.Entry) it.next();
+			detailMap.put(entry.getKey(), detailProperty.getValue(entry
+					.getValue()));
+		}
+		return detailMap;
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		if (!diff.getAddedKeys().isEmpty())
+			throw new UnsupportedOperationException(toString()
+					+ " does not support entry additions"); //$NON-NLS-1$
+		if (!diff.getRemovedKeys().isEmpty())
+			throw new UnsupportedOperationException(toString()
+					+ " does not support entry removals"); //$NON-NLS-1$
+		Map masterMap = masterProperty.getMap(source);
+		for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) {
+			Object key = it.next();
+			Object masterValue = masterMap.get(key);
+			detailProperty.setValue(masterValue, diff.getNewValue(key));
+		}
+	}
+
+	public IObservableMap observe(Realm realm, Object source) {
+		IObservableMap masterMap;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterMap = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterMap);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterMap);
+		return detailMap;
+	}
+
+	public IObservableMap observeDetail(IObservableValue master) {
+		IObservableMap masterMap;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterMap = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterMap);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterMap);
+		return detailMap;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/PropertyObservableUtil.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/PropertyObservableUtil.java
new file mode 100644
index 0000000..a86c8ea
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/PropertyObservableUtil.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 194734)
+ *     Matthew Hall - bug 265727
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class PropertyObservableUtil {
+	/**
+	 * Causes the target observable to be disposed when the source observable is
+	 * disposed.
+	 * 
+	 * @param source
+	 *            the source observable
+	 * @param target
+	 *            the target observable
+	 */
+	public static void cascadeDispose(IObservable source,
+			final IObservable target) {
+		source.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				target.dispose();
+			}
+		});
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/SetPropertyDetailValuesMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/SetPropertyDetailValuesMap.java
new file mode 100644
index 0000000..98308c4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/SetPropertyDetailValuesMap.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.map.MapProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SetPropertyDetailValuesMap extends MapProperty {
+	private final ISetProperty masterProperty;
+	private final IValueProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public SetPropertyDetailValuesMap(ISetProperty masterProperty,
+			IValueProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getKeyType() {
+		return masterProperty.getElementType();
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	protected Map doGetMap(Object source) {
+		Set set = masterProperty.getSet(source);
+		Map map = new IdentityMap();
+		for (Iterator it = set.iterator(); it.hasNext();) {
+			Object key = it.next();
+			map.put(key, detailProperty.getValue(key));
+		}
+		return map;
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		if (!diff.getAddedKeys().isEmpty())
+			throw new UnsupportedOperationException(toString()
+					+ " does not support entry additions"); //$NON-NLS-1$
+		if (!diff.getRemovedKeys().isEmpty())
+			throw new UnsupportedOperationException(toString()
+					+ " does not support entry removals"); //$NON-NLS-1$
+		for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) {
+			Object key = it.next();
+			Object newValue = diff.getNewValue(key);
+			detailProperty.setValue(key, newValue);
+		}
+	}
+
+	public IObservableMap observe(Realm realm, Object source) {
+		IObservableSet masterSet;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterSet = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterSet);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterSet);
+		return detailMap;
+	}
+
+	public IObservableMap observeDetail(IObservableValue master) {
+		IObservableSet masterSet;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterSet = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterSet);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterSet);
+		return detailMap;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/Util.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/Util.java
new file mode 100644
index 0000000..18f78cc
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/Util.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+/**
+ * @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 boolean equals(final Object left, final Object right) {
+		return left == null ? right == null : ((right != null) && left
+				.equals(right));
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailList.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailList.java
new file mode 100644
index 0000000..095edf4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailList.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.list.ListProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ValuePropertyDetailList extends ListProperty {
+	private final IValueProperty masterProperty;
+	private final IListProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public ValuePropertyDetailList(IValueProperty masterProperty,
+			IListProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getElementType() {
+		return detailProperty.getElementType();
+	}
+
+	protected List doGetList(Object source) {
+		Object masterValue = masterProperty.getValue(source);
+		return detailProperty.getList(masterValue);
+	}
+
+	protected void doSetList(Object source, List list) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.setList(masterValue, list);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.updateList(masterValue, diff);
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableList detailList = detailProperty.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailList, masterValue);
+		return detailList;
+	}
+
+	public IObservableList observeDetail(IObservableValue master) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableList detailList = detailProperty.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailList, masterValue);
+		return detailList;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailMap.java
new file mode 100644
index 0000000..1abdcec
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailMap.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.map.IMapProperty;
+import org.eclipse.core.databinding.property.map.MapProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ValuePropertyDetailMap extends MapProperty {
+	private final IValueProperty masterProperty;
+	private final IMapProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public ValuePropertyDetailMap(IValueProperty masterProperty,
+			IMapProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getKeyType() {
+		return detailProperty.getKeyType();
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	protected Map doGetMap(Object source) {
+		Object masterValue = masterProperty.getValue(source);
+		return detailProperty.getMap(masterValue);
+	}
+
+	protected void doSetMap(Object source, Map map) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.setMap(masterValue, map);
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.updateMap(masterValue, diff);
+	}
+
+	public IObservableMap observe(Realm realm, Object source) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterValue);
+		return detailMap;
+	}
+
+	public IObservableMap observeDetail(IObservableValue master) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterValue);
+		return detailMap;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailSet.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailSet.java
new file mode 100644
index 0000000..6e363fa
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailSet.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.set.SetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ValuePropertyDetailSet extends SetProperty {
+	private IValueProperty masterProperty;
+	private ISetProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public ValuePropertyDetailSet(IValueProperty masterProperty,
+			ISetProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getElementType() {
+		return detailProperty.getElementType();
+	}
+
+	protected Set doGetSet(Object source) {
+		Object masterValue = masterProperty.getValue(source);
+		return detailProperty.getSet(masterValue);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.setSet(masterValue, set);
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.updateSet(masterValue, diff);
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableSet detailSet = detailProperty.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailSet, masterValue);
+		return detailSet;
+	}
+
+	public IObservableSet observeDetail(IObservableValue master) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableSet detailSet = detailProperty.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailSet, masterValue);
+		return detailSet;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailValue.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailValue.java
new file mode 100644
index 0000000..27cf02c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/ValuePropertyDetailValue.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+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.databinding.property.value.IValueProperty;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+
+/**
+ * @since 1.2
+ * 
+ */
+public class ValuePropertyDetailValue extends ValueProperty implements
+		IValueProperty {
+	private IValueProperty masterProperty;
+	private IValueProperty detailProperty;
+
+	/**
+	 * @param masterProperty
+	 * @param detailProperty
+	 */
+	public ValuePropertyDetailValue(IValueProperty masterProperty,
+			IValueProperty detailProperty) {
+		this.masterProperty = masterProperty;
+		this.detailProperty = detailProperty;
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	protected Object doGetValue(Object source) {
+		Object masterValue = masterProperty.getValue(source);
+		return detailProperty.getValue(masterValue);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		Object masterValue = masterProperty.getValue(source);
+		detailProperty.setValue(masterValue, value);
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observe(realm, source);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableValue detailValue = detailProperty
+				.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailValue, masterValue);
+		return detailValue;
+	}
+
+	public IObservableValue observeDetail(IObservableValue master) {
+		IObservableValue masterValue;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterValue = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableValue detailValue = detailProperty
+				.observeDetail(masterValue);
+		PropertyObservableUtil.cascadeDispose(detailValue, masterValue);
+		return detailValue;
+	}
+
+	public IObservableList observeDetail(IObservableList master) {
+		IObservableList masterList;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterList = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableList detailList = detailProperty.observeDetail(masterList);
+		PropertyObservableUtil.cascadeDispose(detailList, masterList);
+		return detailList;
+	}
+
+	public IObservableMap observeDetail(IObservableSet master) {
+		IObservableMap masterMap;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterMap = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterMap);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterMap);
+		return detailMap;
+	}
+
+	public IObservableMap observeDetail(IObservableMap master) {
+		IObservableMap masterMap;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			masterMap = masterProperty.observeDetail(master);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		IObservableMap detailMap = detailProperty.observeDetail(masterMap);
+		PropertyObservableUtil.cascadeDispose(detailMap, masterMap);
+		return detailMap;
+	}
+
+	public String toString() {
+		return masterProperty + " => " + detailProperty; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/list/SelfListProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/list/SelfListProperty.java
new file mode 100644
index 0000000..63a7137
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/list/SelfListProperty.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 263868)
+ *     Matthew Hall - bug 268203
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.list;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SelfListProperty extends SimpleListProperty {
+	private final Object elementType;
+
+	/**
+	 * @param elementType
+	 */
+	public SelfListProperty(Object elementType) {
+		this.elementType = elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected List doGetList(Object source) {
+		return (List) source;
+	}
+
+	protected void doSetList(Object source, List list, ListDiff diff) {
+		doUpdateList(source, diff);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		diff.applyTo((List) source);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null; // no listener API
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/list/SimplePropertyObservableList.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/list/SimplePropertyObservableList.java
new file mode 100644
index 0000000..609ab05
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/list/SimplePropertyObservableList.java
@@ -0,0 +1,606 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 265561, 262287, 268203, 268688, 301774
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.list;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+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.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.AbstractObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+
+/**
+ * @since 1.2
+ * 
+ */
+public class SimplePropertyObservableList extends AbstractObservableList
+		implements IPropertyObservable {
+	private Object source;
+	private SimpleListProperty property;
+
+	private volatile boolean updating = false;
+
+	private volatile int modCount = 0;
+
+	private INativePropertyListener listener;
+
+	private List cachedList;
+	private boolean stale;
+
+	/**
+	 * @param realm
+	 * @param source
+	 * @param property
+	 */
+	public SimplePropertyObservableList(Realm realm, Object source,
+			SimpleListProperty property) {
+		super(realm);
+		this.source = source;
+		this.property = property;
+	}
+
+	protected void firstListenerAdded() {
+		if (!isDisposed()) {
+			if (listener == null) {
+				listener = property
+						.adaptListener(new ISimplePropertyListener() {
+							public void handleEvent(
+									final SimplePropertyEvent event) {
+								if (!isDisposed() && !updating) {
+									getRealm().exec(new Runnable() {
+										public void run() {
+											if (event.type == SimplePropertyEvent.CHANGE) {
+												modCount++;
+												notifyIfChanged((ListDiff) event.diff);
+											} else if (event.type == SimplePropertyEvent.STALE
+													&& !stale) {
+												stale = true;
+												fireStale();
+											}
+										}
+									});
+								}
+							}
+						});
+			}
+
+			getRealm().exec(new Runnable() {
+				public void run() {
+					cachedList = new ArrayList(getList());
+					stale = false;
+
+					if (listener != null)
+						listener.addTo(source);
+				}
+			});
+		}
+	}
+
+	protected void lastListenerRemoved() {
+		if (listener != null)
+			listener.removeFrom(source);
+
+		cachedList = null;
+		stale = false;
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public Object getElementType() {
+		return property.getElementType();
+	}
+
+	// Queries
+
+	private List getList() {
+		return property.getList(source);
+	}
+
+	protected int doGetSize() {
+		return getList().size();
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+		return getList().contains(o);
+	}
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return getList().containsAll(c);
+	}
+
+	public Object get(int index) {
+		getterCalled();
+		return getList().get(index);
+	}
+
+	public int indexOf(Object o) {
+		getterCalled();
+		return getList().indexOf(o);
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return getList().isEmpty();
+	}
+
+	public int lastIndexOf(Object o) {
+		getterCalled();
+		return getList().lastIndexOf(o);
+	}
+
+	public Object[] toArray() {
+		getterCalled();
+		return getList().toArray();
+	}
+
+	public Object[] toArray(Object[] a) {
+		getterCalled();
+		return getList().toArray(a);
+	}
+
+	// Single change operations
+
+	private void updateList(List list, ListDiff diff) {
+		if (!diff.isEmpty()) {
+			boolean wasUpdating = updating;
+			updating = true;
+			try {
+				property.updateList(source, diff);
+				modCount++;
+			} finally {
+				updating = wasUpdating;
+			}
+
+			notifyIfChanged(null);
+		}
+	}
+
+	public boolean add(Object o) {
+		checkRealm();
+
+		List list = getList();
+
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(list
+				.size(), true, o));
+		updateList(list, diff);
+
+		return true;
+	}
+
+	public void add(int index, Object o) {
+		checkRealm();
+
+		List list = getList();
+
+		if (index < 0 || index > list.size())
+			throw new IndexOutOfBoundsException();
+
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index,
+				true, o));
+		updateList(list, diff);
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return new Iterator() {
+			int expectedModCount = modCount;
+			List list = new ArrayList(getList());
+			ListIterator iterator = list.listIterator();
+
+			Object lastElement = null;
+			int lastIndex = -1;
+
+			public boolean hasNext() {
+				getterCalled();
+				checkForComodification();
+				return iterator.hasNext();
+			}
+
+			public Object next() {
+				getterCalled();
+				checkForComodification();
+				Object next = lastElement = iterator.next();
+				lastIndex = iterator.previousIndex();
+				return next;
+			}
+
+			public void remove() {
+				checkRealm();
+				checkForComodification();
+				if (lastIndex == -1)
+					throw new IllegalStateException();
+
+				iterator.remove(); // stay in sync
+				ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(
+						lastIndex, false, lastElement));
+
+				updateList(list, diff);
+
+				lastElement = null;
+				lastIndex = -1;
+
+				expectedModCount = modCount;
+			}
+
+			private void checkForComodification() {
+				if (expectedModCount != modCount)
+					throw new ConcurrentModificationException();
+			}
+		};
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		checkRealm();
+
+		List list = getList();
+		int size = list.size();
+		if (oldIndex < 0 || oldIndex >= size || newIndex < 0
+				|| newIndex >= size)
+			throw new IndexOutOfBoundsException();
+
+		if (oldIndex == newIndex)
+			return list.get(oldIndex);
+
+		Object element = list.get(oldIndex);
+
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(
+				oldIndex, false, element), Diffs.createListDiffEntry(newIndex,
+				true, element));
+		updateList(list, diff);
+
+		return element;
+	}
+
+	public boolean remove(Object o) {
+		checkRealm();
+
+		List list = getList();
+
+		int index = list.indexOf(o);
+		if (index == -1)
+			return false;
+
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index,
+				false, o));
+		updateList(list, diff);
+
+		return true;
+	}
+
+	public ListIterator listIterator() {
+		return listIterator(0);
+	}
+
+	public ListIterator listIterator(final int index) {
+		getterCalled();
+		return new ListIterator() {
+			int expectedModCount = modCount;
+			List list = new ArrayList(getList());
+			ListIterator iterator = list.listIterator(index);
+
+			Object lastElement = null;
+			int lastIndex = -1;
+
+			public boolean hasNext() {
+				getterCalled();
+				checkForComodification();
+				return iterator.hasNext();
+			}
+
+			public int nextIndex() {
+				getterCalled();
+				checkForComodification();
+				return iterator.nextIndex();
+			}
+
+			public Object next() {
+				getterCalled();
+				checkForComodification();
+				lastElement = iterator.next();
+				lastIndex = iterator.previousIndex();
+				return lastElement;
+			}
+
+			public boolean hasPrevious() {
+				getterCalled();
+				checkForComodification();
+				return iterator.hasPrevious();
+			}
+
+			public int previousIndex() {
+				getterCalled();
+				checkForComodification();
+				return iterator.previousIndex();
+			}
+
+			public Object previous() {
+				getterCalled();
+				checkForComodification();
+				lastElement = iterator.previous();
+				lastIndex = iterator.nextIndex();
+				return lastElement;
+			}
+
+			public void add(Object o) {
+				checkRealm();
+				checkForComodification();
+				int index = iterator.nextIndex();
+
+				ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(
+						index, true, o));
+				updateList(list, diff);
+
+				iterator.add(o); // keep in sync
+
+				lastElement = null;
+				lastIndex = -1;
+				expectedModCount = modCount;
+			}
+
+			public void set(Object o) {
+				checkRealm();
+				checkForComodification();
+
+				ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(
+						lastIndex, false, lastElement), Diffs
+						.createListDiffEntry(lastIndex, true, o));
+				updateList(list, diff);
+
+				iterator.set(o);
+
+				lastElement = o;
+				expectedModCount = modCount;
+			}
+
+			public void remove() {
+				checkRealm();
+				checkForComodification();
+				if (lastIndex == -1)
+					throw new IllegalStateException();
+
+				ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(
+						lastIndex, false, lastElement));
+				updateList(list, diff);
+
+				iterator.remove(); // keep in sync
+
+				lastElement = null;
+				lastIndex = -1;
+				expectedModCount = modCount;
+			}
+
+			private void checkForComodification() {
+				if (expectedModCount != modCount)
+					throw new ConcurrentModificationException();
+			}
+		};
+	}
+
+	public Object remove(int index) {
+		checkRealm();
+
+		List list = getList();
+		Object element = list.get(index);
+
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index,
+				false, element));
+		updateList(list, diff);
+
+		return element;
+	}
+
+	public Object set(int index, Object o) {
+		checkRealm();
+
+		List list = getList();
+		Object oldElement = list.get(index);
+
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index,
+				false, oldElement), Diffs.createListDiffEntry(index, true, o));
+		updateList(list, diff);
+
+		return oldElement;
+	}
+
+	public List subList(int fromIndex, int toIndex) {
+		getterCalled();
+		return Collections.unmodifiableList(getList().subList(fromIndex,
+				toIndex));
+	}
+
+	// Bulk change operations
+
+	public boolean addAll(Collection c) {
+		checkRealm();
+
+		if (c.isEmpty())
+			return false;
+
+		List list = getList();
+		return addAll(list, list.size(), c);
+	}
+
+	public boolean addAll(int index, Collection c) {
+		checkRealm();
+
+		if (c.isEmpty())
+			return false;
+
+		return addAll(getList(), index, c);
+	}
+
+	private boolean addAll(List list, int index, Collection c) {
+		if (index < 0 || index > list.size())
+			throw new IndexOutOfBoundsException();
+
+		ListDiffEntry[] entries = new ListDiffEntry[c.size()];
+		int offsetIndex = 0;
+		for (Iterator it = c.iterator(); it.hasNext();) {
+			Object element = it.next();
+			entries[offsetIndex] = Diffs.createListDiffEntry(index
+					+ offsetIndex, true, element);
+			offsetIndex++;
+		}
+		ListDiff diff = Diffs.createListDiff(entries);
+
+		updateList(list, diff);
+
+		return true;
+	}
+
+	public boolean removeAll(Collection c) {
+		checkRealm();
+
+		if (c.isEmpty())
+			return false;
+
+		List list = getList();
+		if (list.isEmpty())
+			return false;
+
+		List entries = new ArrayList();
+		for (ListIterator it = list.listIterator(); it.hasNext();) {
+			int index = it.nextIndex() - entries.size();
+			Object element = it.next();
+			if (c.contains(element)) {
+				entries.add(Diffs.createListDiffEntry(index, false, element));
+			}
+		}
+
+		if (entries.isEmpty())
+			return false;
+
+		ListDiff diff = Diffs.createListDiff((ListDiffEntry[]) entries
+				.toArray(new ListDiffEntry[entries.size()]));
+		updateList(list, diff);
+
+		return true;
+	}
+
+	public boolean retainAll(Collection c) {
+		checkRealm();
+
+		List list = getList();
+		if (list.isEmpty())
+			return false;
+
+		if (c.isEmpty()) {
+			clear();
+			return true;
+		}
+
+		List entries = new ArrayList();
+		for (ListIterator it = list.listIterator(); it.hasNext();) {
+			int index = it.nextIndex() - entries.size();
+			Object element = it.next();
+			if (!c.contains(element)) {
+				entries.add(Diffs.createListDiffEntry(index, false, element));
+			}
+		}
+
+		if (entries.isEmpty())
+			return false;
+
+		ListDiff diff = Diffs.createListDiff((ListDiffEntry[]) entries
+				.toArray(new ListDiffEntry[entries.size()]));
+		updateList(list, diff);
+
+		return true;
+	}
+
+	public void clear() {
+		checkRealm();
+
+		List list = getList();
+		if (list.isEmpty())
+			return;
+
+		List entries = new ArrayList();
+		for (ListIterator it = list.listIterator(list.size()); it.hasPrevious();) {
+			// always report 0 as the remove index
+			int index = it.previousIndex();
+			Object element = it.previous();
+			entries.add(Diffs.createListDiffEntry(index, false, element));
+		}
+		ListDiff diff = Diffs.createListDiff((ListDiffEntry[]) entries
+				.toArray(new ListDiffEntry[entries.size()]));
+
+		updateList(list, diff);
+	}
+
+	private void notifyIfChanged(ListDiff diff) {
+		if (hasListeners()) {
+			List oldList = cachedList;
+			List newList = cachedList = new ArrayList(getList());
+			if (diff == null)
+				diff = Diffs.computeListDiff(oldList, newList);
+			if (!diff.isEmpty() || stale) {
+				stale = false;
+				fireListChange(diff);
+			}
+		}
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return stale;
+	}
+
+	public boolean equals(Object o) {
+		getterCalled();
+		return getList().equals(o);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return getList().hashCode();
+	}
+
+	public Object getObserved() {
+		return source;
+	}
+
+	public IProperty getProperty() {
+		return property;
+	}
+
+	public synchronized void dispose() {
+		if (!isDisposed()) {
+			if (listener != null)
+				listener.removeFrom(source);
+			property = null;
+			source = null;
+			listener = null;
+			stale = false;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/map/SelfMapProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/map/SelfMapProperty.java
new file mode 100644
index 0000000..c693607
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/map/SelfMapProperty.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 263868)
+ *     Matthew Hall - bug 268203
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.map;
+
+import java.util.Map;
+
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.map.SimpleMapProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class SelfMapProperty extends SimpleMapProperty {
+	private final Object keyType;
+	private final Object valueType;
+
+	/**
+	 * @param keyType
+	 * @param valueType
+	 */
+	public SelfMapProperty(Object keyType, Object valueType) {
+		this.keyType = keyType;
+		this.valueType = valueType;
+	}
+
+	public Object getKeyType() {
+		return keyType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Map doGetMap(Object source) {
+		return (Map) source;
+	}
+
+	protected void doSetMap(Object source, Map map, MapDiff diff) {
+		doUpdateMap(source, diff);
+	}
+
+	protected void doUpdateMap(Object source, MapDiff diff) {
+		diff.applyTo((Map) source);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null; // no listener API
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/map/SimplePropertyObservableMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/map/SimplePropertyObservableMap.java
new file mode 100644
index 0000000..df7fd0d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/map/SimplePropertyObservableMap.java
@@ -0,0 +1,340 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 265561, 262287, 268203, 268688, 301774, 303847
+ *     Ovidio Mallo - bug 332367
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.map;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+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.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.map.SimpleMapProperty;
+
+/**
+ * @since 1.2
+ */
+public class SimplePropertyObservableMap extends AbstractObservableMap
+		implements IPropertyObservable {
+	private Object source;
+	private SimpleMapProperty property;
+
+	private volatile boolean updating = false;
+
+	private volatile int modCount = 0;
+
+	private INativePropertyListener listener;
+
+	private Map cachedMap;
+	private boolean stale;
+
+	/**
+	 * @param realm
+	 * @param source
+	 * @param property
+	 */
+	public SimplePropertyObservableMap(Realm realm, Object source,
+			SimpleMapProperty property) {
+		super(realm);
+		this.source = source;
+		this.property = property;
+	}
+
+	public Object getKeyType() {
+		return property.getKeyType();
+	}
+
+	public Object getValueType() {
+		return property.getValueType();
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	protected void firstListenerAdded() {
+		if (!isDisposed()) {
+			if (listener == null) {
+				listener = property
+						.adaptListener(new ISimplePropertyListener() {
+							public void handleEvent(
+									final SimplePropertyEvent event) {
+								if (!isDisposed() && !updating) {
+									getRealm().exec(new Runnable() {
+										public void run() {
+											if (event.type == SimplePropertyEvent.CHANGE) {
+												modCount++;
+												notifyIfChanged((MapDiff) event.diff);
+											} else if (event.type == SimplePropertyEvent.STALE
+													&& !stale) {
+												stale = true;
+												fireStale();
+											}
+										}
+									});
+								}
+							}
+						});
+			}
+
+			getRealm().exec(new Runnable() {
+				public void run() {
+					cachedMap = new HashMap(getMap());
+					stale = false;
+
+					if (listener != null)
+						listener.addTo(source);
+				}
+			});
+		}
+	}
+
+	protected void lastListenerRemoved() {
+		if (listener != null)
+			listener.removeFrom(source);
+
+		cachedMap.clear();
+		cachedMap = null;
+		stale = false;
+	}
+
+	// Queries
+
+	private Map getMap() {
+		return property.getMap(source);
+	}
+
+	// Single change operations
+
+	private void updateMap(Map map, MapDiff diff) {
+		if (!diff.isEmpty()) {
+			boolean wasUpdating = updating;
+			updating = true;
+			try {
+				property.updateMap(source, diff);
+				modCount++;
+			} finally {
+				updating = wasUpdating;
+			}
+
+			notifyIfChanged(null);
+		}
+	}
+
+	private EntrySet es = new EntrySet();
+
+	public Set entrySet() {
+		getterCalled();
+		return es;
+	}
+
+	private class EntrySet extends AbstractSet {
+		public Iterator iterator() {
+			return new EntrySetIterator();
+		}
+
+		public int size() {
+			return getMap().size();
+		}
+	}
+
+	private class EntrySetIterator implements Iterator {
+		private volatile int expectedModCount = modCount;
+		Map map = new HashMap(getMap());
+		Iterator iterator = map.entrySet().iterator();
+		Map.Entry last = null;
+
+		public boolean hasNext() {
+			getterCalled();
+			checkForComodification();
+			return iterator.hasNext();
+		}
+
+		public Object next() {
+			getterCalled();
+			checkForComodification();
+			last = (Map.Entry) iterator.next();
+			return last;
+		}
+
+		public void remove() {
+			getterCalled();
+			checkForComodification();
+
+			MapDiff diff = Diffs.createMapDiffSingleRemove(last.getKey(),
+					last.getValue());
+			updateMap(map, diff);
+
+			iterator.remove(); // stay in sync
+
+			last = null;
+			expectedModCount = modCount;
+		}
+
+		private void checkForComodification() {
+			if (expectedModCount != modCount)
+				throw new ConcurrentModificationException();
+		}
+	}
+
+	public Set keySet() {
+		getterCalled();
+		// AbstractMap depends on entrySet() to fulfil keySet() API, so all
+		// getterCalled() and comodification checks will still be handled
+		return super.keySet();
+	}
+
+	public boolean containsKey(Object key) {
+		getterCalled();
+
+		return getMap().containsKey(key);
+	}
+
+	public Object get(Object key) {
+		getterCalled();
+
+		return getMap().get(key);
+	}
+
+	public Object put(Object key, Object value) {
+		checkRealm();
+
+		Map map = getMap();
+
+		boolean add = !map.containsKey(key);
+
+		Object oldValue = map.get(key);
+
+		MapDiff diff;
+		if (add)
+			diff = Diffs.createMapDiffSingleAdd(key, value);
+		else
+			diff = Diffs.createMapDiffSingleChange(key, oldValue, value);
+
+		updateMap(map, diff);
+
+		return oldValue;
+	}
+
+	public void putAll(Map m) {
+		checkRealm();
+
+		Map map = getMap();
+
+		Map oldValues = new HashMap();
+		Map newValues = new HashMap();
+		Set changedKeys = new HashSet();
+		Set addedKeys = new HashSet();
+		for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Map.Entry) it.next();
+			Object key = entry.getKey();
+			Object newValue = entry.getValue();
+			if (map.containsKey(key)) {
+				changedKeys.add(key);
+				oldValues.put(key, map.get(key));
+			} else {
+				addedKeys.add(key);
+			}
+			newValues.put(key, newValue);
+		}
+
+		MapDiff diff = Diffs.createMapDiff(addedKeys, Collections.EMPTY_SET,
+				changedKeys, oldValues, newValues);
+		updateMap(map, diff);
+	}
+
+	public Object remove(Object key) {
+		checkRealm();
+
+		Map map = getMap();
+		if (!map.containsKey(key))
+			return null;
+
+		Object oldValue = map.get(key);
+
+		MapDiff diff = Diffs.createMapDiffSingleRemove(key, oldValue);
+		updateMap(map, diff);
+
+		return oldValue;
+	}
+
+	public void clear() {
+		getterCalled();
+
+		Map map = getMap();
+		if (map.isEmpty())
+			return;
+
+		MapDiff diff = Diffs.createMapDiffRemoveAll(new HashMap(map));
+		updateMap(map, diff);
+	}
+
+	public Collection values() {
+		getterCalled();
+		// AbstractMap depends on entrySet() to fulfil values() API, so all
+		// getterCalled() and comodification checks will still be handled
+		return super.values();
+	}
+
+	private void notifyIfChanged(MapDiff diff) {
+		if (hasListeners()) {
+			Map oldMap = cachedMap;
+			Map newMap = cachedMap = new HashMap(getMap());
+			if (diff == null)
+				diff = Diffs.computeMapDiff(oldMap, newMap);
+			if (!diff.isEmpty() || stale) {
+				stale = false;
+				fireMapChange(diff);
+			}
+		}
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return stale;
+	}
+
+	public Object getObserved() {
+		return source;
+	}
+
+	public IProperty getProperty() {
+		return property;
+	}
+
+	public synchronized void dispose() {
+		if (!isDisposed()) {
+			if (listener != null)
+				listener.removeFrom(source);
+			property = null;
+			source = null;
+			listener = null;
+			stale = false;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/set/SelfSetProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/set/SelfSetProperty.java
new file mode 100644
index 0000000..dd76d50
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/set/SelfSetProperty.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263868)
+ *     Matthew Hall - bug 268203
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.set;
+
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class SelfSetProperty extends SimpleSetProperty {
+	private final Object elementType;
+
+	/**
+	 * @param elementType
+	 */
+	public SelfSetProperty(Object elementType) {
+		this.elementType = elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected Set doGetSet(Object source) {
+		return (Set) source;
+	}
+
+	protected void doSetSet(Object source, Set set, SetDiff diff) {
+		diff.applyTo((Set) source);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null; // no listener API
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/set/SimplePropertyObservableSet.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/set/SimplePropertyObservableSet.java
new file mode 100644
index 0000000..4e851ab
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/set/SimplePropertyObservableSet.java
@@ -0,0 +1,363 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 265561, 262287, 268203, 268688, 301774
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.set;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+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.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+
+/**
+ * @since 1.2
+ * 
+ */
+public class SimplePropertyObservableSet extends AbstractObservableSet
+		implements IPropertyObservable {
+	private Object source;
+	private SimpleSetProperty property;
+
+	private volatile boolean updating = false;
+
+	private volatile int modCount = 0;
+
+	private INativePropertyListener listener;
+
+	private Set cachedSet;
+	private boolean stale;
+
+	/**
+	 * @param realm
+	 * @param source
+	 * @param property
+	 */
+	public SimplePropertyObservableSet(Realm realm, Object source,
+			SimpleSetProperty property) {
+		super(realm);
+		this.source = source;
+		this.property = property;
+	}
+
+	protected void firstListenerAdded() {
+		if (!isDisposed()) {
+			if (listener == null) {
+				listener = property
+						.adaptListener(new ISimplePropertyListener() {
+							public void handleEvent(
+									final SimplePropertyEvent event) {
+								if (!isDisposed() && !updating) {
+									getRealm().exec(new Runnable() {
+										public void run() {
+											if (event.type == SimplePropertyEvent.CHANGE) {
+												modCount++;
+												notifyIfChanged((SetDiff) event.diff);
+											} else if (event.type == SimplePropertyEvent.STALE
+													&& !stale) {
+												stale = true;
+												fireStale();
+											}
+										}
+									});
+								}
+							}
+						});
+			}
+
+			getRealm().exec(new Runnable() {
+				public void run() {
+					cachedSet = new HashSet(getSet());
+					stale = false;
+
+					if (listener != null)
+						listener.addTo(source);
+				}
+			});
+		}
+	}
+
+	protected void lastListenerRemoved() {
+		if (listener != null)
+			listener.removeFrom(source);
+
+		cachedSet.clear();
+		cachedSet = null;
+		stale = false;
+	}
+
+	protected Set getWrappedSet() {
+		return getSet();
+	}
+
+	public Object getElementType() {
+		return property.getElementType();
+	}
+
+	// Queries
+
+	private Set getSet() {
+		return property.getSet(source);
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+		return getSet().contains(o);
+	}
+
+	public boolean containsAll(Collection c) {
+		getterCalled();
+		return getSet().containsAll(c);
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return getSet().isEmpty();
+	}
+
+	public Object[] toArray() {
+		getterCalled();
+		return getSet().toArray();
+	}
+
+	public Object[] toArray(Object[] a) {
+		getterCalled();
+		return getSet().toArray(a);
+	}
+
+	// Single change operations
+
+	private void updateSet(Set set, SetDiff diff) {
+		if (!diff.isEmpty()) {
+			boolean wasUpdating = updating;
+			updating = true;
+			try {
+				property.updateSet(source, diff);
+				modCount++;
+			} finally {
+				updating = wasUpdating;
+			}
+
+			notifyIfChanged(null);
+		}
+	}
+
+	public boolean add(Object o) {
+		checkRealm();
+
+		Set set = getSet();
+		if (set.contains(o))
+			return false;
+
+		SetDiff diff = Diffs.createSetDiff(Collections.singleton(o),
+				Collections.EMPTY_SET);
+		updateSet(set, diff);
+
+		return true;
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return new Iterator() {
+			int expectedModCount = modCount;
+			Set set = new HashSet(getSet());
+			Iterator iterator = set.iterator();
+			Object last = null;
+
+			public boolean hasNext() {
+				getterCalled();
+				checkForComodification();
+				return iterator.hasNext();
+			}
+
+			public Object next() {
+				getterCalled();
+				checkForComodification();
+				last = iterator.next();
+				return last;
+			}
+
+			public void remove() {
+				checkRealm();
+				checkForComodification();
+
+				SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET,
+						Collections.singleton(last));
+				updateSet(set, diff);
+
+				iterator.remove(); // stay in sync
+
+				last = null;
+				expectedModCount = modCount;
+			}
+
+			private void checkForComodification() {
+				if (expectedModCount != modCount)
+					throw new ConcurrentModificationException();
+			}
+		};
+	}
+
+	public boolean remove(Object o) {
+		getterCalled();
+
+		Set set = getSet();
+		if (!set.contains(o))
+			return false;
+
+		SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, Collections
+				.singleton(o));
+		updateSet(set, diff);
+
+		return true;
+	}
+
+	// Bulk change operations
+
+	public boolean addAll(Collection c) {
+		getterCalled();
+
+		if (c.isEmpty())
+			return false;
+
+		Set set = getSet();
+		if (set.containsAll(c))
+			return false;
+
+		Set additions = new HashSet(c);
+		additions.removeAll(set);
+
+		if (additions.isEmpty())
+			return false;
+
+		SetDiff diff = Diffs.createSetDiff(additions, Collections.EMPTY_SET);
+		updateSet(set, diff);
+
+		return true;
+	}
+
+	public boolean removeAll(Collection c) {
+		getterCalled();
+
+		if (c.isEmpty())
+			return false;
+
+		Set set = getSet();
+		if (set.isEmpty())
+			return false;
+
+		Set removals = new HashSet(c);
+		removals.retainAll(set);
+
+		if (removals.isEmpty())
+			return false;
+
+		SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, removals);
+		updateSet(set, diff);
+
+		return true;
+	}
+
+	public boolean retainAll(Collection c) {
+		getterCalled();
+
+		Set set = getSet();
+		if (set.isEmpty())
+			return false;
+
+		if (c.isEmpty()) {
+			clear();
+			return true;
+		}
+
+		Set removals = new HashSet(set);
+		removals.removeAll(c);
+
+		if (removals.isEmpty())
+			return false;
+
+		SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, removals);
+		updateSet(set, diff);
+
+		return true;
+	}
+
+	public void clear() {
+		getterCalled();
+
+		Set set = getSet();
+		if (set.isEmpty())
+			return;
+
+		SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, set);
+		updateSet(set, diff);
+	}
+
+	private void notifyIfChanged(SetDiff diff) {
+		if (hasListeners()) {
+			Set oldSet = cachedSet;
+			Set newSet = cachedSet = new HashSet(getSet());
+			if (diff == null)
+				diff = Diffs.computeSetDiff(oldSet, newSet);
+			if (!diff.isEmpty() || stale) {
+				stale = false;
+				fireSetChange(diff);
+			}
+		}
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return stale;
+	}
+
+	public boolean equals(Object o) {
+		getterCalled();
+		return getSet().equals(o);
+	}
+
+	public int hashCode() {
+		getterCalled();
+		return getSet().hashCode();
+	}
+
+	public Object getObserved() {
+		return source;
+	}
+
+	public IProperty getProperty() {
+		return property;
+	}
+
+	public synchronized void dispose() {
+		if (!isDisposed()) {
+			if (listener != null)
+				listener.removeFrom(source);
+			property = null;
+			source = null;
+			listener = null;
+			stale = false;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/DelegatingCache.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/DelegatingCache.java
new file mode 100644
index 0000000..af820e5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/DelegatingCache.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 194734)
+ *     Matthew Hall - bugs 262269, 281727, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ISetChangeListener;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.identity.IdentityObservableSet;
+
+/**
+ * @since 3.3
+ * 
+ */
+abstract class DelegatingCache {
+	private Realm realm;
+	private DelegatingValueProperty detailProperty;
+	private IObservableSet elements;
+	private Map delegateCaches;
+
+	private class DelegateCache implements IMapChangeListener {
+		private final IValueProperty delegate;
+		private final IObservableSet masterElements;
+		private final IObservableMap masterElementValues;
+		private final Map cachedValues;
+
+		DelegateCache(IValueProperty delegate) {
+			this.delegate = delegate;
+			ObservableTracker.setIgnore(true);
+			try {
+				this.masterElements = new IdentityObservableSet(realm, elements
+						.getElementType());
+				this.masterElementValues = delegate
+						.observeDetail(masterElements);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+			this.cachedValues = new IdentityMap();
+
+			masterElementValues.addMapChangeListener(this);
+		}
+
+		void add(Object masterElement) {
+			boolean wasEmpty = masterElements.isEmpty();
+
+			masterElements.add(masterElement);
+			cachedValues.put(masterElement, masterElementValues
+					.get(masterElement));
+
+			if (wasEmpty)
+				delegateCaches.put(delegate, this);
+		}
+
+		void remove(Object masterElement) {
+			cachedValues.remove(masterElement);
+			masterElements.remove(masterElement);
+			if (cachedValues.isEmpty())
+				dispose();
+		}
+
+		Object get(Object masterElement) {
+			return cachedValues.get(masterElement);
+		}
+
+		Object put(Object masterElement, Object detailValue) {
+			Object oldValue = masterElementValues.put(masterElement,
+					detailValue);
+			notifyIfChanged(masterElement);
+			return oldValue;
+		}
+
+		boolean containsValue(Object detailValue) {
+			return cachedValues.containsValue(detailValue);
+		}
+
+		public void handleMapChange(MapChangeEvent event) {
+			Set changedKeys = event.diff.getChangedKeys();
+			for (Iterator it = changedKeys.iterator(); it.hasNext();)
+				notifyIfChanged(it.next());
+		}
+
+		private void notifyIfChanged(Object masterElement) {
+			Object oldValue = cachedValues.get(masterElement);
+			Object newValue = masterElementValues.get(masterElement);
+			if (oldValue != newValue) {
+				cachedValues.put(masterElement, newValue);
+				handleValueChange(masterElement, oldValue, newValue);
+			}
+		}
+
+		void handleValueChange(Object masterElement, Object oldValue,
+				Object newValue) {
+			DelegatingCache.this.handleValueChange(masterElement, oldValue,
+					newValue);
+		}
+
+		void dispose() {
+			delegateCaches.remove(delegate);
+			masterElementValues.dispose();
+			masterElements.dispose();
+			cachedValues.clear();
+		}
+	}
+
+	DelegatingCache(Realm realm, DelegatingValueProperty detailProperty) {
+		this.realm = realm;
+		this.detailProperty = detailProperty;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			this.elements = new IdentityObservableSet(realm, null);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		this.delegateCaches = new IdentityMap();
+
+		elements.addSetChangeListener(new ISetChangeListener() {
+			public void handleSetChange(SetChangeEvent event) {
+				for (Iterator it = event.diff.getRemovals().iterator(); it
+						.hasNext();) {
+					Object element = it.next();
+					getCache(element).remove(element);
+
+				}
+				for (Iterator it = event.diff.getAdditions().iterator(); it
+						.hasNext();) {
+					Object element = it.next();
+					getCache(element).add(element);
+				}
+			}
+		});
+	}
+
+	private DelegateCache getCache(Object masterElement) {
+		IValueProperty delegate = detailProperty.getDelegate(masterElement);
+		if (delegateCaches.containsKey(delegate)) {
+			return (DelegateCache) delegateCaches.get(delegate);
+		}
+		return new DelegateCache(delegate);
+	}
+
+	Object get(Object element) {
+		return getCache(element).get(element);
+	}
+
+	Object put(Object element, Object value) {
+		return getCache(element).put(element, value);
+	}
+
+	boolean containsValue(Object value) {
+		for (Iterator it = delegateCaches.values().iterator(); it.hasNext();) {
+			DelegateCache cache = (DelegateCache) it.next();
+			if (cache.containsValue(value))
+				return true;
+		}
+		return false;
+	}
+
+	void addAll(Collection elements) {
+		this.elements.addAll(elements);
+	}
+
+	void retainAll(Collection elements) {
+		this.elements.retainAll(elements);
+	}
+
+	abstract void handleValueChange(Object masterElement, Object oldValue,
+			Object newValue);
+
+	void dispose() {
+		if (elements != null) {
+			elements.clear(); // clears caches
+			elements.dispose();
+			elements = null;
+		}
+
+		if (delegateCaches != null) {
+			for (Iterator it = delegateCaches.values().iterator(); it.hasNext();) {
+				DelegateCache cache = (DelegateCache) it.next();
+				cache.dispose();
+			}
+			delegateCaches.clear();
+			delegateCaches = null;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ListDelegatingValueObservableList.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ListDelegatingValueObservableList.java
new file mode 100644
index 0000000..e436263
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ListDelegatingValueObservableList.java
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262269
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+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.AbstractObservableList;
+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.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+
+/**
+ * @since 1.2
+ */
+public class ListDelegatingValueObservableList extends AbstractObservableList
+		implements IPropertyObservable {
+	private IObservableList masterList;
+	private DelegatingValueProperty detailProperty;
+	private DelegatingCache cache;
+
+	private IListChangeListener masterListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			if (isDisposed())
+				return;
+
+			cache.addAll(masterList);
+
+			// Need both obsolete and new elements to convert diff
+			ListDiff diff = convertDiff(event.diff);
+
+			cache.retainAll(masterList);
+
+			fireListChange(diff);
+		}
+
+		private ListDiff convertDiff(ListDiff diff) {
+			// Convert diff to detail value
+			ListDiffEntry[] masterEntries = diff.getDifferences();
+			ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
+			for (int i = 0; i < masterEntries.length; i++) {
+				ListDiffEntry masterDifference = masterEntries[i];
+				int index = masterDifference.getPosition();
+				boolean addition = masterDifference.isAddition();
+				Object masterElement = masterDifference.getElement();
+				Object detailValue = cache.get(masterElement);
+
+				detailEntries[i] = Diffs.createListDiffEntry(index, addition,
+						detailValue);
+			}
+			return Diffs.createListDiff(detailEntries);
+		}
+	};
+
+	private IStaleListener staleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	/**
+	 * @param masterList
+	 * @param valueProperty
+	 */
+	public ListDelegatingValueObservableList(IObservableList masterList,
+			DelegatingValueProperty valueProperty) {
+		super(masterList.getRealm());
+		this.masterList = masterList;
+		this.detailProperty = valueProperty;
+		this.cache = new DelegatingCache(getRealm(), valueProperty) {
+			void handleValueChange(Object masterElement, Object oldValue,
+					Object newValue) {
+				fireListChange(indicesOf(masterElement), oldValue, newValue);
+			}
+		};
+		cache.addAll(masterList);
+
+		masterList.addListChangeListener(masterListener);
+		masterList.addStaleListener(staleListener);
+	}
+
+	protected int doGetSize() {
+		getterCalled();
+		return masterList.size();
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public Object get(int index) {
+		getterCalled();
+		Object masterElement = masterList.get(index);
+		return cache.get(masterElement);
+	}
+
+	public boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(int index, Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+		return cache.containsValue(o);
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return masterList.isEmpty();
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return masterList.isStale();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return new Iterator() {
+			Iterator it = masterList.iterator();
+
+			public boolean hasNext() {
+				getterCalled();
+				return it.hasNext();
+			}
+
+			public Object next() {
+				getterCalled();
+				Object masterElement = it.next();
+				return cache.get(masterElement);
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object[] toArray() {
+		getterCalled();
+		Object[] masterElements = masterList.toArray();
+		Object[] result = new Object[masterElements.length];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = cache.get(masterElements[i]);
+		}
+		return result;
+	}
+
+	public Object[] toArray(Object[] a) {
+		getterCalled();
+		Object[] masterElements = masterList.toArray();
+		if (a.length < masterElements.length)
+			a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
+					masterElements.length);
+		for (int i = 0; i < masterElements.length; i++) {
+			a[i] = cache.get(masterElements[i]);
+		}
+		return a;
+	}
+
+	public void add(int index, Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	public ListIterator listIterator() {
+		return listIterator(0);
+	}
+
+	public ListIterator listIterator(final int index) {
+		getterCalled();
+		return new ListIterator() {
+			ListIterator it = masterList.listIterator(index);
+			Object lastMasterElement;
+			Object lastElement;
+			boolean haveIterated = false;
+
+			public void add(Object arg0) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean hasNext() {
+				getterCalled();
+				return it.hasNext();
+			}
+
+			public boolean hasPrevious() {
+				getterCalled();
+				return it.hasPrevious();
+			}
+
+			public Object next() {
+				getterCalled();
+				lastMasterElement = it.next();
+				lastElement = cache.get(lastMasterElement);
+				haveIterated = true;
+				return lastElement;
+			}
+
+			public int nextIndex() {
+				getterCalled();
+				return it.nextIndex();
+			}
+
+			public Object previous() {
+				getterCalled();
+				lastMasterElement = it.previous();
+				lastElement = cache.get(lastMasterElement);
+				haveIterated = true;
+				return lastElement;
+			}
+
+			public int previousIndex() {
+				getterCalled();
+				return it.previousIndex();
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			public void set(Object o) {
+				checkRealm();
+				if (!haveIterated)
+					throw new IllegalStateException();
+
+				cache.put(lastMasterElement, o);
+
+				lastElement = o;
+			}
+		};
+	}
+
+	private int[] indicesOf(Object masterElement) {
+		List indices = new ArrayList();
+
+		for (ListIterator it = masterList.listIterator(); it.hasNext();) {
+			if (masterElement == it.next())
+				indices.add(new Integer(it.previousIndex()));
+		}
+
+		int[] result = new int[indices.size()];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = ((Integer) indices.get(i)).intValue();
+		}
+		return result;
+	}
+
+	private void fireListChange(int[] indices, Object oldValue, Object newValue) {
+		ListDiffEntry[] differences = new ListDiffEntry[indices.length * 2];
+		for (int i = 0; i < indices.length; i++) {
+			int index = indices[i];
+			differences[i * 2] = Diffs.createListDiffEntry(index, false,
+					oldValue);
+			differences[i * 2 + 1] = Diffs.createListDiffEntry(index, true,
+					newValue);
+		}
+		fireListChange(Diffs.createListDiff(differences));
+	}
+
+	public Object remove(int index) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object set(int index, Object o) {
+		checkRealm();
+		Object masterElement = masterList.get(index);
+		return cache.put(masterElement, o);
+	}
+
+	public Object getObserved() {
+		return masterList;
+	}
+
+	public IProperty getProperty() {
+		return detailProperty;
+	}
+
+	public Object getElementType() {
+		return detailProperty.getValueType();
+	}
+
+	public synchronized void dispose() {
+		if (masterList != null) {
+			masterList.removeListChangeListener(masterListener);
+			masterList.removeStaleListener(staleListener);
+			masterList = null;
+		}
+
+		if (cache != null) {
+			cache.dispose();
+			cache = null;
+		}
+
+		masterListener = null;
+		detailProperty = null;
+
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ListSimpleValueObservableList.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ListSimpleValueObservableList.java
new file mode 100644
index 0000000..54a0821
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ListSimpleValueObservableList.java
@@ -0,0 +1,478 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 262269, 265561, 262287, 268688, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+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.AbstractObservableList;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ISetChangeListener;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.identity.IdentityObservableSet;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * @since 1.2
+ */
+public class ListSimpleValueObservableList extends AbstractObservableList
+		implements IPropertyObservable {
+	private IObservableList masterList;
+	private SimpleValueProperty detailProperty;
+
+	private IObservableSet knownMasterElements;
+	private Map cachedValues;
+	private Set staleElements;
+
+	private boolean updating;
+
+	private IListChangeListener masterListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			if (!isDisposed()) {
+				updateKnownElements();
+				fireListChange(convertDiff(event.diff));
+			}
+		}
+
+		private void updateKnownElements() {
+			Set identityKnownElements = new IdentitySet(masterList);
+			knownMasterElements.retainAll(identityKnownElements);
+			knownMasterElements.addAll(identityKnownElements);
+		}
+
+		private ListDiff convertDiff(ListDiff diff) {
+			// Convert diff to detail value
+			ListDiffEntry[] masterEntries = diff.getDifferences();
+			ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
+			for (int i = 0; i < masterEntries.length; i++) {
+				ListDiffEntry masterDifference = masterEntries[i];
+				int index = masterDifference.getPosition();
+				boolean addition = masterDifference.isAddition();
+				Object masterElement = masterDifference.getElement();
+				Object elementDetailValue = detailProperty
+						.getValue(masterElement);
+				detailEntries[i] = Diffs.createListDiffEntry(index, addition,
+						elementDetailValue);
+			}
+			return Diffs.createListDiff(detailEntries);
+		}
+	};
+
+	private IStaleListener staleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private INativePropertyListener detailListener;
+
+	/**
+	 * @param masterList
+	 * @param valueProperty
+	 */
+	public ListSimpleValueObservableList(IObservableList masterList,
+			SimpleValueProperty valueProperty) {
+		super(masterList.getRealm());
+		this.masterList = masterList;
+		this.detailProperty = valueProperty;
+
+		ISimplePropertyListener listener = new ISimplePropertyListener() {
+			public void handleEvent(final SimplePropertyEvent event) {
+				if (!isDisposed() && !updating) {
+					getRealm().exec(new Runnable() {
+						public void run() {
+							if (event.type == SimplePropertyEvent.CHANGE) {
+								notifyIfChanged(event.getSource());
+							} else if (event.type == SimplePropertyEvent.STALE) {
+								boolean wasStale = !staleElements.isEmpty();
+								staleElements.add(event.getSource());
+								if (!wasStale)
+									fireStale();
+							}
+						}
+					});
+				}
+			}
+		};
+		this.detailListener = detailProperty.adaptListener(listener);
+	}
+
+	protected void firstListenerAdded() {
+		ObservableTracker.setIgnore(true);
+		try {
+			knownMasterElements = new IdentityObservableSet(getRealm(), null);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		cachedValues = new IdentityMap();
+		staleElements = new IdentitySet();
+		knownMasterElements.addSetChangeListener(new ISetChangeListener() {
+			public void handleSetChange(SetChangeEvent event) {
+				for (Iterator it = event.diff.getRemovals().iterator(); it
+						.hasNext();) {
+					Object key = it.next();
+					if (detailListener != null)
+						detailListener.removeFrom(key);
+					cachedValues.remove(key);
+					staleElements.remove(key);
+				}
+				for (Iterator it = event.diff.getAdditions().iterator(); it
+						.hasNext();) {
+					Object key = it.next();
+					cachedValues.put(key, detailProperty.getValue(key));
+					if (detailListener != null)
+						detailListener.addTo(key);
+				}
+			}
+		});
+		getRealm().exec(new Runnable() {
+			public void run() {
+				knownMasterElements.addAll(masterList);
+
+				masterList.addListChangeListener(masterListener);
+				masterList.addStaleListener(staleListener);
+			}
+		});
+	}
+
+	protected void lastListenerRemoved() {
+		if (masterList != null) {
+			masterList.removeListChangeListener(masterListener);
+			masterList.removeStaleListener(staleListener);
+		}
+		if (knownMasterElements != null) {
+			knownMasterElements.dispose();
+			knownMasterElements = null;
+		}
+		if (cachedValues != null) {
+			cachedValues.clear();
+			cachedValues = null;
+		}
+		if (staleElements != null) {
+			staleElements.clear();
+			staleElements = null;
+		}
+	}
+
+	protected int doGetSize() {
+		getterCalled();
+		return masterList.size();
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public Object getElementType() {
+		return detailProperty.getValueType();
+	}
+
+	public Object get(int index) {
+		getterCalled();
+		Object masterElement = masterList.get(index);
+		return detailProperty.getValue(masterElement);
+	}
+
+	public boolean add(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean addAll(int index, Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean contains(Object o) {
+		getterCalled();
+
+		for (Iterator it = masterList.iterator(); it.hasNext();) {
+			if (Util.equals(detailProperty.getValue(it.next()), o))
+				return true;
+		}
+		return false;
+	}
+
+	public boolean isEmpty() {
+		getterCalled();
+		return masterList.isEmpty();
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return masterList.isStale() || staleElements != null
+				&& !staleElements.isEmpty();
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		return new Iterator() {
+			Iterator it = masterList.iterator();
+
+			public boolean hasNext() {
+				getterCalled();
+				return it.hasNext();
+			}
+
+			public Object next() {
+				getterCalled();
+				Object masterElement = it.next();
+				return detailProperty.getValue(masterElement);
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	public Object move(int oldIndex, int newIndex) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean remove(Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean removeAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public boolean retainAll(Collection c) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object[] toArray() {
+		getterCalled();
+		Object[] masterElements = masterList.toArray();
+		Object[] result = new Object[masterElements.length];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = detailProperty.getValue(masterElements[i]);
+		}
+		return result;
+	}
+
+	public Object[] toArray(Object[] a) {
+		getterCalled();
+		Object[] masterElements = masterList.toArray();
+		if (a.length < masterElements.length)
+			a = (Object[]) Array.newInstance(a.getClass().getComponentType(),
+					masterElements.length);
+		for (int i = 0; i < masterElements.length; i++) {
+			a[i] = detailProperty.getValue(masterElements[i]);
+		}
+		return a;
+	}
+
+	public void add(int index, Object o) {
+		throw new UnsupportedOperationException();
+	}
+
+	public void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	public ListIterator listIterator() {
+		return listIterator(0);
+	}
+
+	public ListIterator listIterator(final int index) {
+		getterCalled();
+		return new ListIterator() {
+			ListIterator it = masterList.listIterator(index);
+			Object lastMasterElement;
+			Object lastElement;
+			boolean haveIterated = false;
+
+			public void add(Object arg0) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean hasNext() {
+				getterCalled();
+				return it.hasNext();
+			}
+
+			public boolean hasPrevious() {
+				getterCalled();
+				return it.hasPrevious();
+			}
+
+			public Object next() {
+				getterCalled();
+				lastMasterElement = it.next();
+				lastElement = detailProperty.getValue(lastMasterElement);
+				haveIterated = true;
+				return lastElement;
+			}
+
+			public int nextIndex() {
+				getterCalled();
+				return it.nextIndex();
+			}
+
+			public Object previous() {
+				getterCalled();
+				lastMasterElement = it.previous();
+				lastElement = detailProperty.getValue(lastMasterElement);
+				haveIterated = true;
+				return lastElement;
+			}
+
+			public int previousIndex() {
+				getterCalled();
+				return it.previousIndex();
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+
+			public void set(Object o) {
+				checkRealm();
+				if (!haveIterated)
+					throw new IllegalStateException();
+
+				boolean wasUpdating = updating;
+				updating = true;
+				try {
+					detailProperty.setValue(lastElement, o);
+				} finally {
+					updating = wasUpdating;
+				}
+
+				notifyIfChanged(lastMasterElement);
+
+				lastElement = o;
+			}
+		};
+	}
+
+	private void notifyIfChanged(Object masterElement) {
+		if (cachedValues != null) {
+			Object oldValue = cachedValues.get(masterElement);
+			Object newValue = detailProperty.getValue(masterElement);
+			if (!Util.equals(oldValue, newValue)
+					|| staleElements.contains(masterElement)) {
+				cachedValues.put(masterElement, newValue);
+				staleElements.remove(masterElement);
+				fireListChange(indicesOf(masterElement), oldValue, newValue);
+			}
+		}
+	}
+
+	private int[] indicesOf(Object masterElement) {
+		List indices = new ArrayList();
+
+		for (ListIterator it = ListSimpleValueObservableList.this.masterList
+				.listIterator(); it.hasNext();) {
+			if (masterElement == it.next())
+				indices.add(new Integer(it.previousIndex()));
+		}
+
+		int[] result = new int[indices.size()];
+		for (int i = 0; i < result.length; i++) {
+			result[i] = ((Integer) indices.get(i)).intValue();
+		}
+		return result;
+	}
+
+	private void fireListChange(int[] indices, Object oldValue, Object newValue) {
+		ListDiffEntry[] differences = new ListDiffEntry[indices.length * 2];
+		for (int i = 0; i < indices.length; i++) {
+			int index = indices[i];
+			differences[i * 2] = Diffs.createListDiffEntry(index, false,
+					oldValue);
+			differences[i * 2 + 1] = Diffs.createListDiffEntry(index, true,
+					newValue);
+		}
+		fireListChange(Diffs.createListDiff(differences));
+	}
+
+	public Object remove(int index) {
+		throw new UnsupportedOperationException();
+	}
+
+	public Object set(int index, Object o) {
+		checkRealm();
+		Object masterElement = masterList.get(index);
+		Object oldValue = detailProperty.getValue(masterElement);
+
+		boolean wasUpdating = updating;
+		updating = true;
+		try {
+			detailProperty.setValue(masterElement, o);
+		} finally {
+			updating = wasUpdating;
+		}
+
+		notifyIfChanged(masterElement);
+
+		return oldValue;
+	}
+
+	public Object getObserved() {
+		return masterList;
+	}
+
+	public IProperty getProperty() {
+		return detailProperty;
+	}
+
+	public synchronized void dispose() {
+		if (knownMasterElements != null) {
+			knownMasterElements.clear(); // detaches listeners
+			knownMasterElements.dispose();
+			knownMasterElements = null;
+		}
+
+		if (masterList != null) {
+			masterList.removeListChangeListener(masterListener);
+			masterList = null;
+		}
+
+		masterListener = null;
+		detailListener = null;
+		detailProperty = null;
+		cachedValues = null;
+		staleElements = null;
+
+		super.dispose();
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/MapDelegatingValueObservableMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/MapDelegatingValueObservableMap.java
new file mode 100644
index 0000000..feb2d38
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/MapDelegatingValueObservableMap.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+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.IStaleListener;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
+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.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * @since 1.2
+ */
+public class MapDelegatingValueObservableMap extends AbstractObservableMap
+		implements IPropertyObservable {
+	private IObservableMap masterMap;
+	private DelegatingValueProperty detailProperty;
+	private DelegatingCache cache;
+
+	private Set entrySet;
+
+	class EntrySet extends AbstractSet {
+		public Iterator iterator() {
+			return new Iterator() {
+				Iterator it = masterMap.entrySet().iterator();
+
+				public boolean hasNext() {
+					getterCalled();
+					return it.hasNext();
+				}
+
+				public Object next() {
+					getterCalled();
+					Map.Entry next = (Map.Entry) it.next();
+					return new MapEntry(next.getKey());
+				}
+
+				public void remove() {
+					it.remove();
+				}
+			};
+		}
+
+		public int size() {
+			return masterMap.size();
+		}
+	}
+
+	class MapEntry implements Map.Entry {
+		private Object key;
+
+		MapEntry(Object key) {
+			this.key = key;
+		}
+
+		public Object getKey() {
+			getterCalled();
+			return key;
+		}
+
+		public Object getValue() {
+			getterCalled();
+
+			if (!masterMap.containsKey(key))
+				return null;
+
+			Object masterValue = masterMap.get(key);
+			return cache.get(masterValue);
+		}
+
+		public Object setValue(Object value) {
+			checkRealm();
+
+			if (!masterMap.containsKey(key))
+				return null;
+
+			Object masterValue = masterMap.get(key);
+			return cache.put(masterValue, value);
+		}
+
+		public boolean equals(Object o) {
+			getterCalled();
+			if (o == this)
+				return true;
+			if (o == null)
+				return false;
+			if (!(o instanceof Map.Entry))
+				return false;
+			Map.Entry that = (Map.Entry) o;
+			return Util.equals(this.getKey(), that.getKey())
+					&& Util.equals(this.getValue(), that.getValue());
+		}
+
+		public int hashCode() {
+			getterCalled();
+			Object value = getValue();
+			return (key == null ? 0 : key.hashCode())
+					^ (value == null ? 0 : value.hashCode());
+		}
+	}
+
+	private IMapChangeListener masterListener = new IMapChangeListener() {
+		public void handleMapChange(final MapChangeEvent event) {
+			if (isDisposed())
+				return;
+
+			cache.addAll(masterMap.values());
+
+			// Need both obsolete and new master values to convert diff
+			MapDiff diff = convertDiff(event.diff);
+
+			cache.retainAll(masterMap.values());
+
+			fireMapChange(diff);
+		}
+
+		private MapDiff convertDiff(MapDiff diff) {
+			Map oldValues = new HashMap();
+			Map newValues = new HashMap();
+
+			Set addedKeys = diff.getAddedKeys();
+			for (Iterator it = addedKeys.iterator(); it.hasNext();) {
+				Object key = it.next();
+				Object masterValue = diff.getNewValue(key);
+				Object newValue = cache.get(masterValue);
+				newValues.put(key, newValue);
+			}
+
+			Set removedKeys = diff.getRemovedKeys();
+			for (Iterator it = removedKeys.iterator(); it.hasNext();) {
+				Object key = it.next();
+				Object masterValue = diff.getOldValue(key);
+				Object oldValue = cache.get(masterValue);
+				oldValues.put(key, oldValue);
+			}
+
+			Set changedKeys = new HashSet(diff.getChangedKeys());
+			for (Iterator it = changedKeys.iterator(); it.hasNext();) {
+				Object key = it.next();
+
+				Object oldMasterValue = diff.getOldValue(key);
+				Object newMasterValue = diff.getNewValue(key);
+
+				Object oldValue = cache.get(oldMasterValue);
+				Object newValue = cache.get(newMasterValue);
+
+				if (Util.equals(oldValue, newValue)) {
+					it.remove();
+				} else {
+					oldValues.put(key, oldValue);
+					newValues.put(key, newValue);
+				}
+			}
+
+			return Diffs.createMapDiff(addedKeys, removedKeys, changedKeys,
+					oldValues, newValues);
+		}
+	};
+
+	private IStaleListener staleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	/**
+	 * @param map
+	 * @param valueProperty
+	 */
+	public MapDelegatingValueObservableMap(IObservableMap map,
+			DelegatingValueProperty valueProperty) {
+		super(map.getRealm());
+		this.masterMap = map;
+		this.detailProperty = valueProperty;
+		this.cache = new DelegatingCache(getRealm(), valueProperty) {
+			void handleValueChange(Object masterElement, Object oldValue,
+					Object newValue) {
+				fireMapChange(keysFor(masterElement), oldValue, newValue);
+			}
+		};
+		cache.addAll(masterMap.values());
+
+		masterMap.addMapChangeListener(masterListener);
+		masterMap.addStaleListener(staleListener);
+	}
+
+	public Set entrySet() {
+		getterCalled();
+		if (entrySet == null)
+			entrySet = new EntrySet();
+		return entrySet;
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public Object get(Object key) {
+		getterCalled();
+		Object masterValue = masterMap.get(key);
+		return cache.get(masterValue);
+	}
+
+	public Object put(Object key, Object value) {
+		if (!masterMap.containsKey(key))
+			return null;
+		Object masterValue = masterMap.get(key);
+		return cache.put(masterValue, value);
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return masterMap.isStale();
+	}
+
+	public Object getObserved() {
+		return masterMap;
+	}
+
+	public IProperty getProperty() {
+		return detailProperty;
+	}
+
+	public Object getKeyType() {
+		return masterMap.getKeyType();
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	private Set keysFor(Object masterValue) {
+		Set keys = new HashSet();
+
+		for (Iterator it = masterMap.entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Entry) it.next();
+			if (entry.getValue() == masterValue) {
+				keys.add(entry.getKey());
+			}
+		}
+
+		return keys;
+	}
+
+	private void fireMapChange(final Set changedKeys, final Object oldValue,
+			final Object newValue) {
+		fireMapChange(new MapDiff() {
+			public Set getAddedKeys() {
+				return Collections.EMPTY_SET;
+			}
+
+			public Set getRemovedKeys() {
+				return Collections.EMPTY_SET;
+			}
+
+			public Set getChangedKeys() {
+				return Collections.unmodifiableSet(changedKeys);
+			}
+
+			public Object getOldValue(Object key) {
+				if (changedKeys.contains(key))
+					return oldValue;
+				return null;
+			}
+
+			public Object getNewValue(Object key) {
+				if (changedKeys.contains(key))
+					return newValue;
+				return null;
+			}
+		});
+	}
+
+	public synchronized void dispose() {
+		if (masterMap != null) {
+			masterMap.removeMapChangeListener(masterListener);
+			masterMap.removeStaleListener(staleListener);
+			masterMap = null;
+		}
+
+		if (cache != null) {
+			cache.dispose();
+			cache = null;
+		}
+
+		masterListener = null;
+		detailProperty = null;
+
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/MapSimpleValueObservableMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/MapSimpleValueObservableMap.java
new file mode 100644
index 0000000..d6e24f2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/MapSimpleValueObservableMap.java
@@ -0,0 +1,432 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 262269, 265561, 262287, 268688, 278550, 303847
+ *     Ovidio Mallo - bugs 299619, 301370
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import java.util.AbstractSet;
+import java.util.Collections;
+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.IStaleListener;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ISetChangeListener;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.identity.IdentityObservableSet;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * @since 1.2
+ * 
+ */
+public class MapSimpleValueObservableMap extends AbstractObservableMap
+		implements IPropertyObservable {
+	private IObservableMap masterMap;
+	private SimpleValueProperty detailProperty;
+
+	private IObservableSet knownMasterValues;
+	private Map cachedValues;
+	private Set staleMasterValues;
+
+	private boolean updating = false;
+
+	private IMapChangeListener masterListener = new IMapChangeListener() {
+		public void handleMapChange(final MapChangeEvent event) {
+			if (!isDisposed()) {
+				updateKnownValues();
+				if (!updating)
+					fireMapChange(convertDiff(event.diff));
+			}
+		}
+
+		private void updateKnownValues() {
+			Set knownValues = new IdentitySet(masterMap.values());
+			knownMasterValues.retainAll(knownValues);
+			knownMasterValues.addAll(knownValues);
+		}
+
+		private MapDiff convertDiff(MapDiff diff) {
+			Map oldValues = new IdentityMap();
+			Map newValues = new IdentityMap();
+
+			Set addedKeys = diff.getAddedKeys();
+			for (Iterator it = addedKeys.iterator(); it.hasNext();) {
+				Object key = it.next();
+				Object newSource = diff.getNewValue(key);
+				Object newValue = detailProperty.getValue(newSource);
+				newValues.put(key, newValue);
+			}
+
+			Set removedKeys = diff.getRemovedKeys();
+			for (Iterator it = removedKeys.iterator(); it.hasNext();) {
+				Object key = it.next();
+				Object oldSource = diff.getOldValue(key);
+				Object oldValue = detailProperty.getValue(oldSource);
+				oldValues.put(key, oldValue);
+			}
+
+			Set changedKeys = new IdentitySet(diff.getChangedKeys());
+			for (Iterator it = changedKeys.iterator(); it.hasNext();) {
+				Object key = it.next();
+
+				Object oldSource = diff.getOldValue(key);
+				Object newSource = diff.getNewValue(key);
+
+				Object oldValue = detailProperty.getValue(oldSource);
+				Object newValue = detailProperty.getValue(newSource);
+
+				if (Util.equals(oldValue, newValue)) {
+					it.remove();
+				} else {
+					oldValues.put(key, oldValue);
+					newValues.put(key, newValue);
+				}
+			}
+
+			return Diffs.createMapDiff(addedKeys, removedKeys, changedKeys,
+					oldValues, newValues);
+		}
+	};
+
+	private IStaleListener staleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private INativePropertyListener detailListener;
+
+	/**
+	 * @param map
+	 * @param valueProperty
+	 */
+	public MapSimpleValueObservableMap(IObservableMap map,
+			SimpleValueProperty valueProperty) {
+		super(map.getRealm());
+		this.masterMap = map;
+		this.detailProperty = valueProperty;
+
+		ISimplePropertyListener listener = new ISimplePropertyListener() {
+			public void handleEvent(final SimplePropertyEvent event) {
+				if (!isDisposed() && !updating) {
+					getRealm().exec(new Runnable() {
+						public void run() {
+							if (event.type == SimplePropertyEvent.CHANGE) {
+								notifyIfChanged(event.getSource());
+							} else if (event.type == SimplePropertyEvent.STALE) {
+								boolean wasStale = !staleMasterValues.isEmpty();
+								staleMasterValues.add(event.getSource());
+								if (!wasStale)
+									fireStale();
+							}
+						}
+					});
+				}
+			}
+		};
+		this.detailListener = detailProperty.adaptListener(listener);
+	}
+
+	public Object getKeyType() {
+		return masterMap.getKeyType();
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	protected void firstListenerAdded() {
+		ObservableTracker.setIgnore(true);
+		try {
+			knownMasterValues = new IdentityObservableSet(getRealm(), null);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		cachedValues = new IdentityMap();
+		staleMasterValues = new IdentitySet();
+		knownMasterValues.addSetChangeListener(new ISetChangeListener() {
+			public void handleSetChange(SetChangeEvent event) {
+				for (Iterator it = event.diff.getRemovals().iterator(); it
+						.hasNext();) {
+					Object key = it.next();
+					if (detailListener != null)
+						detailListener.removeFrom(key);
+					cachedValues.remove(key);
+					staleMasterValues.remove(key);
+				}
+				for (Iterator it = event.diff.getAdditions().iterator(); it
+						.hasNext();) {
+					Object key = it.next();
+					cachedValues.put(key, detailProperty.getValue(key));
+					if (detailListener != null)
+						detailListener.addTo(key);
+				}
+			}
+		});
+
+		getRealm().exec(new Runnable() {
+			public void run() {
+				knownMasterValues.addAll(masterMap.values());
+
+				masterMap.addMapChangeListener(masterListener);
+				masterMap.addStaleListener(staleListener);
+			}
+		});
+	}
+
+	protected void lastListenerRemoved() {
+		masterMap.removeMapChangeListener(masterListener);
+		masterMap.removeStaleListener(staleListener);
+		if (knownMasterValues != null) {
+			knownMasterValues.dispose();
+			knownMasterValues = null;
+		}
+		cachedValues.clear();
+		cachedValues = null;
+		staleMasterValues.clear();
+		staleMasterValues = null;
+	}
+
+	private Set entrySet;
+
+	public Set entrySet() {
+		getterCalled();
+		if (entrySet == null)
+			entrySet = new EntrySet();
+		return entrySet;
+	}
+
+	class EntrySet extends AbstractSet {
+		public Iterator iterator() {
+			return new Iterator() {
+				Iterator it = masterMap.entrySet().iterator();
+
+				public boolean hasNext() {
+					getterCalled();
+					return it.hasNext();
+				}
+
+				public Object next() {
+					getterCalled();
+					Map.Entry next = (Map.Entry) it.next();
+					return new MapEntry(next.getKey());
+				}
+
+				public void remove() {
+					it.remove();
+				}
+			};
+		}
+
+		public int size() {
+			return masterMap.size();
+		}
+	}
+
+	class MapEntry implements Map.Entry {
+		private Object key;
+
+		MapEntry(Object key) {
+			this.key = key;
+		}
+
+		public Object getKey() {
+			getterCalled();
+			return key;
+		}
+
+		public Object getValue() {
+			getterCalled();
+			if (!masterMap.containsKey(key))
+				return null;
+			return detailProperty.getValue(masterMap.get(key));
+		}
+
+		public Object setValue(Object value) {
+			if (!masterMap.containsKey(key))
+				return null;
+			Object source = masterMap.get(key);
+
+			Object oldValue = detailProperty.getValue(source);
+
+			updating = true;
+			try {
+				detailProperty.setValue(source, value);
+			} finally {
+				updating = false;
+			}
+
+			notifyIfChanged(source);
+
+			return oldValue;
+		}
+
+		public boolean equals(Object o) {
+			getterCalled();
+			if (o == this)
+				return true;
+			if (o == null)
+				return false;
+			if (!(o instanceof Map.Entry))
+				return false;
+			Map.Entry that = (Map.Entry) o;
+			return Util.equals(this.getKey(), that.getKey())
+					&& Util.equals(this.getValue(), that.getValue());
+		}
+
+		public int hashCode() {
+			getterCalled();
+			Object value = getValue();
+			return (key == null ? 0 : key.hashCode())
+					^ (value == null ? 0 : value.hashCode());
+		}
+	}
+
+	public boolean containsKey(Object key) {
+		getterCalled();
+
+		return masterMap.containsKey(key);
+	}
+
+	public Object get(Object key) {
+		getterCalled();
+
+		return detailProperty.getValue(masterMap.get(key));
+	}
+
+	public Object put(Object key, Object value) {
+		if (!masterMap.containsKey(key))
+			return null;
+		Object masterValue = masterMap.get(key);
+		Object oldValue = detailProperty.getValue(masterValue);
+		detailProperty.setValue(masterValue, value);
+		notifyIfChanged(masterValue);
+		return oldValue;
+	}
+
+	public Object remove(Object key) {
+		checkRealm();
+
+		Object masterValue = masterMap.get(key);
+		Object oldValue = detailProperty.getValue(masterValue);
+
+		masterMap.remove(key);
+
+		return oldValue;
+	}
+
+	private void notifyIfChanged(Object masterValue) {
+		if (cachedValues != null) {
+			final Set keys = keysFor(masterValue);
+
+			final Object oldValue = cachedValues.get(masterValue);
+			final Object newValue = detailProperty.getValue(masterValue);
+
+			if (!Util.equals(oldValue, newValue)
+					|| staleMasterValues.contains(masterValue)) {
+				cachedValues.put(masterValue, newValue);
+				staleMasterValues.remove(masterValue);
+				fireMapChange(new MapDiff() {
+					public Set getAddedKeys() {
+						return Collections.EMPTY_SET;
+					}
+
+					public Set getChangedKeys() {
+						return keys;
+					}
+
+					public Set getRemovedKeys() {
+						return Collections.EMPTY_SET;
+					}
+
+					public Object getNewValue(Object key) {
+						return newValue;
+					}
+
+					public Object getOldValue(Object key) {
+						return oldValue;
+					}
+				});
+			}
+		}
+	}
+
+	private Set keysFor(Object value) {
+		Set keys = new IdentitySet();
+
+		for (Iterator it = masterMap.entrySet().iterator(); it.hasNext();) {
+			Map.Entry entry = (Entry) it.next();
+			if (entry.getValue() == value) {
+				keys.add(entry.getKey());
+			}
+		}
+
+		return keys;
+	}
+
+	public boolean isStale() {
+		getterCalled();
+		return masterMap.isStale() || staleMasterValues != null
+				&& !staleMasterValues.isEmpty();
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public Object getObserved() {
+		return masterMap;
+	}
+
+	public IProperty getProperty() {
+		return detailProperty;
+	}
+
+	public synchronized void dispose() {
+		if (masterMap != null) {
+			masterMap.removeMapChangeListener(masterListener);
+			masterMap = null;
+		}
+		if (knownMasterValues != null) {
+			knownMasterValues.clear(); // detaches listeners
+			knownMasterValues.dispose();
+			knownMasterValues = null;
+		}
+
+		masterListener = null;
+		detailListener = null;
+		detailProperty = null;
+		cachedValues = null;
+		staleMasterValues = null;
+
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ObservableValueProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ObservableValueProperty.java
new file mode 100644
index 0000000..fc619fd
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/ObservableValueProperty.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263709)
+ *     Matthew Hall - bugs 265561, 262287
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+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.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.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+/*
+ * This class extends SimpleValueProperty rather than ValueProperty to make it
+ * easy to observe multiple IObservableValues, for example an IObservableList of
+ * IObservableValues. In the simple case of observe(Object) or
+ * observeDetail(IObservableValue) we just cast the source object to
+ * IObservableValue and return it.
+ */
+public class ObservableValueProperty extends SimpleValueProperty {
+	private final Object valueType;
+
+	/**
+	 * @param valueType
+	 */
+	public ObservableValueProperty(Object valueType) {
+		this.valueType = valueType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((IObservableValue) source).getValue();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((IObservableValue) source).setValue(value);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return new Listener(this, listener);
+	}
+
+	private class Listener extends NativePropertyListener implements
+			IValueChangeListener, IStaleListener {
+		Listener(IProperty property, ISimplePropertyListener listener) {
+			super(property, listener);
+		}
+
+		public void handleValueChange(ValueChangeEvent event) {
+			fireChange(event.getObservable(), event.diff);
+		}
+
+		public void handleStale(StaleEvent event) {
+			fireStale(event.getObservable());
+		}
+
+		protected void doAddTo(Object source) {
+			IObservableValue observable = (IObservableValue) source;
+			observable.addValueChangeListener(this);
+			observable.addStaleListener(this);
+		}
+
+		protected void doRemoveFrom(Object source) {
+			IObservableValue observable = (IObservableValue) source;
+			observable.removeValueChangeListener(this);
+			observable.removeStaleListener(this);
+		}
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		// Ignore realm if different
+		return (IObservableValue) source;
+	}
+
+	public String toString() {
+		String result = "IObservableValue#value"; //$NON-NLS-1$
+		if (valueType != null)
+			result += " <" + valueType + ">"; //$NON-NLS-1$ //$NON-NLS-2$
+		return result;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SelfValueProperty.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SelfValueProperty.java
new file mode 100644
index 0000000..95743ac
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SelfValueProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263868)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class SelfValueProperty extends SimpleValueProperty {
+	private final Object valueType;
+
+	/**
+	 * @param valueType
+	 */
+	public SelfValueProperty(Object valueType) {
+		this.valueType = valueType;
+	}
+
+	public Object getValueType() {
+		return valueType;
+	}
+
+	protected Object doGetValue(Object source) {
+		return source;
+	}
+
+	protected void doSetValue(Object source, Object value) {
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SetDelegatingValueObservableMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SetDelegatingValueObservableMap.java
new file mode 100644
index 0000000..d2f7ec2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SetDelegatingValueObservableMap.java
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.HashMap;
+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.IStaleListener;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+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.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * @since 1.2
+ */
+public class SetDelegatingValueObservableMap extends AbstractObservableMap
+		implements IPropertyObservable {
+	private IObservableSet masterSet;
+	private DelegatingValueProperty detailProperty;
+	private DelegatingCache cache;
+
+	private Set entrySet;
+
+	class EntrySet extends AbstractSet {
+		public Iterator iterator() {
+			return new Iterator() {
+				final Iterator it = masterSet.iterator();
+
+				public boolean hasNext() {
+					return it.hasNext();
+				}
+
+				public Object next() {
+					return new MapEntry(it.next());
+				}
+
+				public void remove() {
+					it.remove();
+				}
+			};
+		}
+
+		public int size() {
+			return masterSet.size();
+		}
+	}
+
+	class MapEntry implements Map.Entry {
+		private final Object key;
+
+		MapEntry(Object key) {
+			this.key = key;
+		}
+
+		public Object getKey() {
+			getterCalled();
+			return key;
+		}
+
+		public Object getValue() {
+			getterCalled();
+
+			if (!masterSet.contains(key))
+				return null;
+
+			return cache.get(key);
+		}
+
+		public Object setValue(Object value) {
+			checkRealm();
+
+			if (!masterSet.contains(key))
+				return null;
+
+			return cache.put(key, value);
+		}
+
+		public boolean equals(Object o) {
+			getterCalled();
+			if (o == this)
+				return true;
+			if (o == null)
+				return false;
+			if (!(o instanceof Map.Entry))
+				return false;
+			Map.Entry that = (Map.Entry) o;
+			return Util.equals(this.getKey(), that.getKey())
+					&& Util.equals(this.getValue(), that.getValue());
+		}
+
+		public int hashCode() {
+			getterCalled();
+			Object value = getValue();
+			return (key == null ? 0 : key.hashCode())
+					^ (value == null ? 0 : value.hashCode());
+		}
+	}
+
+	private ISetChangeListener masterListener = new ISetChangeListener() {
+		public void handleSetChange(SetChangeEvent event) {
+			if (isDisposed())
+				return;
+
+			cache.addAll(masterSet);
+
+			// Need both obsolete and new elements to convert diff
+			MapDiff diff = convertDiff(event.diff);
+
+			cache.retainAll(masterSet);
+
+			fireMapChange(diff);
+		}
+
+		private MapDiff convertDiff(SetDiff diff) {
+			// Convert diff to detail value
+			Map oldValues = new HashMap();
+			Map newValues = new HashMap();
+
+			for (Iterator it = diff.getRemovals().iterator(); it.hasNext();) {
+				Object masterElement = it.next();
+				oldValues.put(masterElement, cache.get(masterElement));
+			}
+			for (Iterator it = diff.getAdditions().iterator(); it.hasNext();) {
+				Object masterElement = it.next();
+				newValues.put(masterElement, cache.get(masterElement));
+			}
+			return Diffs.createMapDiff(diff.getAdditions(), diff.getRemovals(),
+					Collections.EMPTY_SET, oldValues, newValues);
+		}
+	};
+
+	private IStaleListener staleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	/**
+	 * @param keySet
+	 * @param valueProperty
+	 */
+	public SetDelegatingValueObservableMap(IObservableSet keySet,
+			DelegatingValueProperty valueProperty) {
+		super(keySet.getRealm());
+		this.masterSet = keySet;
+		this.detailProperty = valueProperty;
+		this.cache = new DelegatingCache(getRealm(), valueProperty) {
+			void handleValueChange(Object masterElement, Object oldValue,
+					Object newValue) {
+				fireMapChange(Diffs.createMapDiffSingleChange(masterElement,
+						oldValue, newValue));
+			}
+		};
+		cache.addAll(masterSet);
+
+		masterSet.addSetChangeListener(masterListener);
+		masterSet.addStaleListener(staleListener);
+	}
+
+	public Set entrySet() {
+		getterCalled();
+		if (entrySet == null)
+			entrySet = new EntrySet();
+		return entrySet;
+	}
+
+	private void getterCalled() {
+		ObservableTracker.getterCalled(this);
+	}
+
+	public Object get(Object key) {
+		getterCalled();
+		return cache.get(key);
+	}
+
+	public Object put(Object key, Object value) {
+		checkRealm();
+		return cache.put(key, value);
+	}
+
+	public boolean isStale() {
+		return masterSet.isStale();
+	}
+
+	public Object getObserved() {
+		return masterSet;
+	}
+
+	public IProperty getProperty() {
+		return detailProperty;
+	}
+
+	public Object getKeyType() {
+		return masterSet.getElementType();
+	}
+
+	public Object getValueType() {
+		return detailProperty.getValueType();
+	}
+
+	public synchronized void dispose() {
+		if (masterSet != null) {
+			masterSet.removeSetChangeListener(masterListener);
+			masterSet.removeStaleListener(staleListener);
+			masterSet = null;
+		}
+
+		if (cache != null) {
+			cache.dispose();
+			cache = null;
+		}
+
+		masterListener = null;
+		detailProperty = null;
+
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SetSimpleValueObservableMap.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SetSimpleValueObservableMap.java
new file mode 100644
index 0000000..26a5765
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SetSimpleValueObservableMap.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 262269, 266754, 265561, 262287, 268688
+ *     Ovidio Mallo - bug 299619
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * @since 1.2
+ */
+public class SetSimpleValueObservableMap extends ComputedObservableMap
+		implements IPropertyObservable {
+	private SimpleValueProperty detailProperty;
+
+	private INativePropertyListener listener;
+
+	private Map cachedValues;
+	private Set staleKeys;
+
+	private boolean updating;
+
+	/**
+	 * @param keySet
+	 * @param valueProperty
+	 */
+	public SetSimpleValueObservableMap(IObservableSet keySet,
+			SimpleValueProperty valueProperty) {
+		super(keySet, valueProperty.getValueType());
+		this.detailProperty = valueProperty;
+	}
+
+	protected void firstListenerAdded() {
+		if (listener == null) {
+			listener = detailProperty
+					.adaptListener(new ISimplePropertyListener() {
+						public void handleEvent(final SimplePropertyEvent event) {
+							if (!isDisposed() && !updating) {
+								getRealm().exec(new Runnable() {
+									public void run() {
+										if (event.type == SimplePropertyEvent.CHANGE) {
+											notifyIfChanged(event.getSource());
+										} else if (event.type == SimplePropertyEvent.STALE) {
+											boolean wasStale = !staleKeys
+													.isEmpty();
+											staleKeys.add(event.getSource());
+											if (!wasStale)
+												fireStale();
+										}
+									}
+								});
+							}
+						}
+					});
+		}
+		cachedValues = new IdentityMap();
+		staleKeys = new IdentitySet();
+		super.firstListenerAdded();
+	}
+
+	protected void lastListenerRemoved() {
+		super.lastListenerRemoved();
+		cachedValues.clear();
+		cachedValues = null;
+		staleKeys.clear();
+		staleKeys = null;
+	}
+
+	protected void hookListener(Object addedKey) {
+		if (cachedValues != null) {
+			cachedValues.put(addedKey, detailProperty.getValue(addedKey));
+			if (listener != null)
+				listener.addTo(addedKey);
+		}
+	}
+
+	protected void unhookListener(Object removedKey) {
+		if (cachedValues != null) {
+			if (listener != null)
+				listener.removeFrom(removedKey);
+			cachedValues.remove(removedKey);
+			staleKeys.remove(removedKey);
+		}
+	}
+
+	protected Object doGet(Object key) {
+		return detailProperty.getValue(key);
+	}
+
+	protected Object doPut(Object key, Object value) {
+		Object oldValue = detailProperty.getValue(key);
+
+		updating = true;
+		try {
+			detailProperty.setValue(key, value);
+		} finally {
+			updating = false;
+		}
+
+		notifyIfChanged(key);
+
+		return oldValue;
+	}
+
+	private void notifyIfChanged(Object key) {
+		if (cachedValues != null) {
+			Object oldValue = cachedValues.get(key);
+			Object newValue = detailProperty.getValue(key);
+			if (!Util.equals(oldValue, newValue) || staleKeys.contains(key)) {
+				cachedValues.put(key, newValue);
+				staleKeys.remove(key);
+				fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue,
+						newValue));
+			}
+		}
+	}
+
+	public Object getObserved() {
+		return keySet();
+	}
+
+	public IProperty getProperty() {
+		return detailProperty;
+	}
+
+	public boolean isStale() {
+		return super.isStale() || staleKeys != null && !staleKeys.isEmpty();
+	}
+
+	public synchronized void dispose() {
+		if (cachedValues != null) {
+			cachedValues.clear();
+			cachedValues = null;
+		}
+
+		listener = null;
+		detailProperty = null;
+		cachedValues = null;
+		staleKeys = null;
+
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SimplePropertyObservableValue.java b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SimplePropertyObservableValue.java
new file mode 100644
index 0000000..59ae670
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding.property/src/org/eclipse/core/internal/databinding/property/value/SimplePropertyObservableValue.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 265561, 262287, 268688
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+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.value.AbstractObservableValue;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.core.internal.databinding.property.Util;
+
+/**
+ * @since 1.2
+ * 
+ */
+public class SimplePropertyObservableValue extends AbstractObservableValue
+		implements IPropertyObservable {
+	private Object source;
+	private SimpleValueProperty property;
+
+	private boolean updating = false;
+	private Object cachedValue;
+	private boolean stale;
+
+	private INativePropertyListener listener;
+
+	/**
+	 * @param realm
+	 * @param source
+	 * @param property
+	 */
+	public SimplePropertyObservableValue(Realm realm, Object source,
+			SimpleValueProperty property) {
+		super(realm);
+		this.source = source;
+		this.property = property;
+	}
+
+	protected void firstListenerAdded() {
+		if (!isDisposed()) {
+			if (listener == null) {
+				listener = property
+						.adaptListener(new ISimplePropertyListener() {
+							public void handleEvent(
+									final SimplePropertyEvent event) {
+								if (!isDisposed() && !updating) {
+									getRealm().exec(new Runnable() {
+										public void run() {
+											if (event.type == SimplePropertyEvent.CHANGE) {
+												notifyIfChanged((ValueDiff) event.diff);
+											} else if (event.type == SimplePropertyEvent.STALE
+													&& !stale) {
+												stale = true;
+												fireStale();
+											}
+										}
+									});
+								}
+							}
+						});
+			}
+			getRealm().exec(new Runnable() {
+				public void run() {
+					cachedValue = property.getValue(source);
+					stale = false;
+					if (listener != null)
+						listener.addTo(source);
+				}
+			});
+		}
+	}
+
+	protected void lastListenerRemoved() {
+		if (listener != null)
+			listener.removeFrom(source);
+		cachedValue = null;
+		stale = false;
+	}
+
+	protected Object doGetValue() {
+		notifyIfChanged(null);
+		return property.getValue(source);
+	}
+
+	protected void doSetValue(Object value) {
+		updating = true;
+		try {
+			property.setValue(source, value);
+		} finally {
+			updating = false;
+		}
+
+		notifyIfChanged(null);
+	}
+
+	private void notifyIfChanged(ValueDiff diff) {
+		if (hasListeners()) {
+			Object oldValue = cachedValue;
+			Object newValue = cachedValue = property.getValue(source);
+			if (diff == null)
+				diff = Diffs.createValueDiff(oldValue, newValue);
+			if (!Util.equals(oldValue, newValue) || stale) {
+				stale = false;
+				fireValueChange(diff);
+			}
+		}
+	}
+
+	public Object getValueType() {
+		return property.getValueType();
+	}
+
+	public Object getObserved() {
+		return source;
+	}
+
+	public IProperty getProperty() {
+		return property;
+	}
+
+	public boolean isStale() {
+		ObservableTracker.getterCalled(this);
+		return stale;
+	}
+
+	public synchronized void dispose() {
+		if (!isDisposed()) {
+			if (listener != null)
+				listener.removeFrom(source);
+			source = null;
+			property = null;
+			listener = null;
+			stale = false;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/.classpath b/bundles/org.eclipse.core.databinding/.classpath
new file mode 100644
index 0000000..6f3b481
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.core.databinding/.cvsignore b/bundles/org.eclipse.core.databinding/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.core.databinding/.project b/bundles/org.eclipse.core.databinding/.project
new file mode 100644
index 0000000..a52c8d9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.core.databinding</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.core.databinding/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..9e52c97
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,354 @@
+#Thu Feb 05 11:35:38 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.core.databinding/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..f590c4e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:05:48 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.3\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.core.databinding/.settings/org.eclipse.pde.api.tools.prefs b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 0000000..60843cb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,145 @@
+#Wed Apr 02 17:10:39 EDT 2008
+ANNOTATION_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error
+ANNOTATION_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD_WITH_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_LEAK=Warning
+API_PROFILE_ELEMENT_TYPE_REMOVED_API_COMPONENT=Error
+CLASS_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_ADDED_FIELD=Error
+CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERCLASS_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_CHANGED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+CLASS_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+CLASS_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+CLASS_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+ENUM_ELEMENT_TYPE_ADDED_FIELD=Error
+ENUM_ELEMENT_TYPE_ADDED_METHOD=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+ENUM_ELEMENT_TYPE_CHANGED_TO_INTERFACE=Error
+ENUM_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Ignore
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENTS=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETERS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_ANNOTATION=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_CLASS=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TO_ENUM=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+METHOD_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_ADDED_INTERFACE_BOUNDS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+METHOD_ELEMENT_TYPE_REMOVED_INTERFACE_BOUNDS=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETERS=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
diff --git a/bundles/org.eclipse.core.databinding/.settings/org.eclipse.pde.prefs b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..4a56680
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:49:44 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=0
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=0
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.core.databinding/.settings/org.moreunit.prefs b/bundles/org.eclipse.core.databinding/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..a0824d6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/.settings/org.moreunit.prefs
@@ -0,0 +1,5 @@
+#Tue Feb 02 23:24:59 MST 2010
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=org.eclipse.core.databinding\:src\:org.eclipse.jface.tests.databinding\:src
+org.moreunit.useprojectsettings=true
diff --git a/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7eb6a40
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF
@@ -0,0 +1,27 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.core.databinding
+Bundle-Version: 1.4.0.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.core.databinding,
+ org.eclipse.core.databinding.conversion;x-internal:=false,
+ org.eclipse.core.databinding.validation;x-internal:=false,
+ org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans",
+ org.eclipse.core.internal.databinding.conversion;x-friends:="org.eclipse.jface.tests.databinding",
+ org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding"
+Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.databinding.observable;bundle-version="[1.3.0,2.0.0)";visibility:=reexport,
+ org.eclipse.core.databinding.property;bundle-version="[1.3.0,2.0.0)"
+Import-Package-Comment: see http://wiki.eclipse.org/
+Import-Package: com.ibm.icu.math;resolution:=optional,
+ com.ibm.icu.text,
+ org.osgi.framework;version="[1.4.0,2.0.0)";resolution:=optional,
+ org.osgi.util.tracker;version="[1.3.3,2.0.0)";resolution:=optional,
+ org.eclipse.osgi.framework.log;version="[1.0.0,2.0.0)";resolution:=optional
+Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,
+ J2SE-1.4
+Bundle-Activator: org.eclipse.core.internal.databinding.Activator
+Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.core.databinding/about.html b/bundles/org.eclipse.core.databinding/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/build.properties b/bundles/org.eclipse.core.databinding/build.properties
new file mode 100644
index 0000000..c320187
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2003, 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+src.includes = about.html
diff --git a/bundles/org.eclipse.core.databinding/plugin.properties b/bundles/org.eclipse.core.databinding/plugin.properties
new file mode 100644
index 0000000..e18f395
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2000, 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
+###############################################################################
+pluginName = JFace Data Binding
+providerName = Eclipse.org
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.java
new file mode 100644
index 0000000..019731b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 218269, 146397, 249526, 267451
+ *******************************************************************************/
+package org.eclipse.core.databinding;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+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 extends ComputedValue {
+	/**
+	 * 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 #getStatusMerged(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 #getStatusMaxSeverity(Collection)
+	 */
+	public static final int MAX_SEVERITY = 2;
+
+	private int strategy;
+	private IObservableCollection validationStatusProviders;
+
+	/**
+	 * 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 AggregateValidationStatus(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 AggregateValidationStatus(
+			final IObservableCollection validationStatusProviders, int 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 AggregateValidationStatus(final Realm realm,
+			final IObservableCollection validationStatusProviders, int strategy) {
+		super(realm, IStatus.class);
+		this.validationStatusProviders = validationStatusProviders;
+		this.strategy = strategy;
+	}
+
+	protected Object calculate() {
+		IStatus result;
+		if (strategy == MERGED) {
+			result = getStatusMerged(validationStatusProviders);
+		} else {
+			result = getStatusMaxSeverity(validationStatusProviders);
+		}
+		return result;
+	}
+
+	/**
+	 * 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 = (ValidationStatusProvider) it
+					.next();
+			IStatus status = (IStatus) validationStatusProvider
+					.getValidationStatus().getValue();
+			if (!status.isOK()) {
+				statuses.add(status);
+			}
+		}
+		if (statuses.size() == 1) {
+			return (IStatus) statuses.get(0);
+		}
+		if (!statuses.isEmpty()) {
+			MultiStatus result = new MultiStatus(Policy.JFACE_DATABINDING, 0,
+					BindingMessages
+							.getString(BindingMessages.MULTIPLE_PROBLEMS), null);
+			for (Iterator it = statuses.iterator(); it.hasNext();) {
+				IStatus status = (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 = (ValidationStatusProvider) it
+					.next();
+			IStatus status = (IStatus) validationStatusProvider
+					.getValidationStatus().getValue();
+			if (status.getSeverity() > maxSeverity) {
+				maxSeverity = status.getSeverity();
+				maxStatus = status;
+			}
+		}
+		return maxStatus;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.java
new file mode 100644
index 0000000..3f2e00c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.java
@@ -0,0 +1,183 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 254524, 146906, 281723
+ *******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+
+/**
+ * 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 #init(DataBindingContext)}.
+ * 
+ * @since 1.0
+ */
+public abstract class Binding extends ValidationStatusProvider {
+
+	protected DataBindingContext context;
+	private IObservable target;
+	private IObservable model;
+	private IDisposeListener disposeListener;
+	
+	/**
+	 * Creates a new binding.
+	 * 
+	 * @param target target observable
+	 * @param model model observable
+	 */
+	public Binding(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;
+		if (target.isDisposed())
+			throw new IllegalArgumentException("Target observable is disposed"); //$NON-NLS-1$
+		if (model.isDisposed())
+			throw new IllegalArgumentException("Model observable is disposed"); //$NON-NLS-1$
+		this.disposeListener = new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				Binding.this.context.getValidationRealm().exec(new Runnable() {
+					public void run() {
+						if (!isDisposed())
+							dispose();
+					}
+				});
+			}
+		};
+		target.addDisposeListener(disposeListener);
+		model.addDisposeListener(disposeListener);
+		preInit();
+		context.addBinding(this);
+		postInit();
+	}
+	
+	/**
+	 * Called by {@link #init(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 #init(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();
+
+	/**
+	 * 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 != null) {
+			context.removeBinding(this);
+		}
+		context = null;
+		if (disposeListener != null) {
+			if (target != null) {
+				target.removeDisposeListener(disposeListener);
+			}
+			if (model != null) {
+				model.removeDisposeListener(disposeListener);
+			}
+			disposeListener = null;
+		}
+		target = null;
+		model = null;
+		super.dispose();
+	}
+
+	/**
+	 * @param context
+	 */
+	/* package */ void setDataBindingContext(DataBindingContext context) {
+		this.context = context;
+	}
+
+	/**
+	 * Returns the target observable
+	 * 
+	 * @return the target observable
+	 */
+	public IObservable getTarget() {
+		return target;
+	}
+
+	public IObservableList getTargets() {
+		return Observables.staticObservableList(context.getValidationRealm(),
+				Collections.singletonList(target));
+	}
+
+	/**
+	 * Returns the model observable
+	 * 
+	 * @return the model observable
+	 */
+	public IObservable getModel() {
+		return model;
+	}
+
+	public IObservableList getModels() {
+		return Observables.staticObservableList(context.getValidationRealm(),
+				Collections.singletonList(model));
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.java
new file mode 100644
index 0000000..ebe8624
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.databinding;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * An unchecked exception indicating a binding problem.
+ * 
+ * @since 1.0
+ */
+public class BindingException extends 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 BindingException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Creates a new BindingException with the given message and cause.
+	 * 
+	 * @param message
+	 * @param cause
+	 */
+	public BindingException(String message, Throwable cause) {
+		super(message);
+		this.cause = cause;
+	}
+
+	public void printStackTrace(PrintStream err) {
+		super.printStackTrace(err);
+		if (cause != null) {
+			err.println("caused by:"); //$NON-NLS-1$
+			cause.printStackTrace(err);
+		}
+	}
+
+	public void printStackTrace(PrintWriter err) {
+		super.printStackTrace(err);
+		if (cause != null) {
+			err.println("caused by:"); //$NON-NLS-1$
+			cause.printStackTrace(err);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingProperties.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingProperties.java
new file mode 100644
index 0000000..0977bf9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingProperties.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263709)
+ *     Matthew Hall - bug 264954
+ *     Ovidio Mallo - bug 306611
+ ******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.property.Properties;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.BindingModelProperty;
+import org.eclipse.core.internal.databinding.BindingTargetProperty;
+import org.eclipse.core.internal.databinding.ConverterValueProperty;
+import org.eclipse.core.internal.databinding.DataBindingContextBindingsProperty;
+import org.eclipse.core.internal.databinding.DataBindingContextValidationStatusProvidersProperty;
+import org.eclipse.core.internal.databinding.ValidationStatusProviderModelsProperty;
+import org.eclipse.core.internal.databinding.ValidationStatusProviderTargetsProperty;
+import org.eclipse.core.internal.databinding.ValidationStatusProviderValidationStatusProperty;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * A factory for creating properties for core types in the DataBinding framework
+ * e.g. {@link DataBindingContext} or ValidationStatusProvider.
+ * 
+ * @since 1.2
+ */
+public class BindingProperties {
+	/**
+	 * Returns an {@link IListProperty} &lt; {@link Binding} &gt; for observing
+	 * the bindings of a {@link DataBindingContext}.
+	 * 
+	 * @return an {@link IListProperty} &lt; {@link Binding} &gt; for observing
+	 *         the bindings of a {@link DataBindingContext}.
+	 */
+	public static IListProperty bindings() {
+		return new DataBindingContextBindingsProperty();
+	}
+
+	/**
+	 * Returns an {@link IValueProperty} &lt; {@link IObservable} &gt; for
+	 * observing the model of a {@link Binding}.
+	 * 
+	 * @return an {@link IValueProperty} &lt; {@link IObservable} &gt; for
+	 *         observing the model of a {@link Binding}.
+	 */
+	public static IValueProperty model() {
+		return new BindingModelProperty();
+	}
+
+	/**
+	 * Returns an {@link IListProperty} &lt; {@link IObservable} &gt; for
+	 * observing the models of a {@link ValidationStatusProvider}.
+	 * 
+	 * @return an {@link IListProperty} &lt; {@link IObservable} &gt; for
+	 *         observing the models of a {@link ValidationStatusProvider}.
+	 */
+	public static IListProperty models() {
+		return new ValidationStatusProviderModelsProperty();
+	}
+
+	/**
+	 * Returns an {@link IValueProperty} &lt; {@link IObservable} &gt; for
+	 * observing the target of a {@link Binding}.
+	 * 
+	 * @return an {@link IValueProperty} &lt; {@link IObservable} &gt; for
+	 *         observing the target of a {@link Binding}.
+	 */
+	public static IValueProperty target() {
+		return new BindingTargetProperty();
+	}
+
+	/**
+	 * Returns an {@link IListProperty} &lt; {@link IObservable} &gt; for
+	 * observing the targets of a {@link ValidationStatusProvider}.
+	 * 
+	 * @return an {@link IListProperty} &lt; {@link IObservable} &gt; for
+	 *         observing the targets of a {@link ValidationStatusProvider}.
+	 */
+	public static IListProperty targets() {
+		return new ValidationStatusProviderTargetsProperty();
+	}
+
+	/**
+	 * Returns an {@link IValueProperty} &lt; {@link IStatus} &gt; for observing
+	 * the validation status of a {@link ValidationStatusProvider}.
+	 * 
+	 * @return an {@link IValueProperty} &lt; {@link IStatus} &gt; for observing
+	 *         the validation status of a {@link ValidationStatusProvider}.
+	 */
+	public static IValueProperty validationStatus() {
+		return new ValidationStatusProviderValidationStatusProperty()
+				.value(Properties.observableValue(IStatus.class));
+	}
+
+	/**
+	 * Returns an {@link IListProperty} &lt; {@link ValidationStatusProvider}
+	 * &gt; for observing the validation status providers of a
+	 * {@link DataBindingContext}.
+	 * 
+	 * @return an {@link IListProperty} &lt; {@link ValidationStatusProvider}
+	 *         &gt; for observing the validation status providers of a
+	 *         {@link DataBindingContext}.
+	 */
+	public static IListProperty validationStatusProviders() {
+		return new DataBindingContextValidationStatusProvidersProperty();
+	}
+
+	/**
+	 * Returns an {@link IValueProperty} whose value results from applying the
+	 * given {@link IConverter} on the source object of the value property.
+	 * Consequently, the {@link IValueProperty#getValueType() value type} of the
+	 * returned property is the same as the {@link IConverter#getToType() target
+	 * type} of the converter. Setting a value on the property is not supported.
+	 * 
+	 * @param converter
+	 *            The converter to apply to the source object of the value
+	 *            property.
+	 * @return A new instance of a value property whose value is the result of
+	 *         applying the given converter to the source object passed to the
+	 *         value property.
+	 * 
+	 * @since 1.4
+	 */
+	public static IValueProperty convertedValue(IConverter converter) {
+		return new ConverterValueProperty(converter);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.java
new file mode 100644
index 0000000..19e7404
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.java
@@ -0,0 +1,500 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 159539, 140644, 159940, 116920, 159768
+ *     Matthew Hall - bugs 118516, 124684, 218269, 260329, 252732, 146906,
+ *                    278550
+ *     Boris Bokowski - bug 218269
+ *******************************************************************************/
+package org.eclipse.core.databinding;
+
+import java.util.Iterator;
+
+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.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 DataBindingContext() {
+		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 DataBindingContext(Realm validationRealm) {
+		Assert.isNotNull(validationRealm, "Validation realm cannot be null"); //$NON-NLS-1$
+		this.validationRealm = validationRealm;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			bindings = new WritableList(validationRealm);
+			unmodifiableBindings = Observables
+					.unmodifiableObservableList(bindings);
+
+			validationStatusProviders = new WritableList(validationRealm);
+			unmodifiableStatusProviders = Observables
+					.unmodifiableObservableList(validationStatusProviders);
+
+			validationStatusMap = new ValidationStatusMap(validationRealm,
+					bindings);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	/**
+	 * Creates a {@link Binding} to synchronize the values of two
+	 * {@link IObservableValue observable values}. This method is an alias for
+	 * <code>bindValue(targetObservableValue, modelObservableValue, null,
+	 * null)</code>.
+	 * 
+	 * @param targetObservableValue
+	 *            target value, commonly a UI widget
+	 * @param modelObservableValue
+	 *            model value
+	 * @return created binding
+	 * @since 1.2
+	 */
+	public final Binding bindValue(IObservableValue targetObservableValue,
+			IObservableValue modelObservableValue) {
+		return bindValue(targetObservableValue, modelObservableValue, null,
+				null);
+	}
+
+	/**
+	 * 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 != null ? targetToModel
+						: createTargetToModelUpdateValueStrategy(targetObservableValue, modelObservableValue);
+		UpdateValueStrategy modelToTargetStrategy = modelToTarget != 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}. This method is an alias for
+	 * <code>bindList(targetObservableList, modelObservableList, null,
+	 * null)</code>.
+	 * 
+	 * @param targetObservableList
+	 *            target list, commonly a list representing a list in the UI
+	 * @param modelObservableList
+	 *            model list
+	 * @return created binding
+	 * 
+	 * @see UpdateListStrategy
+	 * @since 1.2
+	 */
+	public final Binding bindList(IObservableList targetObservableList,
+			IObservableList modelObservableList) {
+		return bindList(targetObservableList, modelObservableList, null, null);
+	}
+
+	/**
+	 * 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 != null ? targetToModel
+				: createTargetToModelUpdateListStrategy(targetObservableList,
+						modelObservableList);
+		UpdateListStrategy modelToTargetStrategy = modelToTarget != 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}. This method is an alias for
+	 * <code>bindSet(targetObservableValue, modelObservableValue, null,
+	 * null)</code>.
+	 * 
+	 * @param targetObservableSet
+	 *            target set, commonly a set representing a set in the UI
+	 * @param modelObservableSet
+	 *            model set
+	 * @return created binding
+	 * @since 1.2
+	 */
+	public final Binding bindSet(IObservableSet targetObservableSet,
+			IObservableSet modelObservableSet) {
+		return bindSet(targetObservableSet, modelObservableSet, null, null);
+	}
+
+	/**
+	 * 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 == null)
+			targetToModel = createTargetToModelUpdateSetStrategy(
+					targetObservableSet, modelObservableSet);
+		if (modelToTarget == 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. This method must be
+	 * called in the {@link #getValidationRealm() validation realm}.
+	 */
+	public final void dispose() {
+		Binding[] bindingArray = (Binding[]) bindings.toArray(new Binding[bindings.size()]);
+		for (int i = 0; i < bindingArray.length; i++) {
+			bindingArray[i].dispose();
+		}
+		ValidationStatusProvider[] statusProviderArray = (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 {@link IObservableList} &lt; {@link Binding} &gt;
+	 * of all bindings in order by time of addition.
+	 * 
+	 * @return an unmodifiable {@link IObservableList} &lt; {@link Binding} &gt;
+	 *         of all bindings
+	 */
+	public final IObservableList getBindings() {
+		return unmodifiableBindings;
+	}
+
+	/**
+	 * Returns an unmodifiable an unmodifiable {@link IObservableList} &lt;
+	 * {@link ValidationStatusProvider} &gt; of all validation status providers
+	 * in order by time of addition.
+	 * 
+	 * @return an unmodifiable {@link IObservableList} &lt;
+	 *         {@link ValidationStatusProvider} &gt; of all validation status
+	 *         providers
+	 * @since 1.1
+	 */
+	public final IObservableList getValidationStatusProviders() {
+		return unmodifiableStatusProviders;
+	}
+
+	/**
+	 * Returns an {@link IObservableMap} &lt; {@link Binding}, {@link IStatus}
+	 * &gt; mapping from bindings to current validation statuses. The keys of the
+	 * map are the bindings returned by {@link #getBindings()}, and the values
+	 * are the current IStatus 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 #addValidationStatusProvider(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 = (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 = (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 boolean 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 boolean removeValidationStatusProvider(
+			ValidationStatusProvider validationStatusProvider) {
+		return validationStatusProviders.remove(validationStatusProvider);
+	}
+
+	/**
+	 * Returns the validation realm.
+	 * 
+	 * @return the realm for the validation observables
+	 * @see Realm
+	 */
+	public final Realm getValidationRealm() {
+		return validationRealm;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ListBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ListBinding.java
new file mode 100644
index 0000000..10301ff
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ListBinding.java
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 262221, 271148, 280341, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.Diffs;
+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.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+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 extends Binding {
+
+	private UpdateListStrategy targetToModel;
+	private UpdateListStrategy modelToTarget;
+	private IObservableValue validationStatusObservable;
+	private boolean updatingTarget;
+	private boolean updatingModel;
+
+	private IListChangeListener targetChangeListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			if (!updatingTarget) {
+				doUpdate((IObservableList) getTarget(),
+						(IObservableList) getModel(), event.diff,
+						targetToModel, false, false);
+			}
+		}
+	};
+	private IListChangeListener modelChangeListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			if (!updatingModel) {
+				doUpdate((IObservableList) getModel(),
+						(IObservableList) getTarget(), event.diff,
+						modelToTarget, false, false);
+			}
+		}
+	};
+
+	/**
+	 * @param target
+	 * @param model
+	 * @param modelToTargetStrategy
+	 * @param targetToModelStrategy
+	 */
+	public ListBinding(IObservableList target, IObservableList model,
+			UpdateListStrategy targetToModelStrategy,
+			UpdateListStrategy modelToTargetStrategy) {
+		super(target, model);
+		this.targetToModel = targetToModelStrategy;
+		this.modelToTarget = modelToTargetStrategy;
+		if ((targetToModel.getUpdatePolicy() & UpdateListStrategy.POLICY_UPDATE) != 0) {
+			target.addListChangeListener(targetChangeListener);
+		} else {
+			targetChangeListener = null;
+		}
+		if ((modelToTarget.getUpdatePolicy() & UpdateListStrategy.POLICY_UPDATE) != 0) {
+			model.addListChangeListener(modelChangeListener);
+		} else {
+			modelChangeListener = null;
+		}
+	}
+
+	public IObservableValue getValidationStatus() {
+		return validationStatusObservable;
+	}
+
+	protected void preInit() {
+		ObservableTracker.setIgnore(true);
+		try {
+			validationStatusObservable = new WritableValue(context
+					.getValidationRealm(), Status.OK_STATUS, IStatus.class);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	protected void postInit() {
+		if (modelToTarget.getUpdatePolicy() == UpdateListStrategy.POLICY_UPDATE) {
+			updateModelToTarget();
+		}
+		if (targetToModel.getUpdatePolicy() == UpdateListStrategy.POLICY_UPDATE) {
+			validateTargetToModel();
+		}
+	}
+
+	public void updateModelToTarget() {
+		final IObservableList modelList = (IObservableList) getModel();
+		modelList.getRealm().exec(new Runnable() {
+			public void run() {
+				ListDiff diff = Diffs.computeListDiff(Collections.EMPTY_LIST,
+						modelList);
+				doUpdate(modelList, (IObservableList) getTarget(), diff,
+						modelToTarget, true, true);
+			}
+		});
+	}
+
+	public void updateTargetToModel() {
+		final IObservableList targetList = (IObservableList) getTarget();
+		targetList.getRealm().exec(new Runnable() {
+			public void run() {
+				ListDiff diff = Diffs.computeListDiff(Collections.EMPTY_LIST,
+						targetList);
+				doUpdate(targetList, (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(final IObservableList source,
+			final IObservableList destination, final ListDiff diff,
+			final UpdateListStrategy updateListStrategy,
+			final boolean explicit, final boolean clearDestination) {
+		final int policy = updateListStrategy.getUpdatePolicy();
+		if (policy != UpdateListStrategy.POLICY_NEVER) {
+			if (policy != UpdateListStrategy.POLICY_ON_REQUEST || explicit) {
+				destination.getRealm().exec(new Runnable() {
+					public void run() {
+						if (destination == getTarget()) {
+							updatingTarget = true;
+						} else {
+							updatingModel = true;
+						}
+						final MultiStatus multiStatus = BindingStatus.ok();
+
+						try {
+							if (clearDestination) {
+								destination.clear();
+							}
+							diff.accept(new ListDiffVisitor() {
+								boolean useMoveAndReplace = updateListStrategy
+										.useMoveAndReplace();
+
+								public void handleAdd(int index, Object element) {
+									IStatus setterStatus = updateListStrategy
+											.doAdd(destination,
+													updateListStrategy
+															.convert(element),
+													index);
+
+									mergeStatus(multiStatus, setterStatus);
+								}
+
+								public void handleRemove(int index,
+										Object element) {
+									IStatus setterStatus = updateListStrategy
+											.doRemove(destination, index);
+
+									mergeStatus(multiStatus, setterStatus);
+								}
+
+								public void handleMove(int oldIndex,
+										int newIndex, Object element) {
+									if (useMoveAndReplace) {
+										IStatus setterStatus = updateListStrategy
+												.doMove(destination, oldIndex,
+														newIndex);
+
+										mergeStatus(multiStatus, setterStatus);
+									} else {
+										super.handleMove(oldIndex, newIndex,
+												element);
+									}
+								}
+
+								public void handleReplace(int index,
+										Object oldElement, Object newElement) {
+									if (useMoveAndReplace) {
+										IStatus setterStatus = updateListStrategy
+												.doReplace(destination, index,
+														newElement);
+
+										mergeStatus(multiStatus, setterStatus);
+									} else {
+										super.handleReplace(index, oldElement,
+												newElement);
+									}
+								}
+							});
+							// TODO - at this point, the two lists will be out
+							// of sync if an error occurred...
+						} finally {
+							validationStatusObservable.setValue(multiStatus);
+
+							if (destination == getTarget()) {
+								updatingTarget = false;
+							} else {
+								updatingModel = false;
+							}
+						}
+					}
+				});
+			}
+		}
+	}
+
+	/**
+	 * 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 != null) {
+			((IObservableList) getTarget())
+					.removeListChangeListener(targetChangeListener);
+			targetChangeListener = null;
+		}
+		if (modelChangeListener != null) {
+			((IObservableList) getModel())
+					.removeListChangeListener(modelChangeListener);
+			modelChangeListener = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ObservablesManager.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ObservablesManager.java
new file mode 100644
index 0000000..87fdbb3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ObservablesManager.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 249526
+ *     Matthew Hall - bug 249526, 261513
+ *******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import java.util.HashMap;
+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.ObservableTracker;
+import org.eclipse.core.internal.databinding.IdentitySet;
+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 IdentitySet();
+	private Set excludedObservables = new IdentitySet();
+	private Map contexts = new HashMap();
+
+	/**
+	 * Create a new observables manager.
+	 */
+	public ObservablesManager() {
+	}
+
+	/**
+	 * 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,
+			boolean trackTargets, boolean trackModels) {
+		if (trackTargets || trackModels) {
+			contexts.put(context, new Pair(new Boolean(trackTargets),
+					new Boolean(trackModels)));
+		}
+	}
+
+	/**
+	 * Executes the specified runnable and adds to this manager all observables
+	 * created while executing the runnable.
+	 * <p>
+	 * <em>NOTE: As of 1.2 (Eclipse 3.5), there are unresolved problems with this API, see
+	 * <a href="https://bugs.eclipse.org/278550">bug 278550</a>. If we cannot
+	 * find a way to make this API work, it will be deprecated as of 3.6.</em>
+	 * </p>
+	 * 
+	 * @param runnable
+	 *            the runnable to execute
+	 * @since 1.2
+	 */
+	public void runAndCollect(Runnable runnable) {
+		IObservable[] collected = ObservableTracker.runAndCollect(runnable);
+		for (int i = 0; i < collected.length; i++)
+			addObservable(collected[i]);
+	}
+
+	/**
+	 * Disposes of this manager and all observables that it manages.
+	 */
+	public void dispose() {
+		Set observables = new IdentitySet();
+		observables.addAll(managedObservables);
+		for (Iterator it = contexts.keySet().iterator(); it.hasNext();) {
+			DataBindingContext context = (DataBindingContext) it.next();
+			Pair trackModelsOrTargets = (Pair) contexts.get(context);
+			boolean disposeTargets = ((Boolean) trackModelsOrTargets.a)
+					.booleanValue();
+			boolean disposeModels = ((Boolean) trackModelsOrTargets.b)
+					.booleanValue();
+			for (Iterator it2 = context.getBindings().iterator(); it2.hasNext();) {
+				Binding binding = (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 = (IObservable) it.next();
+			observable.dispose();
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/SetBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/SetBinding.java
new file mode 100644
index 0000000..8f5cb1e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/SetBinding.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Matthew Hall - bugs 271148, 278550
+ ******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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 extends Binding {
+
+	private UpdateSetStrategy targetToModel;
+	private UpdateSetStrategy modelToTarget;
+	private IObservableValue validationStatusObservable;
+	private boolean updatingTarget;
+	private boolean updatingModel;
+
+	private ISetChangeListener targetChangeListener = new ISetChangeListener() {
+		public void handleSetChange(SetChangeEvent event) {
+			if (!updatingTarget) {
+				doUpdate((IObservableSet) getTarget(),
+						(IObservableSet) getModel(), event.diff, targetToModel,
+						false, false);
+			}
+		}
+	};
+
+	private ISetChangeListener modelChangeListener = new ISetChangeListener() {
+		public void handleSetChange(SetChangeEvent event) {
+			if (!updatingModel) {
+				doUpdate((IObservableSet) getModel(),
+						(IObservableSet) getTarget(), event.diff,
+						modelToTarget, false, false);
+			}
+		}
+	};
+
+	/**
+	 * @param target
+	 * @param model
+	 * @param modelToTargetStrategy
+	 * @param targetToModelStrategy
+	 */
+	public SetBinding(IObservableSet target, IObservableSet model,
+			UpdateSetStrategy targetToModelStrategy,
+			UpdateSetStrategy modelToTargetStrategy) {
+		super(target, model);
+		this.targetToModel = targetToModelStrategy;
+		this.modelToTarget = modelToTargetStrategy;
+		if ((targetToModel.getUpdatePolicy() & UpdateSetStrategy.POLICY_UPDATE) != 0) {
+			target.addSetChangeListener(targetChangeListener);
+		} else {
+			targetChangeListener = null;
+		}
+		if ((modelToTarget.getUpdatePolicy() & UpdateSetStrategy.POLICY_UPDATE) != 0) {
+			model.addSetChangeListener(modelChangeListener);
+		} else {
+			modelChangeListener = null;
+		}
+	}
+
+	public IObservableValue getValidationStatus() {
+		return validationStatusObservable;
+	}
+
+	protected void preInit() {
+		ObservableTracker.setIgnore(true);
+		try {
+			validationStatusObservable = new WritableValue(context
+					.getValidationRealm(), Status.OK_STATUS, IStatus.class);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	protected void postInit() {
+		if (modelToTarget.getUpdatePolicy() == UpdateSetStrategy.POLICY_UPDATE) {
+			updateModelToTarget();
+		}
+		if (targetToModel.getUpdatePolicy() == UpdateSetStrategy.POLICY_UPDATE) {
+			validateTargetToModel();
+		}
+	}
+
+	public void updateModelToTarget() {
+		final IObservableSet modelSet = (IObservableSet) getModel();
+		modelSet.getRealm().exec(new Runnable() {
+			public void run() {
+				SetDiff diff = Diffs.computeSetDiff(Collections.EMPTY_SET,
+						modelSet);
+				doUpdate(modelSet, (IObservableSet) getTarget(), diff,
+						modelToTarget, true, true);
+			}
+		});
+	}
+
+	public void updateTargetToModel() {
+		final IObservableSet targetSet = (IObservableSet) getTarget();
+		targetSet.getRealm().exec(new Runnable() {
+			public void run() {
+				SetDiff diff = Diffs.computeSetDiff(Collections.EMPTY_SET,
+						targetSet);
+				doUpdate(targetSet, (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(final IObservableSet source,
+			final IObservableSet destination, final SetDiff diff,
+			final UpdateSetStrategy updateSetStrategy, final boolean explicit,
+			final boolean clearDestination) {
+		final int policy = updateSetStrategy.getUpdatePolicy();
+		if (policy == UpdateSetStrategy.POLICY_NEVER)
+			return;
+		if (policy == UpdateSetStrategy.POLICY_ON_REQUEST && !explicit)
+			return;
+		destination.getRealm().exec(new Runnable() {
+			public void run() {
+				if (destination == 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 == getTarget()) {
+						updatingTarget = false;
+					} else {
+						updatingModel = false;
+					}
+				}
+			}
+		});
+	}
+
+	/**
+	 * 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 != null) {
+			((IObservableSet) getTarget())
+					.removeSetChangeListener(targetChangeListener);
+			targetChangeListener = null;
+		}
+		if (modelChangeListener != null) {
+			((IObservableSet) getModel())
+					.removeSetChangeListener(modelChangeListener);
+			modelChangeListener = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateListStrategy.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateListStrategy.java
new file mode 100644
index 0000000..4d7e8b2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateListStrategy.java
@@ -0,0 +1,314 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *     Matthew Hall - bug 262221, 270461
+ *******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+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 #convert(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 extends 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 boolean 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 UpdateListStrategy() {
+		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 UpdateListStrategy(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 UpdateListStrategy(boolean 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 #setConverter(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 == 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 != null && destinationType != null) {
+			if (converter == null) {
+				setConverter(createConverter(sourceType, destinationType));
+			}
+		}
+		if (converter != null) {
+			if (sourceType != null) {
+				checkAssignable(converter.getFromType(), sourceType,
+						"converter does not convert from type " + sourceType); //$NON-NLS-1$
+			}
+			if (destinationType != null) {
+				checkAssignable(destinationType, converter.getToType(),
+						"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
+									.getString(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
+									.getString(BindingMessages.VALUEBINDING_ERROR_WHILE_SETTING_VALUE),
+							ex);
+		}
+		return Status.OK_STATUS;
+	}
+
+	/**
+	 * Returns whether ListBinding should call
+	 * {@link #doMove(IObservableList, int, int)} and
+	 * {@link #doReplace(IObservableList, int, Object)} instead of paired calls
+	 * to {@link #doRemove(IObservableList, int)} and
+	 * {@link #doAdd(IObservableList, Object, int)}. The default implementation
+	 * returns true for this class and false for subclasses.
+	 * <p>
+	 * This method is provided for the benefit of subclasses which extend the
+	 * {@link #doAdd(IObservableList, Object, int)} and
+	 * {@link #doRemove(IObservableList, int)} methods. The
+	 * {@link #doMove(IObservableList, int, int)} and
+	 * {@link #doReplace(IObservableList, int, Object)} methods were added in
+	 * version 1.2 so that logically related remove() and add() operations could
+	 * be combined into a single method call. However to ensure that custom
+	 * behavior is not lost in existing code, {@link ListBinding} is advised by
+	 * this method whether these new methods may be used.
+	 * <p>
+	 * To enable use of these methods in subclasses, override this method to
+	 * return true.
+	 * 
+	 * @return whether ListBinding should call doMove() and doReplace() instead
+	 *         of paired calls of doRemove() and doAdd()
+	 * @since 1.2
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected boolean useMoveAndReplace() {
+		return getClass() == UpdateListStrategy.class;
+	}
+
+	/**
+	 * Moves the element in the observable list located at the given old index
+	 * to the given new index.
+	 * 
+	 * @param observableList
+	 * @param oldIndex
+	 * @param newIndex
+	 * @return a status
+	 * @since 1.2
+	 */
+	protected IStatus doMove(IObservableList observableList, int oldIndex,
+			int newIndex) {
+		try {
+			observableList.move(oldIndex, newIndex);
+		} catch (Exception ex) {
+			return ValidationStatus
+					.error(
+							BindingMessages
+									.getString(BindingMessages.VALUEBINDING_ERROR_WHILE_SETTING_VALUE),
+							ex);
+		}
+		return Status.OK_STATUS;
+	}
+
+	/**
+	 * Replaces the element in the observable list located at the given index to
+	 * with the given element.
+	 * 
+	 * @param observableList
+	 * @param index
+	 * @param element
+	 * @return a status
+	 * @since 1.2
+	 */
+	protected IStatus doReplace(IObservableList observableList, int index,
+			Object element) {
+		try {
+			observableList.set(index, element);
+		} catch (Exception ex) {
+			return ValidationStatus
+					.error(
+							BindingMessages
+									.getString(BindingMessages.VALUEBINDING_ERROR_WHILE_SETTING_VALUE),
+							ex);
+		}
+		return Status.OK_STATUS;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateSetStrategy.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateSetStrategy.java
new file mode 100644
index 0000000..c56d8b6
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateSetStrategy.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Matthew Hall - bug 270461
+ ******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+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 #convert(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 extends 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 boolean 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 UpdateSetStrategy() {
+		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 UpdateSetStrategy(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 UpdateSetStrategy(boolean 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 #setConverter(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 == 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 != null && destinationType != null) {
+			if (converter == null) {
+				setConverter(createConverter(sourceType, destinationType));
+			}
+		}
+		if (converter != null) {
+			if (sourceType != null) {
+				checkAssignable(converter.getFromType(), sourceType,
+						"converter does not convert from type " + sourceType); //$NON-NLS-1$
+			}
+			if (destinationType != null) {
+				checkAssignable(destinationType, converter.getToType(),
+						"converter does not convert to type " + destinationType); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 * @return the update policy
+	 */
+	public int getUpdatePolicy() {
+		return updatePolicy;
+	}
+
+	/**
+	 * Sets the converter to be invoked when converting added elements from the
+	 * source element type to the destination element type.
+	 * 
+	 * @param converter
+	 * @return the receiver, to enable method call chaining
+	 */
+	public UpdateSetStrategy setConverter(IConverter converter) {
+		this.converter = converter;
+		return this;
+	}
+
+	/**
+	 * Adds the given element at the given index to the given observable list.
+	 * Clients may extend but must call the super implementation.
+	 * 
+	 * @param observableSet
+	 * @param element
+	 * @return a status
+	 */
+	protected IStatus doAdd(IObservableSet observableSet, Object element) {
+		try {
+			observableSet.add(element);
+		} catch (Exception ex) {
+			return ValidationStatus.error(BindingMessages
+					.getString("ValueBinding_ErrorWhileSettingValue"), //$NON-NLS-1$
+					ex);
+		}
+		return Status.OK_STATUS;
+	}
+
+	/**
+	 * Removes the element at the given index from the given observable list.
+	 * Clients may extend but must call the super implementation.
+	 * 
+	 * @param observableSet
+	 * @param element
+	 * @return a status
+	 */
+	protected IStatus doRemove(IObservableSet observableSet, Object element) {
+		try {
+			observableSet.remove(element);
+		} catch (Exception ex) {
+			return ValidationStatus.error(BindingMessages
+					.getString("ValueBinding_ErrorWhileSettingValue"), //$NON-NLS-1$
+					ex);
+		}
+		return Status.OK_STATUS;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.java
new file mode 100644
index 0000000..a98c6da
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.java
@@ -0,0 +1,716 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *                 - bug 197679 (Character support completed)
+ *******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+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_CLASS = "boolean.class"; //$NON-NLS-1$
+
+	private static final String SHORT_CLASS = "short.class"; //$NON-NLS-1$
+
+	private static final String BYTE_CLASS = "byte.class"; //$NON-NLS-1$
+
+	private static final String DOUBLE_CLASS = "double.class"; //$NON-NLS-1$
+
+	private static final String FLOAT_CLASS = "float.class"; //$NON-NLS-1$
+
+	private static final String INTEGER_CLASS = "int.class"; //$NON-NLS-1$
+
+	private static final String LONG_CLASS = "long.class"; //$NON-NLS-1$
+
+	private static final String CHARACTER_CLASS = "char.class"; //$NON-NLS-1$
+
+	private static Map converterMap;
+
+	private static Class autoboxed(Class clazz) {
+		if (clazz == Float.TYPE)
+			return Float.class;
+		else if (clazz == Double.TYPE)
+			return Double.class;
+		else if (clazz == Short.TYPE)
+			return Short.class;
+		else if (clazz == Integer.TYPE)
+			return Integer.class;
+		else if (clazz == Long.TYPE)
+			return Long.class;
+		else if (clazz == Byte.TYPE)
+			return Byte.class;
+		else if (clazz == Boolean.TYPE)
+			return Boolean.class;
+		else if (clazz == Character.TYPE)
+			return Character.class;
+		return clazz;
+	}
+
+	final protected void checkAssignable(Object toType, Object fromType,
+			String errorString) {
+		Boolean assignableFromModelToModelConverter = isAssignableFromTo(
+				fromType, toType);
+		if (assignableFromModelToModelConverter != 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 (!(fromType instanceof Class) || !(toType instanceof Class)) {
+			return new DefaultConverter(fromType, toType);
+		}
+		Class toClass = (Class) toType;
+		Class originalToClass = toClass;
+		if (toClass.isPrimitive()) {
+			toClass = autoboxed(toClass);
+		}
+		Class fromClass = (Class) fromType;
+		Class originalFromClass = fromClass;
+		if (fromClass.isPrimitive()) {
+			fromClass = autoboxed(fromClass);
+		}
+		if (!((Class) toType).isPrimitive()
+				&& toClass.isAssignableFrom(fromClass)) {
+			return new IdentityConverter(originalFromClass, originalToClass);
+		}
+		if (((Class) fromType).isPrimitive() && ((Class) toType).isPrimitive()
+				&& fromType.equals(toType)) {
+			return new IdentityConverter(originalFromClass, originalToClass);
+		}
+		Map converterMap = getConverterMap();
+		Class[] supertypeHierarchyFlattened = ClassLookupSupport
+				.getTypeHierarchyFlattened(fromClass);
+		for (int i = 0; i < supertypeHierarchyFlattened.length; i++) {
+			Class currentFromClass = supertypeHierarchyFlattened[i];
+			if (currentFromClass == 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 (converterOrClassname instanceof IConverter) {
+				return (IConverter) converterOrClassname;
+			} else if (converterOrClassname instanceof String) {
+				String classname = (String) converterOrClassname;
+				Class converterClass;
+				try {
+					converterClass = Class.forName(classname);
+					IConverter result = (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 == 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.String", "java.math.BigDecimal"), StringToNumberConverter.toBigDecimal(numberFormat)); //$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.math.BigDecimal", "java.lang.String"), NumberToStringConverter.fromBigDecimal(numberFormat)); //$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.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", INTEGER_CLASS), StringToNumberConverter.toInteger(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(INTEGER_CLASS, "java.lang.Integer"), new IdentityConverter(Integer.class, Integer.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(INTEGER_CLASS, "java.lang.Object"), new IdentityConverter(Integer.class, Object.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(INTEGER_CLASS, "java.lang.String"), NumberToStringConverter.fromInteger(integerFormat, true)); //$NON-NLS-1$
+
+			// Byte.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", BYTE_CLASS), StringToByteConverter.toByte(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(BYTE_CLASS, "java.lang.Byte"), new IdentityConverter(Byte.class, Byte.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(BYTE_CLASS, "java.lang.String"), IntegerToStringConverter.fromByte(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(BYTE_CLASS, "java.lang.Object"), new IdentityConverter(Byte.class, Object.class)); //$NON-NLS-1$
+
+			// Double.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", DOUBLE_CLASS), StringToNumberConverter.toDouble(numberFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(DOUBLE_CLASS, "java.lang.String"), NumberToStringConverter.fromDouble(numberFormat, true)); //$NON-NLS-1$
+
+			converterMap
+					.put(
+							new Pair(DOUBLE_CLASS, "java.lang.Double"), new IdentityConverter(Double.class, Double.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(DOUBLE_CLASS, "java.lang.Object"), new IdentityConverter(Double.class, Object.class)); //$NON-NLS-1$
+
+			// Boolean.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", BOOLEAN_CLASS), "org.eclipse.core.internal.databinding.conversion.StringToBooleanPrimitiveConverter"); //$NON-NLS-1$ //$NON-NLS-2$
+			converterMap
+					.put(
+							new Pair(BOOLEAN_CLASS, "java.lang.Boolean"), new IdentityConverter(Boolean.class, Boolean.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(BOOLEAN_CLASS, "java.lang.String"), new ObjectToStringConverter(Boolean.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(BOOLEAN_CLASS, "java.lang.Object"), new IdentityConverter(Boolean.class, Object.class)); //$NON-NLS-1$
+
+			// Float.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", FLOAT_CLASS), StringToNumberConverter.toFloat(numberFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(FLOAT_CLASS, "java.lang.String"), NumberToStringConverter.fromFloat(numberFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(FLOAT_CLASS, "java.lang.Float"), new IdentityConverter(Float.class, Float.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(FLOAT_CLASS, "java.lang.Object"), new IdentityConverter(Float.class, Object.class)); //$NON-NLS-1$
+
+			// Short.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", SHORT_CLASS), StringToShortConverter.toShort(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(SHORT_CLASS, "java.lang.Short"), new IdentityConverter(Short.class, Short.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(SHORT_CLASS, "java.lang.String"), IntegerToStringConverter.fromShort(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(SHORT_CLASS, "java.lang.Object"), new IdentityConverter(Short.class, Object.class)); //$NON-NLS-1$
+
+			// Long.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", LONG_CLASS), StringToNumberConverter.toLong(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(LONG_CLASS, "java.lang.String"), NumberToStringConverter.fromLong(integerFormat, true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(LONG_CLASS, "java.lang.Long"), new IdentityConverter(Long.class, Long.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(LONG_CLASS, "java.lang.Object"), new IdentityConverter(Long.class, Object.class)); //$NON-NLS-1$
+
+			// Character.class
+			converterMap
+					.put(
+							new Pair("java.lang.String", CHARACTER_CLASS), StringToCharacterConverter.toCharacter(true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(CHARACTER_CLASS, "java.lang.Character"), new IdentityConverter(Character.class, Character.class)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(CHARACTER_CLASS, "java.lang.String"), CharacterToStringConverter.fromCharacter(true)); //$NON-NLS-1$
+			converterMap
+					.put(
+							new Pair(CHARACTER_CLASS, "java.lang.Object"), new IdentityConverter(Character.class, Object.class)); //$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 Class[] integerClasses = new Class[] { byte.class,
+			Byte.class, short.class, Short.class, int.class, Integer.class,
+			long.class, Long.class, BigInteger.class };
+
+	private static final Class[] floatClasses = new Class[] { float.class,
+			Float.class, double.class, Double.class, BigDecimal.class };
+
+	/**
+	 * 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, Class[] fromTypes) {
+
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(Byte.class) && !fromType.equals(byte.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map
+						.put(new Pair(fromName, BYTE_CLASS),
+								new NumberToByteConverter(numberFormat,
+										fromType, true));
+				map
+						.put(new Pair(fromName, Byte.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(Short.class) && !fromType.equals(short.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map
+						.put(new Pair(fromName, SHORT_CLASS),
+								new NumberToShortConverter(numberFormat,
+										fromType, true));
+				map.put(new Pair(fromName, Short.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(Integer.class)
+					&& !fromType.equals(int.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map.put(new Pair(fromName, INTEGER_CLASS),
+						new NumberToIntegerConverter(numberFormat, fromType,
+								true));
+				map.put(new Pair(fromName, Integer.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(Long.class) && !fromType.equals(long.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map
+						.put(new Pair(fromName, LONG_CLASS),
+								new NumberToLongConverter(numberFormat,
+										fromType, true));
+				map
+						.put(new Pair(fromName, Long.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(Float.class) && !fromType.equals(float.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map
+						.put(new Pair(fromName, FLOAT_CLASS),
+								new NumberToFloatConverter(numberFormat,
+										fromType, true));
+				map.put(new Pair(fromName, Float.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(Double.class) && !fromType.equals(double.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map.put(new Pair(fromName, DOUBLE_CLASS),
+						new NumberToDoubleConverter(numberFormat, fromType,
+								true));
+				map.put(new Pair(fromName, Double.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(BigInteger.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map
+						.put(new Pair(fromName, BigInteger.class.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, Class[] fromTypes) {
+		for (int i = 0; i < fromTypes.length; i++) {
+			Class fromType = fromTypes[i];
+			if (!fromType.equals(BigDecimal.class)) {
+				String fromName = (fromType.isPrimitive()) ? getKeyForClass(
+						fromType, null) : fromType.getName();
+
+				map
+						.put(new Pair(fromName, BigDecimal.class.getName()),
+								new NumberToBigDecimalConverter(numberFormat,
+										fromType));
+			}
+		}
+	}
+
+	private static String getKeyForClass(Object originalValue,
+			Class filteredValue) {
+		if (originalValue instanceof Class) {
+			Class originalClass = (Class) originalValue;
+			if (originalClass.equals(int.class)) {
+				return INTEGER_CLASS;
+			} else if (originalClass.equals(byte.class)) {
+				return BYTE_CLASS;
+			} else if (originalClass.equals(boolean.class)) {
+				return BOOLEAN_CLASS;
+			} else if (originalClass.equals(double.class)) {
+				return DOUBLE_CLASS;
+			} else if (originalClass.equals(float.class)) {
+				return FLOAT_CLASS;
+			} else if (originalClass.equals(long.class)) {
+				return LONG_CLASS;
+			} else if (originalClass.equals(short.class)) {
+				return SHORT_CLASS;
+			}
+		}
+		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 (fromType instanceof Class && toType instanceof Class) {
+			Class toClass = (Class) toType;
+			if (toClass.isPrimitive()) {
+				toClass = autoboxed(toClass);
+			}
+			Class fromClass = (Class) 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 implements IConverter {
+
+		private final Object toType;
+
+		private final Object fromType;
+
+		/**
+		 * @param fromType
+		 * @param toType
+		 */
+		DefaultConverter(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;
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateValueStrategy.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateValueStrategy.java
new file mode 100644
index 0000000..b801526
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateValueStrategy.java
@@ -0,0 +1,584 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *     Matthew Hall <matthall@woodcraftmill.com> - bug 270461
+ *******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+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 #validateAfterGet(Object)}</li>
+ * <li>Conversion - {@link #convert(Object)}</li>
+ * <li>Validate after conversion - {@link #validateAfterConvert(Object)}</li>
+ * <li>Validate before set - {@link #validateBeforeSet(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 extends 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 #validateBeforeSet(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 boolean provideDefaults;
+
+	/**
+	 * <code>true</code> if we defaulted the converter
+	 */
+	private boolean 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 UpdateValueStrategy() {
+		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 UpdateValueStrategy(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 UpdateValueStrategy(boolean 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 #setConverter(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 == 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 == null || toType == null) {
+			return new 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 #validateAfterGet(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 != null && destinationType != null) {
+			if (converter == null) {
+				IConverter converter = createConverter(sourceType,
+						destinationType);
+				defaultedConverter = (converter != null);
+				setConverter(converter);
+			}
+
+			if (afterGetValidator == null) {
+				afterGetValidator = createValidator(sourceType, destinationType);
+			}
+		}
+		if (converter != null) {
+			if (sourceType != null) {
+				checkAssignable(converter.getFromType(), sourceType,
+						"converter does not convert from type " + sourceType); //$NON-NLS-1$
+			}
+			if (destinationType != null) {
+				checkAssignable(destinationType, converter.getToType(),
+						"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.class.equals(fromType)) {
+				result = (IValidator) validatorsByConverter.get(converter);
+
+				if (result == null) {
+					// TODO sring based lookup
+					if (Integer.class.equals(toType)
+							|| Integer.TYPE.equals(toType)) {
+						result = new StringToIntegerValidator(
+								(NumberFormatConverter) converter);
+					} else if (Long.class.equals(toType)
+							|| Long.TYPE.equals(toType)) {
+						result = new StringToLongValidator(
+								(NumberFormatConverter) converter);
+					} else if (Float.class.equals(toType)
+							|| Float.TYPE.equals(toType)) {
+						result = new StringToFloatValidator(
+								(NumberFormatConverter) converter);
+					} else if (Double.class.equals(toType)
+							|| Double.TYPE.equals(toType)) {
+						result = new StringToDoubleValidator(
+								(NumberFormatConverter) converter);
+					} else if (Byte.class.equals(toType)
+							|| Byte.TYPE.equals(toType)) {
+						result = new StringToByteValidator(
+								(NumberFormatConverter) converter);
+					} else if (Short.class.equals(toType)
+							|| Short.TYPE.equals(toType)) {
+						result = new StringToShortValidator(
+								(NumberFormatConverter) converter);
+					} else if (Character.class.equals(toType)
+							|| Character.TYPE.equals(toType)
+							&& converter instanceof StringToCharacterConverter) {
+						result = new StringToCharacterValidator(
+								(StringToCharacterConverter) converter);
+					} else if (Date.class.equals(toType)
+							&& converter instanceof StringToDateConverter) {
+						result = new StringToDateValidator(
+								(StringToDateConverter) converter);
+					}
+
+					if (result != null) {
+						validatorsByConverter.put(converter, result);
+					}
+				}
+			} else if (converter instanceof NumberToNumberConverter) {
+				result = (IValidator) validatorsByConverter.get(converter);
+
+				if (result == null) {
+					if (converter instanceof NumberToByteConverter) {
+						result = new NumberToByteValidator(
+								(NumberToByteConverter) converter);
+					} else if (converter instanceof NumberToShortConverter) {
+						result = new NumberToShortValidator(
+								(NumberToShortConverter) converter);
+					} else if (converter instanceof NumberToIntegerConverter) {
+						result = new NumberToIntegerValidator(
+								(NumberToIntegerConverter) converter);
+					} else if (converter instanceof NumberToLongConverter) {
+						result = new NumberToLongValidator(
+								(NumberToLongConverter) converter);
+					} else if (converter instanceof NumberToFloatConverter) {
+						result = new NumberToFloatValidator(
+								(NumberToFloatConverter) converter);
+					} else if (converter instanceof NumberToDoubleConverter) {
+						result = new NumberToDoubleValidator(
+								(NumberToDoubleConverter) converter);
+					} else if (converter instanceof NumberToBigIntegerConverter
+							|| converter instanceof NumberToBigDecimalConverter) {
+						result = new NumberToUnboundedNumberValidator(
+								(NumberToNumberConverter) converter);
+					}
+				}
+			}
+
+			if (result == 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 #setAfterConvertValidator(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 == null ? Status.OK_STATUS
+				: afterConvertValidator.validate(value);
+	}
+
+	/**
+	 * Validates the value after it is retrieved from the source.
+	 * <p>
+	 * Default implementation will use the
+	 * {@link #setAfterGetValidator(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 == null ? Status.OK_STATUS : afterGetValidator
+				.validate(value);
+	}
+
+	/**
+	 * Validates the value before it is set on the destination.
+	 * <p>
+	 * Default implementation will use the
+	 * {@link #setBeforeSetValidator(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 == 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
+									.getString(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 ValidatorRegistry() {
+			// Standalone validators here...
+			associate(Integer.class, Integer.TYPE,
+					new ObjectToPrimitiveValidator(Integer.TYPE));
+			associate(Byte.class, Byte.TYPE, new ObjectToPrimitiveValidator(
+					Byte.TYPE));
+			associate(Short.class, Short.TYPE, new ObjectToPrimitiveValidator(
+					Short.TYPE));
+			associate(Long.class, Long.TYPE, new ObjectToPrimitiveValidator(
+					Long.TYPE));
+			associate(Float.class, Float.TYPE, new ObjectToPrimitiveValidator(
+					Float.TYPE));
+			associate(Double.class, Double.TYPE,
+					new ObjectToPrimitiveValidator(Double.TYPE));
+			associate(Boolean.class, Boolean.TYPE,
+					new ObjectToPrimitiveValidator(Boolean.TYPE));
+
+			associate(Object.class, Integer.TYPE,
+					new ObjectToPrimitiveValidator(Integer.TYPE));
+			associate(Object.class, Byte.TYPE, new ObjectToPrimitiveValidator(
+					Byte.TYPE));
+			associate(Object.class, Short.TYPE, new ObjectToPrimitiveValidator(
+					Short.TYPE));
+			associate(Object.class, Long.TYPE, new ObjectToPrimitiveValidator(
+					Long.TYPE));
+			associate(Object.class, Float.TYPE, new ObjectToPrimitiveValidator(
+					Float.TYPE));
+			associate(Object.class, Double.TYPE,
+					new ObjectToPrimitiveValidator(Double.TYPE));
+			associate(Object.class, Boolean.TYPE,
+					new ObjectToPrimitiveValidator(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 = (IValidator) validators.get(new Pair(fromClass,
+					toClass));
+			if (result != null)
+				return result;
+			if (fromClass != null && toClass != null && fromClass == toClass) {
+				return new IValidator() {
+					public IStatus validate(Object value) {
+						return Status.OK_STATUS;
+					}
+				};
+			}
+			return new IValidator() {
+				public IStatus validate(Object value) {
+					return Status.OK_STATUS;
+				}
+			};
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValidationStatusProvider.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValidationStatusProvider.java
new file mode 100644
index 0000000..8b78380
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValidationStatusProvider.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 218269, 146906
+ ******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * 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 boolean disposed = false;
+
+	/**
+	 * @return an {@link IObservableValue}&lt; {@link IStatus} &gt; containing
+	 *         the current validation status
+	 */
+	public abstract IObservableValue getValidationStatus();
+
+	/**
+	 * Returns an {@link IObservableList} &lt; {@link IObservable} &gt;
+	 * containing the target observables (if any) that are being tracked by this
+	 * validation status provider.
+	 * 
+	 * @return an {@link IObservableList} &lt; {@link IObservable} &gt; (may be
+	 *         empty)
+	 */
+	public abstract IObservableList getTargets();
+
+	/**
+	 * Returns an {@link IObservableList} &lt; {@link IObservable} &gt;
+	 * containing the model observables (if any) that are being tracked by this
+	 * validation status provider.
+	 * 
+	 * @return an {@link IObservableList} &lt; {@link IObservable} &gt; (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 boolean isDisposed() {
+		return disposed;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.java
new file mode 100644
index 0000000..61d4e9b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.java
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 220700, 271148, 278550
+ *******************************************************************************/
+
+package org.eclipse.core.databinding;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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 extends Binding {
+	private final UpdateValueStrategy targetToModel;
+	private final UpdateValueStrategy modelToTarget;
+	private WritableValue validationStatusObservable;
+	private IObservableValue target;
+	private IObservableValue model;
+
+	private boolean updatingTarget;
+	private boolean updatingModel;
+	private IValueChangeListener targetChangeListener = new 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 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 ValueBinding(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)) != 0) {
+			target.addValueChangeListener(targetChangeListener);
+		} else {
+			targetChangeListener = null;
+		}
+		if ((modelToTarget.getUpdatePolicy() & (UpdateValueStrategy.POLICY_CONVERT | UpdateValueStrategy.POLICY_UPDATE)) != 0) {
+			model.addValueChangeListener(modelChangeListener);
+		} else {
+			modelChangeListener = null;
+		}
+	}
+
+	protected void preInit() {
+		ObservableTracker.setIgnore(true);
+		try {
+			validationStatusObservable = new WritableValue(context
+					.getValidationRealm(), Status.OK_STATUS, IStatus.class);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	protected void postInit() {
+		if (modelToTarget.getUpdatePolicy() == UpdateValueStrategy.POLICY_UPDATE) {
+			updateModelToTarget();
+		} else if (modelToTarget.getUpdatePolicy() == UpdateValueStrategy.POLICY_CONVERT) {
+			validateModelToTarget();
+		}
+		if (targetToModel.getUpdatePolicy() == UpdateValueStrategy.POLICY_UPDATE
+				|| targetToModel.getUpdatePolicy() == UpdateValueStrategy.POLICY_CONVERT) {
+			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 */boolean 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(final IObservableValue source,
+			final IObservableValue destination,
+			final UpdateValueStrategy updateValueStrategy,
+			final boolean explicit, final boolean validateOnly) {
+
+		final int policy = updateValueStrategy.getUpdatePolicy();
+		if (policy == UpdateValueStrategy.POLICY_NEVER)
+			return;
+		if (policy == UpdateValueStrategy.POLICY_ON_REQUEST && !explicit)
+			return;
+
+		source.getRealm().exec(new Runnable() {
+			public void run() {
+				boolean 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 == 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(new Runnable() {
+						public void run() {
+							if (destination == target) {
+								updatingTarget = true;
+							} else {
+								updatingModel = true;
+							}
+							try {
+								IStatus setterStatus = updateValueStrategy
+										.doSet(destination, convertedValue);
+
+								mergeStatus(multiStatus, setterStatus);
+							} finally {
+								if (destination == target) {
+									updatingTarget = false;
+								} else {
+									updatingModel = false;
+								}
+								setValidationStatus(multiStatus);
+							}
+						}
+					});
+				} 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() != null) ? ex
+							.getMessage() : ""; //$NON-NLS-1$
+
+					mergeStatus(multiStatus, new Status(IStatus.ERROR,
+							Policy.JFACE_DATABINDING, IStatus.ERROR, message,
+							ex));
+				} finally {
+					if (!destinationRealmReached) {
+						setValidationStatus(multiStatus);
+					}
+
+				}
+			}
+		});
+	}
+
+	public void validateModelToTarget() {
+		doUpdate(model, target, modelToTarget, true, true);
+	}
+
+	public void validateTargetToModel() {
+		doUpdate(target, model, targetToModel, true, true);
+	}
+
+	private void setValidationStatus(final IStatus status) {
+		validationStatusObservable.getRealm().exec(new Runnable() {
+			public void run() {
+				validationStatusObservable.setValue(status);
+			}
+		});
+	}
+
+	public void dispose() {
+		if (targetChangeListener != null) {
+			target.removeValueChangeListener(targetChangeListener);
+			targetChangeListener = null;
+		}
+		if (modelChangeListener != null) {
+			model.removeValueChangeListener(modelChangeListener);
+			modelChangeListener = null;
+		}
+		target = null;
+		model = null;
+		super.dispose();
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/Converter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/Converter.java
new file mode 100644
index 0000000..5a344e4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/Converter.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.databinding.conversion;
+
+
+/**
+ * Abstract base class for converters.
+ * 
+ * @since 1.0
+ *
+ */
+public abstract class Converter implements IConverter {
+
+	private Object fromType;
+	private Object toType;
+
+	/**
+	 * @param fromType
+	 * @param toType
+	 */
+	public Converter(Object fromType, Object toType) {
+		this.fromType = fromType;
+		this.toType = toType;
+	}
+
+	public Object getFromType() {
+		return fromType;
+	}
+
+	public Object getToType() {
+		return toType;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/IConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/IConverter.java
new file mode 100644
index 0000000..ebd21ae
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/IConverter.java
@@ -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
+ *******************************************************************************/
+package org.eclipse.core.databinding.conversion;
+
+/**
+ * A one-way converter.
+ * 
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ *              Clients should subclass {@link Converter}.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface IConverter {
+
+	/**
+	 * Returns the type whose instances can be converted by this converter. The
+	 * return type is Object rather than Class to optionally support richer type
+	 * systems than the one provided by Java reflection.
+	 * 
+	 * @return the type whose instances can be converted, or null if this
+	 *         converter is untyped
+	 */
+	public Object getFromType();
+
+	/**
+	 * Returns the type to which this converter can convert. The return type is
+	 * Object rather than Class to optionally support richer type systems than
+	 * the one provided by Java reflection.
+	 * 
+	 * @return the type to which this converter can convert, or null if this
+	 *         converter is untyped
+	 */
+	public Object getToType();
+
+	/**
+	 * Returns the result of the conversion of the given object.
+	 * 
+	 * @param fromObject
+	 *            the object to convert, of type {@link #getFromType()}
+	 * @return the converted object, of type {@link #getToType()}
+	 */
+	public Object convert(Object fromObject);
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/NumberToStringConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/NumberToStringConverter.java
new file mode 100644
index 0000000..359e3bb
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/NumberToStringConverter.java
@@ -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
+ *     Matt Carter - bug 180392
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.conversion;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.ibm.icu.text.DecimalFormat;
+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 extends Converter {
+	private final NumberFormat numberFormat;
+	private final Class fromType;
+	private boolean fromTypeFitsLong;
+	private boolean fromTypeIsDecimalType;
+	private boolean fromTypeIsBigInteger;
+	private boolean fromTypeIsBigDecimal;
+
+	static Class icuBigDecimal = null;
+	static Constructor icuBigDecimalCtr = null; 
+	
+	{
+		/*
+		 * If the full ICU4J library is available, we use the ICU BigDecimal
+		 * class to support proper formatting and parsing of java.math.BigDecimal.
+		 * 
+		 * The version of ICU NumberFormat (DecimalFormat) included in eclipse excludes 
+		 * support for java.math.BigDecimal, and if used falls back to converting as
+		 * an unknown Number type via doubleValue(), which is undesirable.
+		 * 
+		 * See Bug #180392.
+		 */
+		try {
+			icuBigDecimal = Class.forName("com.ibm.icu.math.BigDecimal"); //$NON-NLS-1$
+			icuBigDecimalCtr = icuBigDecimal.getConstructor(new Class[] {BigInteger.class, int.class});
+//			System.out.println("DEBUG: Full ICU4J support state: icuBigDecimal="+(icuBigDecimal != null)+", icuBigDecimalCtr="+(icuBigDecimalCtr != null)); //$NON-NLS-1$ //$NON-NLS-2$
+		} 
+		catch(ClassNotFoundException e) {}
+		catch(NoSuchMethodException e) {}
+	}	
+	
+	/**
+	 * Constructs a new instance.
+	 * <p>
+	 * Private to restrict public instantiation.
+	 * </p>
+	 * 
+	 * @param numberFormat
+	 * @param fromType
+	 */
+	private NumberToStringConverter(NumberFormat numberFormat, Class fromType) {
+		super(fromType, String.class);
+
+		this.numberFormat = numberFormat;
+		this.fromType = fromType;
+
+		if (Integer.class.equals(fromType) || Integer.TYPE.equals(fromType)
+				|| Long.class.equals(fromType) || Long.TYPE.equals(fromType)
+				|| Short.class.equals(fromType) || Short.TYPE.equals(fromType)
+				|| Byte.class.equals(fromType) || Byte.TYPE.equals(fromType)) {
+			fromTypeFitsLong = true;
+		} else if (Float.class.equals(fromType) || Float.TYPE.equals(fromType)
+				|| Double.class.equals(fromType)
+				|| Double.TYPE.equals(fromType)) {
+			fromTypeIsDecimalType = true;
+		} else if (BigInteger.class.equals(fromType)) {
+			fromTypeIsBigInteger = true;
+		} else if (BigDecimal.class.equals(fromType)) {
+			fromTypeIsBigDecimal = 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 == null && !fromType.isPrimitive()) {
+			return ""; //$NON-NLS-1$
+		}
+
+		Number number = (Number) fromObject;
+		String result = null;
+		if (fromTypeFitsLong) {
+			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((BigInteger) number);
+			}
+		} else if (fromTypeIsBigDecimal) {
+			if(icuBigDecimal != null && icuBigDecimalCtr != null && numberFormat instanceof DecimalFormat) {
+				// Full ICU4J present. Convert java.math.BigDecimal to ICU BigDecimal to format. Bug #180392.
+				BigDecimal o = (BigDecimal) fromObject;
+				try {
+					fromObject = icuBigDecimalCtr.newInstance(new Object[] {o.unscaledValue(), new Integer(o.scale())});
+				}
+				catch(InstantiationException e) {}
+				catch(InvocationTargetException e) {}
+				catch(IllegalAccessException e) {}
+				// Otherwise, replacement plugin present and supports java.math.BigDecimal.
+			}
+			synchronized (numberFormat) {
+				result = numberFormat.format(fromObject);
+			}
+		}
+		
+
+		return result;
+	}
+
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the type is a double
+	 * @return Double converter for the default locale
+	 */
+	public static NumberToStringConverter fromDouble(boolean primitive) {
+		return fromDouble(NumberFormat.getNumberInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return Double converter with the provided numberFormat
+	 */
+	public static NumberToStringConverter fromDouble(NumberFormat numberFormat,
+			boolean primitive) {
+		return new NumberToStringConverter(numberFormat,
+				(primitive) ? Double.TYPE : Double.class);
+	}
+
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the type is a long
+	 * @return Long converter for the default locale
+	 */
+	public static NumberToStringConverter fromLong(boolean primitive) {
+		return fromLong(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return Long convert with the provided numberFormat
+	 */
+	public static NumberToStringConverter fromLong(NumberFormat numberFormat,
+			boolean primitive) {
+		return new NumberToStringConverter(numberFormat,
+				(primitive) ? Long.TYPE : Long.class);
+	}
+
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the type is a float
+	 * @return Float converter for the default locale
+	 */
+	public static NumberToStringConverter fromFloat(boolean primitive) {
+		return fromFloat(NumberFormat.getNumberInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return Float converter with the provided numberFormat
+	 */
+	public static NumberToStringConverter fromFloat(NumberFormat numberFormat,
+			boolean primitive) {
+		return new NumberToStringConverter(numberFormat,
+				(primitive) ? Float.TYPE : Float.class);
+	}
+
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the type is a int
+	 * @return Integer converter for the default locale
+	 */
+	public static NumberToStringConverter fromInteger(boolean primitive) {
+		return fromInteger(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return Integer converter with the provided numberFormat
+	 */
+	public static NumberToStringConverter fromInteger(
+			NumberFormat numberFormat, boolean primitive) {
+		return new NumberToStringConverter(numberFormat,
+				(primitive) ? Integer.TYPE : Integer.class);
+	}
+
+	/**
+	 * @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.class);
+	}
+	
+	/**
+	 * @return BigDecimal convert for the default locale
+	 * @since 1.2
+	 */
+	public static NumberToStringConverter fromBigDecimal() {
+		return fromBigDecimal(NumberFormat.getNumberInstance());
+	}
+
+	/**
+	 * @param numberFormat
+	 * @return BigDecimal converter with the provided numberFormat
+	 * @since 1.2
+	 */
+	public static NumberToStringConverter fromBigDecimal(
+			NumberFormat numberFormat) {
+		return new NumberToStringConverter(numberFormat, BigDecimal.class);
+	}
+	
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the type is a short
+	 * @return Short converter for the default locale
+	 * @since 1.2
+	 */
+	public static NumberToStringConverter fromShort(boolean primitive) {
+		return fromShort(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return Short converter with the provided numberFormat
+	 * @since 1.2
+	 */
+	public static NumberToStringConverter fromShort(
+			NumberFormat numberFormat, boolean primitive) {
+		return new NumberToStringConverter(numberFormat,
+				(primitive) ? Short.TYPE : Short.class);
+	}
+	
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the type is a byte
+	 * @return Byte converter for the default locale
+	 * @since 1.2
+	 */
+	public static NumberToStringConverter fromByte(boolean primitive) {
+		return fromByte(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return Byte converter with the provided numberFormat
+	 * @since 1.2
+	 */
+	public static NumberToStringConverter fromByte(
+			NumberFormat numberFormat, boolean primitive) {
+		return new NumberToStringConverter(numberFormat,
+				(primitive) ? Byte.TYPE : Byte.class);
+	}
+	
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/StringToNumberConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/StringToNumberConverter.java
new file mode 100644
index 0000000..ca470cf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/StringToNumberConverter.java
@@ -0,0 +1,401 @@
+/*******************************************************************************
+ * 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
+ *     Michael Scharf - bug 240562
+ *     Matt Carter - bug 180392
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.conversion;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+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 extends NumberFormatConverter {
+	private Class 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 Class boxedType;
+
+	private static final Integer MIN_INTEGER = new Integer(Integer.MIN_VALUE);
+	private static final Integer MAX_INTEGER = new Integer(Integer.MAX_VALUE);
+
+	// This code looks deceptive, but we can't use Double.MIN_VALUE because it
+	// is actually the smallest *positive* number.
+	private static final Double MIN_DOUBLE = new Double(-Double.MAX_VALUE);
+	private static final Double MAX_DOUBLE = new Double(Double.MAX_VALUE);
+
+	private static final Long MIN_LONG = new Long(Long.MIN_VALUE);
+	private static final Long MAX_LONG = new Long(Long.MAX_VALUE);
+
+	// This code looks deceptive, but we can't use Float.MIN_VALUE because it is
+	// actually the smallest *positive* number.
+	private static final Float MIN_FLOAT = new Float(-Float.MAX_VALUE);
+	private static final Float MAX_FLOAT = new Float(Float.MAX_VALUE);
+
+	private static final Short MIN_SHORT = new Short(Short.MIN_VALUE);
+	private static final Short MAX_SHORT = new Short(Short.MAX_VALUE);
+	
+	private static final Byte MIN_BYTE = new Byte(Byte.MIN_VALUE);
+	private static final Byte MAX_BYTE = new Byte(Byte.MAX_VALUE);
+	
+	static Class icuBigDecimal = null;
+	static Method icuBigDecimalScale = null;
+	static Method icuBigDecimalUnscaledValue = null;
+	
+	{
+		/*
+		 * If the full ICU4J library is available, we use the ICU BigDecimal
+		 * class to support proper formatting and parsing of java.math.BigDecimal.
+		 * 
+		 * The version of ICU NumberFormat (DecimalFormat) included in eclipse excludes 
+		 * support for java.math.BigDecimal, and if used falls back to converting as
+		 * an unknown Number type via doubleValue(), which is undesirable.
+		 * 
+		 * See Bug #180392.
+		 */
+		try {
+			icuBigDecimal = Class.forName("com.ibm.icu.math.BigDecimal"); //$NON-NLS-1$
+			icuBigDecimalScale = icuBigDecimal.getMethod("scale", null); //$NON-NLS-1$
+			icuBigDecimalUnscaledValue = icuBigDecimal.getMethod("unscaledValue", null); //$NON-NLS-1$
+/*			System.out.println("DEBUG: Full ICU4J support state: icuBigDecimal="+ //$NON-NLS-1$
+					(icuBigDecimal != null)+", icuBigDecimalScale="+(icuBigDecimalScale != null)+ //$NON-NLS-1$
+					", icuBigDecimalUnscaledValue="+(icuBigDecimalUnscaledValue != null)); //$NON-NLS-1$ */  
+		} 
+		catch(ClassNotFoundException e) {}
+		catch(NoSuchMethodException e) {}
+	}		
+	/**
+	 * @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 StringToNumberConverter(NumberFormat numberFormat, Class toType,
+			Number min, Number max, Class boxedType) {
+		super(String.class, 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() != 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((String) fromObject, result
+							.getPosition()));
+		} else if (result.getNumber() == 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.class.equals(boxedType)) {
+			if (StringToNumberParser.inIntegerRange(result.getNumber())) {
+				return new Integer(result.getNumber().intValue());
+			}
+		} else if (Double.class.equals(boxedType)) {
+			if (StringToNumberParser.inDoubleRange(result.getNumber())) {
+				return new Double(result.getNumber().doubleValue());
+			}
+		} else if (Long.class.equals(boxedType)) {
+			if (StringToNumberParser.inLongRange(result.getNumber())) {
+				return new Long(result.getNumber().longValue());
+			}
+		} else if (Float.class.equals(boxedType)) {
+			if (StringToNumberParser.inFloatRange(result.getNumber())) {
+				return new Float(result.getNumber().floatValue());
+			}
+		} else if (BigInteger.class.equals(boxedType)) {
+			Number n = result.getNumber();
+			if(n instanceof Long)
+				return BigInteger.valueOf(n.longValue());
+			else if(n instanceof BigInteger)
+				return n;
+			else if(n instanceof BigDecimal)
+				return ((BigDecimal) n).toBigInteger();
+			else
+				return new BigDecimal(n.doubleValue()).toBigInteger();
+		} else if (BigDecimal.class.equals(boxedType)) {
+			Number n = result.getNumber();
+			if(n instanceof Long)
+				return BigDecimal.valueOf(n.longValue());
+			else if(n instanceof BigInteger)
+				return new BigDecimal((BigInteger) n);
+			else if(n instanceof BigDecimal)
+				return n;
+			else if(icuBigDecimal != null && icuBigDecimal.isInstance(n)) {
+				try {
+					// Get ICU BigDecimal value and use to construct java.math.BigDecimal
+					int scale = ((Integer) icuBigDecimalScale.invoke(n, null)).intValue();
+					BigInteger unscaledValue = (BigInteger) icuBigDecimalUnscaledValue.invoke(n, null);
+					return new java.math.BigDecimal(unscaledValue, scale);
+				} catch(IllegalAccessException e) {
+					throw new IllegalArgumentException("Error (IllegalAccessException) converting BigDecimal using ICU"); //$NON-NLS-1$
+				} catch(InvocationTargetException e) {
+					throw new IllegalArgumentException("Error (InvocationTargetException) converting BigDecimal using ICU"); //$NON-NLS-1$
+				}
+			} else if(n instanceof Double) {
+				BigDecimal bd = new BigDecimal(n.doubleValue());
+				if(bd.scale() == 0) return bd;
+				throw new IllegalArgumentException("Non-integral Double value returned from NumberFormat " + //$NON-NLS-1$
+						"which cannot be accurately stored in a BigDecimal due to lost precision. " + //$NON-NLS-1$
+						"Consider using ICU4J or Java 5 which can properly format and parse these types."); //$NON-NLS-1$
+			}
+		} else if (Short.class.equals(boxedType)) {
+			if (StringToNumberParser.inShortRange(result.getNumber())) {
+				return new Short(result.getNumber().shortValue());
+			}
+		} else if (Byte.class.equals(boxedType)) {
+			if (StringToNumberParser.inByteRange(result.getNumber())) {
+				return new Byte(result.getNumber().byteValue());
+			}
+		}
+
+		if (min != null && max != 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(boolean primitive) {
+		return toInteger(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Integer converter with the provided numberFormat
+	 */
+	public static StringToNumberConverter toInteger(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToNumberConverter(numberFormat,
+				(primitive) ? Integer.TYPE : Integer.class, MIN_INTEGER,
+				MAX_INTEGER, Integer.class);
+	}
+
+	/**
+	 * @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(boolean primitive) {
+		return toDouble(NumberFormat.getNumberInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Double converter with the provided numberFormat
+	 */
+	public static StringToNumberConverter toDouble(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToNumberConverter(numberFormat,
+				(primitive) ? Double.TYPE : Double.class, MIN_DOUBLE,
+				MAX_DOUBLE, Double.class);
+	}
+
+	/**
+	 * @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(boolean primitive) {
+		return toLong(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Long converter with the provided numberFormat
+	 */
+	public static StringToNumberConverter toLong(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToNumberConverter(numberFormat,
+				(primitive) ? Long.TYPE : Long.class, MIN_LONG, MAX_LONG,
+				Long.class);
+	}
+
+	/**
+	 * @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(boolean primitive) {
+		return toFloat(NumberFormat.getNumberInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Float converter with the provided numberFormat
+	 */
+	public static StringToNumberConverter toFloat(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToNumberConverter(numberFormat,
+				(primitive) ? Float.TYPE : Float.class, MIN_FLOAT, MAX_FLOAT,
+				Float.class);
+	}
+
+	/**
+	 * @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.class,
+				null, null, BigInteger.class);
+	}
+
+	/**
+	 * @return to BigDecimal converter for the default locale
+	 * @since 1.2
+	 */
+	public static StringToNumberConverter toBigDecimal() {
+		return toBigDecimal(NumberFormat.getNumberInstance());
+	}
+	
+	/**
+	 * @param numberFormat
+	 * @return to BigDecimal converter with the provided numberFormat
+	 * @since 1.2
+	 */
+	public static StringToNumberConverter toBigDecimal(NumberFormat numberFormat) {
+		return new StringToNumberConverter(numberFormat, BigDecimal.class,
+				null, null, BigDecimal.class);
+	}
+	
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the convert to type is a short
+	 * @return to Short converter for the default locale
+	 * @since 1.2
+	 */
+	public static StringToNumberConverter toShort(boolean primitive) {
+		return toShort(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Short converter with the provided numberFormat
+	 * @since 1.2
+	 */
+	public static StringToNumberConverter toShort(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToNumberConverter(numberFormat,
+				(primitive) ? Short.TYPE : Short.class, MIN_SHORT,
+				MAX_SHORT, Short.class);
+	}
+	
+	/**
+	 * @param primitive
+	 *            <code>true</code> if the convert to type is a byte
+	 * @return to Byte converter for the default locale
+	 * @since 1.2
+	 */
+	public static StringToNumberConverter toByte(boolean primitive) {
+		return toByte(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Byte converter with the provided numberFormat
+	 * @since 1.2
+	 */
+	public static StringToNumberConverter toByte(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToNumberConverter(numberFormat,
+				(primitive) ? Byte.TYPE : Byte.class, MIN_BYTE,
+				MAX_BYTE, Byte.class);
+	}
+	
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/package.html b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/package.html
new file mode 100644
index 0000000..06a1b53
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/package.html b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/package.html
new file mode 100644
index 0000000..9087bbf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/IValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/IValidator.java
new file mode 100644
index 0000000..a9c4b8b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/IValidator.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.databinding.validation;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * A validator. This validator is responsible for determining if a given value
+ * is valid. Validators can be used on target or model values. For example, a
+ * String2IntValidator would only accept source Strings that can successfully be
+ * converted to an integer value, and a PositiveIntegerValidator would only
+ * accept positive integers.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface IValidator {
+
+	/**
+	 * Determines if the given value is valid.
+	 * 
+	 * @param value
+	 *            the value to validate
+	 * @return a status object indicating whether the validation succeeded
+	 *         {@link IStatus#isOK()} or not. Never null.
+	 */
+	public IStatus validate(Object value);
+	
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/MultiValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/MultiValidator.java
new file mode 100644
index 0000000..2a5ca85
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/MultiValidator.java
@@ -0,0 +1,529 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Matthew Hall - bug 237884, 240590, 251003, 251424, 278550
+ *     Ovidio Mallo - bug 238909, 235859
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.validation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+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.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+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.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.AbstractObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.internal.databinding.Util;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableList;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableMap;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableSet;
+import org.eclipse.core.internal.databinding.validation.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 (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 MultiValidator() {
+ * 	protected IStatus validate() {
+ * 		// Calculate the validation status
+ * 		Integer value0 = (Integer) middle0.getValue();
+ * 		Integer value1 = (Integer) middle1.getValue();
+ * 		if (Math.abs(value0.intValue()) % 2 != 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 extends ValidationStatusProvider {
+	private Realm realm;
+	private ValidationStatusObservableValue validationStatus;
+	private IObservableValue unmodifiableValidationStatus;
+	private WritableList targets;
+	private IObservableList unmodifiableTargets;
+	private IObservableList models;
+
+	IListChangeListener targetsListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			event.diff.accept(new ListDiffVisitor() {
+				public void handleAdd(int index, Object element) {
+					IObservable dependency = (IObservable) element;
+					dependency.addChangeListener(dependencyListener);
+					dependency.addStaleListener(dependencyListener);
+				}
+
+				public void handleRemove(int index, Object element) {
+					IObservable dependency = (IObservable) element;
+					dependency.removeChangeListener(dependencyListener);
+					dependency.removeStaleListener(dependencyListener);
+				}
+			});
+		}
+	};
+
+	private class DependencyListener implements IChangeListener, IStaleListener {
+		public void handleChange(ChangeEvent event) {
+			revalidate();
+		}
+
+		public void handleStale(StaleEvent staleEvent) {
+			validationStatus.makeStale();
+		}
+	}
+
+	private DependencyListener dependencyListener = new DependencyListener();
+
+	/**
+	 * Constructs a MultiValidator on the default realm.
+	 */
+	public MultiValidator() {
+		this(Realm.getDefault());
+	}
+
+	/**
+	 * Constructs a MultiValidator on the given realm.
+	 * 
+	 * @param realm
+	 *            the realm on which validation takes place.
+	 */
+	public MultiValidator(Realm realm) {
+		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
+		this.realm = realm;
+
+		ObservableTracker.setIgnore(true);
+		try {
+			validationStatus = new ValidationStatusObservableValue(realm);
+
+			targets = new WritableList(realm, new ArrayList(),
+					IObservable.class);
+			targets.addListChangeListener(targetsListener);
+			unmodifiableTargets = Observables
+					.unmodifiableObservableList(targets);
+
+			models = Observables.emptyObservableList(realm);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	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 == null) {
+			revalidate();
+			ObservableTracker.setIgnore(true);
+			try {
+				unmodifiableValidationStatus = Observables
+						.unmodifiableObservableValue(validationStatus);
+			} finally {
+				ObservableTracker.setIgnore(false);
+			}
+		}
+		return unmodifiableValidationStatus;
+	}
+
+	/**
+	 * Signals that a re-evaluation of the current validation status is
+	 * necessary.
+	 * <p>
+	 * Clients may invoke this method whenever the validation status needs to be
+	 * updated due to some state change which cannot be automatically tracked by
+	 * the MultiValidator as it is not captured by any {@link IObservable}
+	 * instance.
+	 * <p>
+	 * Note: There is no guarantee as of whether the MultiValidator will
+	 * immediately re-evaluate the validation status by calling
+	 * {@link #validate} when becoming dirty. Instead, it may decide to perform
+	 * the re-evaluation lazily.
+	 * 
+	 * @see #validate()
+	 * @since 1.2
+	 */
+	protected final void revalidate() {
+		class ValidationRunnable implements Runnable {
+			IStatus validationResult;
+
+			public void run() {
+				try {
+					validationResult = validate();
+					if (validationResult == null)
+						validationResult = ValidationStatus.ok();
+				} catch (RuntimeException e) {
+					// Usually an NPE as dependencies are init'ed
+					validationResult = ValidationStatus
+							.error(e.getMessage(), e);
+				}
+			}
+		}
+
+		ValidationRunnable validationRunnable = new ValidationRunnable();
+		final IObservable[] dependencies = ObservableTracker.runAndMonitor(
+				validationRunnable, null, null);
+
+		ObservableTracker.setIgnore(true);
+		try {
+			List newTargets = new ArrayList(Arrays.asList(dependencies));
+
+			// This loop is roughly equivalent to:
+			// targets.retainAll(newTargets);
+			// newTargets.removeAll(targets);
+			// Except that dependencies are compared by identity instead of
+			// equality
+			outer: for (int i = targets.size() - 1; i >= 0; i--) {
+				Object oldDependency = targets.get(i);
+				for (Iterator itNew = newTargets.iterator(); itNew.hasNext();) {
+					Object newDependency = itNew.next();
+					if (oldDependency == newDependency) {
+						// Dependency is already known--remove from list of
+						// new dependencies
+						itNew.remove();
+						continue outer;
+					} else if (newDependency == validationStatus
+							|| newDependency == unmodifiableValidationStatus
+							|| newDependency == targets
+							|| newDependency == unmodifiableTargets
+							|| newDependency == models) {
+						// Internal observables should not be dependencies
+						// (prevent dependency loop)
+						itNew.remove();
+					}
+				}
+				// Old dependency is no longer a dependency--remove from
+				// targets
+				targets.remove(i);
+			}
+
+			targets.addAll(newTargets);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		// Once the dependencies are up-to-date, we set the new status.
+		validationStatus.setValue(validationRunnable.validationResult);
+	}
+
+	/**
+	 * Returns the current validation status.
+	 * <p>
+	 * Note: To ensure that the validation status is kept current automatically,
+	 * 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. Other dependencies not captured by the
+	 * state of those observables may be accounted for by having clients
+	 * <i>explicitly</i> call {@link #revalidate()} whenever the validation
+	 * status needs to be re-evaluated due to some arbitrary change in the
+	 * application state.
+	 * 
+	 * @return the current validation status.
+	 * 
+	 * @see #revalidate()
+	 */
+	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() {
+		if (targets != null) {
+			targets.clear(); // Remove listeners from dependencies
+		}
+
+		if (unmodifiableValidationStatus != null) {
+			unmodifiableValidationStatus.dispose();
+			unmodifiableValidationStatus = null;
+		}
+
+		if (validationStatus != null) {
+			validationStatus.dispose();
+			validationStatus = null;
+		}
+
+		if (unmodifiableTargets != null) {
+			unmodifiableTargets.dispose();
+			unmodifiableTargets = null;
+		}
+
+		if (targets != null) {
+			targets.dispose();
+			targets = null;
+		}
+
+		if (models != null) {
+			models.dispose();
+			models = null;
+		}
+
+		realm = null;
+
+		super.dispose();
+	}
+
+	private class ValidationStatusObservableValue extends
+			AbstractObservableValue {
+		private Object value = ValidationStatus.ok();
+
+		private boolean stale = false;
+
+		public ValidationStatusObservableValue(Realm realm) {
+			super(realm);
+		}
+
+		protected Object doGetValue() {
+			return value;
+		}
+
+		protected void doSetValue(Object value) {
+			boolean oldStale = stale;
+
+			// Update the staleness state by checking whether any of the current
+			// dependencies is stale.
+			stale = false;
+			for (Iterator iter = targets.iterator(); iter.hasNext();) {
+				IObservable dependency = (IObservable) iter.next();
+				if (dependency.isStale()) {
+					stale = true;
+					break;
+				}
+			}
+
+			Object oldValue = this.value;
+			this.value = value;
+
+			// If either becoming non-stale or setting a new value, we must fire
+			// a value change event.
+			if ((oldStale && !stale) || !Util.equals(oldValue, value)) {
+				fireValueChange(Diffs.createValueDiff(oldValue, value));
+			} else if (!oldStale && stale) {
+				fireStale();
+			}
+		}
+
+		void makeStale() {
+			if (!stale) {
+				stale = true;
+				fireStale();
+			}
+		}
+
+		public boolean isStale() {
+			ObservableTracker.getterCalled(this);
+			return stale;
+		}
+
+		public Object getValueType() {
+			return IStatus.class;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/ValidationStatus.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/ValidationStatus.java
new file mode 100644
index 0000000..d61cb19
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/ValidationStatus.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.databinding.validation;
+
+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 extends Status {
+
+	/**
+	 * Creates a new validation status with the given severity, message, and
+	 * exception.
+	 * 
+	 * @param severity
+	 * @param message
+	 * @param exception
+	 */
+	private ValidationStatus(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 ValidationStatus(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 == null) ? 0 : message.hashCode());
+		result = prime * result + severity;
+		result = prime * result
+				+ ((throwable == null) ? 0 : throwable.hashCode());
+		return result;
+	}
+
+	/**
+	 * Equality is based upon instance equality rather than identity.
+	 * 
+	 * @see java.lang.Object#equals(java.lang.Object)
+	 */
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final ValidationStatus other = (ValidationStatus) obj;
+
+		if (getSeverity() != other.getSeverity())
+			return false;
+		if (getMessage() == null) {
+			if (other.getMessage() != null)
+				return false;
+		} else if (!getMessage().equals(other.getMessage()))
+			return false;
+		if (getException() == null) {
+			if (other.getException() != null)
+				return false;
+		} else if (!getException().equals(other.getException()))
+			return false;
+		return true;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/package.html b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/package.html
new file mode 100644
index 0000000..3d98b2e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/package.html
@@ -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>
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Activator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Activator.java
new file mode 100644
index 0000000..5f387b9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Activator.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+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 implements 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 Activator() {
+	}
+
+	public void start(BundleContext context) throws Exception {
+		_frameworkLogTracker = new ServiceTracker(context, FrameworkLog.class.getName(), null);
+		_frameworkLogTracker.open();
+
+		Policy.setLog(new ILogger() {
+
+			public void log(IStatus status) {
+				ServiceTracker frameworkLogTracker = _frameworkLogTracker;
+				FrameworkLog log = frameworkLogTracker == null ? null : (FrameworkLog) frameworkLogTracker.getService();
+				if (log != 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() != 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 = t instanceof CoreException ? 1 : 0;
+		// ensure a substatus inside a CoreException is properly logged 
+		if (stackCode == 1) {
+			IStatus coreStatus = ((CoreException) t).getStatus();
+			if (coreStatus != 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 = (FrameworkLogEntry[]) (childlist.size() == 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) throws Exception {
+		if (_frameworkLogTracker != null) {
+			_frameworkLogTracker.close();
+			_frameworkLogTracker = null;
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.java
new file mode 100644
index 0000000..3e93df2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import com.ibm.icu.text.MessageFormat;
+
+/**
+ * @since 1.0
+ *
+ */
+public class BindingMessages {
+
+	/**
+	 * The Binding resource bundle; eagerly initialized.
+	 */
+	private static final ResourceBundle bundle = ResourceBundle
+			.getBundle("org.eclipse.core.internal.databinding.messages"); //$NON-NLS-1$
+
+	/**
+	 * Key to be used for an index out of range message.
+	 */
+	public static final String INDEX_OUT_OF_RANGE = "IndexOutOfRange"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Multiple Problems." message.
+	 */
+	public static final String MULTIPLE_PROBLEMS = "MultipleProblems"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "ValueBinding_ErrorWhileSettingValue" message
+	 */
+	public static final String VALUEBINDING_ERROR_WHILE_SETTING_VALUE = "ValueBinding_ErrorWhileSettingValue"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "DateFormat_DateTime" message
+	 */
+	public static final String DATE_FORMAT_DATE_TIME = "DateFormat_DateTime"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "DateFormat_Time" message
+	 */
+	public static final String DATEFORMAT_TIME = "DateFormat_Time"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "ValueDelimiter" message
+	 */
+	public static final String VALUE_DELIMITER = "ValueDelimiter"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "TrueStringValues" message
+	 */
+	public static final String TRUE_STRING_VALUES = "TrueStringValues"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "FalseStringValues" message
+	 */
+	public static final String FALSE_STRING_VALUES = "FalseStringValues"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_NumberOutOfRangeError" message
+	 */
+	public static final String VALIDATE_NUMBER_OUT_OF_RANGE_ERROR = "Validate_NumberOutOfRangeError"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_NumberParseError" message
+	 */
+	public static final String VALIDATE_NUMBER_PARSE_ERROR = "Validate_NumberParseError"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_ConversionToPrimitive" message
+	 */
+	public static final String VALIDATE_CONVERSION_TO_PRIMITIVE = "Validate_ConversionToPrimitive"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_ConversionFromClassToPrimitive" message
+	 */
+	public static final String VALIDATE_CONVERSION_FROM_CLASS_TO_PRIMITIVE = "Validate_ConversionFromClassToPrimitive"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_NoChangeAllowedHelp" message
+	 */
+	public static final String VALIDATE_NO_CHANGE_ALLOWED_HELP = "Validate_NoChangeAllowedHelp"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_CharacterHelp" message
+	 */
+	public static final String VALIDATE_CHARACTER_HELP = "Validate_CharacterHelp"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Examples" message
+	 */
+	public static final String EXAMPLES = "Examples"; //$NON-NLS-1$
+
+	/**
+	 * Key to be used for a "Validate_NumberParseErrorNoCharacter" message
+	 */
+	public static final String VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER = "Validate_NumberParseErrorNoCharacter"; //$NON-NLS-1$
+
+	/**
+	 * Returns the resource object with the given key in the resource bundle for
+	 * JFace Data Binding. If there isn't any value under the given key, the key
+	 * is returned.
+	 *
+	 * @param key
+	 *            the resource name
+	 * @return the string
+	 */
+	public static String getString(String key) {
+		try {
+			return bundle.getString(key);
+		} catch (MissingResourceException e) {
+			return key;
+		}
+	}
+
+	/**
+	 * Returns a formatted string with the given key in the resource bundle for
+	 * JFace Data Binding.
+	 *
+	 * @param key
+	 * @param arguments
+	 * @return formatted string, the key if the key is invalid
+	 */
+	public static String formatString(String key, Object[] arguments) {
+		try {
+			return MessageFormat.format(bundle.getString(key), arguments);
+		} catch (MissingResourceException e) {
+			return key;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingModelProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingModelProperty.java
new file mode 100644
index 0000000..163dfb8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingModelProperty.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BindingModelProperty extends SimpleValueProperty {
+	public Object getValueType() {
+		return IObservable.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Binding) source).getModel();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		// no setter API
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		// no listener API
+		return null;
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+
+	public String toString() {
+		return "Binding#model <IObservable>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingStatus.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingStatus.java
new file mode 100644
index 0000000..025b82c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingStatus.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+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 extends MultiStatus {
+	/**
+	 * Constructs a new instance.
+	 * 
+	 * @param pluginId
+	 * @param code
+	 * @param message
+	 * @param exception
+	 */
+	public BindingStatus(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() != 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 == null)
+			return 0;
+		int result = 1;
+		for (int index = 0; index < array.length; index++) {
+			result = prime * result
+					+ (array[index] == 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 boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final BindingStatus other = (BindingStatus) obj;
+		if (!Arrays.equals(getChildren(), other.getChildren()))
+			return false;
+		return true;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingTargetProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingTargetProperty.java
new file mode 100644
index 0000000..c0fda1b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingTargetProperty.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class BindingTargetProperty extends SimpleValueProperty {
+	public Object getValueType() {
+		return IObservable.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Binding) source).getTarget();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		// no setter API
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		// no listener API
+		return null;
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+
+	public String toString() {
+		return "Binding#target <IObservable>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ClassLookupSupport.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ClassLookupSupport.java
new file mode 100644
index 0000000..7b3e04a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ClassLookupSupport.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+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 Class[] getTypeHierarchyFlattened(Class type) {
+		List classes = null;
+		//cache reference to lookup to protect against concurrent flush
+		HashMap lookup = classSearchOrderLookup;
+		if (lookup != null)
+			classes = (List) lookup.get(type);
+		// compute class order only if it hasn't been cached before
+		if (classes == null) {
+			classes = new ArrayList();
+			computeClassOrder(type, classes);
+			if (lookup == null)
+				classSearchOrderLookup = lookup = new HashMap();
+			lookup.put(type, classes);
+		}
+		return (Class[]) classes.toArray(new Class[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(Class adaptable, Collection classes) {
+		Class clazz = adaptable;
+		Set seen = new HashSet(4);
+		while (clazz != null) {
+			classes.add(clazz);
+			computeInterfaceOrder(clazz.getInterfaces(), classes, seen);
+			clazz = clazz.isInterface() ? Object.class : clazz.getSuperclass();
+		}
+	}
+
+	private static void computeInterfaceOrder(Class[] interfaces, Collection classes, Set seen) {
+		List newInterfaces = new ArrayList(interfaces.length);
+		for (int i = 0; i < interfaces.length; i++) {
+			Class 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(((Class) it.next()).getInterfaces(), classes, seen);
+	}
+
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ConverterValueProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ConverterValueProperty.java
new file mode 100644
index 0000000..32ca3a7
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ConverterValueProperty.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 306611)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * Simple value property which applies a given converter on a source object in
+ * order to produce the property's value.
+ */
+public class ConverterValueProperty extends SimpleValueProperty {
+
+	private final IConverter converter;
+
+	/**
+	 * Creates a new value property which applies the given converter on the
+	 * source object in order to produce the property's value.
+	 * 
+	 * @param converter
+	 *            The converter to apply to the source object.
+	 */
+	public ConverterValueProperty(IConverter converter) {
+		this.converter = converter;
+	}
+
+	public Object getValueType() {
+		// the property type is the converter's target type
+		return converter.getToType();
+	}
+
+	public Object getValue(Object source) {
+		// We do also pass null values to the converter.
+		return doGetValue(source);
+	}
+
+	protected Object doGetValue(Object source) {
+		// delegate to the IConverter
+		return converter.convert(source);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		// setting a value is not supported
+		throw new UnsupportedOperationException(toString()
+				+ ": Setter not supported on a converted value!"); //$NON-NLS-1$
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		// no listener API
+		return null;
+	}
+
+	public String toString() {
+		return "IConverter#convert(source) <IConverter#getToType()>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/DataBindingContextBindingsProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/DataBindingContextBindingsProperty.java
new file mode 100644
index 0000000..39dbb0d
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/DataBindingContextBindingsProperty.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.list.ListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class DataBindingContextBindingsProperty extends ListProperty {
+	public Object getElementType() {
+		return Binding.class;
+	}
+
+	protected List doGetList(Object source) {
+		return ((DataBindingContext) source).getBindings();
+	}
+
+	protected void doSetList(Object source, List list) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return ((DataBindingContext) source).getBindings();
+	}
+
+	public String toString() {
+		return "DataBindingContext#bindings[] <Binding>"; //$NON-NLS-1$
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/DataBindingContextValidationStatusProvidersProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/DataBindingContextValidationStatusProvidersProperty.java
new file mode 100644
index 0000000..239b79b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/DataBindingContextValidationStatusProvidersProperty.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.list.ListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class DataBindingContextValidationStatusProvidersProperty extends
+		ListProperty {
+	public Object getElementType() {
+		return ValidationStatusProvider.class;
+	}
+
+	protected List doGetList(Object source) {
+		return ((DataBindingContext) source).getValidationStatusProviders();
+	}
+
+	protected void doSetList(Object source, List list) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return ((DataBindingContext) source).getValidationStatusProviders();
+	}
+
+	public String toString() {
+		return "Binding#validationStatusProviders[] <ValidationStatusProvider>"; //$NON-NLS-1$
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityMap.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityMap.java
new file mode 100644
index 0000000..4f14675
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityMap.java
@@ -0,0 +1,406 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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
+ *         (through ViewerElementMap.java)
+ *     Matthew Hall - bugs 262269, 303847
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+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;
+
+/**
+ * A {@link Map} whose keys are added, removed and compared by identity. The
+ * keys in the map are compared using <code>==</code> 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.
+ * 
+ * @since 1.2
+ */
+public class IdentityMap implements Map {
+	private Map wrappedMap;
+
+	/**
+	 * Constructs an IdentityMap.
+	 */
+	public IdentityMap() {
+		this.wrappedMap = new HashMap();
+	}
+
+	/**
+	 * Constructs an IdentityMap containing all the entries in the specified
+	 * map.
+	 * 
+	 * @param map
+	 *            the map whose entries are to be added to this map.
+	 */
+	public IdentityMap(Map map) {
+		this();
+		Assert.isNotNull(map);
+		putAll(map);
+	}
+
+	public void clear() {
+		wrappedMap.clear();
+	}
+
+	public boolean containsKey(Object key) {
+		return wrappedMap.containsKey(IdentityWrapper.wrap(key));
+	}
+
+	public boolean containsValue(Object value) {
+		return wrappedMap.containsValue(value);
+	}
+
+	public Set entrySet() {
+		final Set wrappedEntrySet = wrappedMap.entrySet();
+		return new Set() {
+			public boolean add(Object o) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean addAll(Collection c) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void clear() {
+				wrappedEntrySet.clear();
+			}
+
+			public boolean contains(Object o) {
+				for (Iterator iterator = iterator(); iterator.hasNext();)
+					if (iterator.next().equals(o))
+						return true;
+				return false;
+			}
+
+			public boolean containsAll(Collection c) {
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					if (!contains(iterator.next()))
+						return false;
+				return true;
+			}
+
+			public boolean isEmpty() {
+				return wrappedEntrySet.isEmpty();
+			}
+
+			public Iterator iterator() {
+				final Iterator wrappedIterator = wrappedEntrySet.iterator();
+				return new Iterator() {
+					public boolean hasNext() {
+						return wrappedIterator.hasNext();
+					}
+
+					public Object next() {
+						final Map.Entry wrappedEntry = (Map.Entry) wrappedIterator
+								.next();
+						return new Map.Entry() {
+							public Object getKey() {
+								return ((IdentityWrapper) wrappedEntry.getKey())
+										.unwrap();
+							}
+
+							public Object getValue() {
+								return wrappedEntry.getValue();
+							}
+
+							public Object setValue(Object value) {
+								return wrappedEntry.setValue(value);
+							}
+
+							public boolean equals(Object obj) {
+								if (obj == this)
+									return true;
+								if (obj == null || !(obj instanceof Map.Entry))
+									return false;
+								Map.Entry that = (Map.Entry) obj;
+								return this.getKey() == that.getKey()
+										&& Util.equals(this.getValue(), that
+												.getValue());
+							}
+
+							public int hashCode() {
+								return wrappedEntry.hashCode();
+							}
+						};
+					}
+
+					public void remove() {
+						wrappedIterator.remove();
+					}
+				};
+			}
+
+			public boolean remove(Object o) {
+				final Map.Entry unwrappedEntry = (Map.Entry) o;
+				final IdentityWrapper wrappedKey = IdentityWrapper
+						.wrap(unwrappedEntry.getKey());
+				Map.Entry wrappedEntry = new Map.Entry() {
+					public Object getKey() {
+						return wrappedKey;
+					}
+
+					public Object getValue() {
+						return unwrappedEntry.getValue();
+					}
+
+					public Object setValue(Object value) {
+						throw new UnsupportedOperationException();
+					}
+
+					public boolean equals(Object obj) {
+						if (obj == this)
+							return true;
+						if (obj == null || !(obj instanceof Map.Entry))
+							return false;
+						Map.Entry that = (Map.Entry) obj;
+						return Util.equals(wrappedKey, that.getKey())
+								&& Util
+										.equals(this.getValue(), that
+												.getValue());
+					}
+
+					public int hashCode() {
+						return wrappedKey.hashCode()
+								^ (getValue() == null ? 0 : getValue()
+										.hashCode());
+					}
+				};
+				return wrappedEntrySet.remove(wrappedEntry);
+			}
+
+			public boolean removeAll(Collection c) {
+				boolean changed = false;
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					changed |= remove(iterator.next());
+				return changed;
+			}
+
+			public boolean retainAll(Collection c) {
+				boolean 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 = (Object[]) Array.newInstance(a.getClass()
+							.getComponentType(), size);
+				}
+				int i = 0;
+				for (Iterator iterator = iterator(); iterator.hasNext();) {
+					a[i] = iterator.next();
+					i++;
+				}
+				return a;
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this)
+					return true;
+				if (obj == null || !(obj instanceof Set))
+					return false;
+				Set that = (Set) obj;
+				return this.size() == that.size() && containsAll(that);
+			}
+
+			public int hashCode() {
+				return wrappedEntrySet.hashCode();
+			}
+		};
+	}
+
+	public Object get(Object key) {
+		return wrappedMap.get(IdentityWrapper.wrap(key));
+	}
+
+	public boolean isEmpty() {
+		return wrappedMap.isEmpty();
+	}
+
+	public Set keySet() {
+		final Set wrappedKeySet = wrappedMap.keySet();
+		return new Set() {
+			public boolean add(Object o) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean addAll(Collection c) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void clear() {
+				wrappedKeySet.clear();
+			}
+
+			public boolean contains(Object o) {
+				return wrappedKeySet.contains(IdentityWrapper.wrap(o));
+			}
+
+			public boolean containsAll(Collection c) {
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					if (!wrappedKeySet.contains(IdentityWrapper.wrap(iterator
+							.next())))
+						return false;
+				return true;
+			}
+
+			public boolean isEmpty() {
+				return wrappedKeySet.isEmpty();
+			}
+
+			public Iterator iterator() {
+				final Iterator wrappedIterator = wrappedKeySet.iterator();
+				return new Iterator() {
+					public boolean hasNext() {
+						return wrappedIterator.hasNext();
+					}
+
+					public Object next() {
+						return ((IdentityWrapper) wrappedIterator.next())
+								.unwrap();
+					}
+
+					public void remove() {
+						wrappedIterator.remove();
+					}
+				};
+			}
+
+			public boolean remove(Object o) {
+				return wrappedKeySet.remove(IdentityWrapper.wrap(o));
+			}
+
+			public boolean removeAll(Collection c) {
+				boolean changed = false;
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					changed |= wrappedKeySet.remove(IdentityWrapper
+							.wrap(iterator.next()));
+				return changed;
+			}
+
+			public boolean retainAll(Collection c) {
+				boolean 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 (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();
+				IdentityWrapper[] wrappedArray = (IdentityWrapper[]) wrappedKeySet
+						.toArray(new IdentityWrapper[size]);
+				Object[] result = a;
+				if (a.length < size) {
+					result = (Object[]) Array.newInstance(a.getClass()
+							.getComponentType(), size);
+				}
+				for (int i = 0; i < size; i++)
+					result[i] = wrappedArray[i].unwrap();
+				return result;
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this)
+					return true;
+				if (obj == null || !(obj instanceof Set))
+					return false;
+				Set that = (Set) obj;
+				return this.size() == that.size() && containsAll(that);
+			}
+
+			public int hashCode() {
+				return wrappedKeySet.hashCode();
+			}
+		};
+	}
+
+	public Object put(Object key, Object value) {
+		return wrappedMap.put(IdentityWrapper.wrap(key), value);
+	}
+
+	public void putAll(Map other) {
+		for (Iterator iterator = other.entrySet().iterator(); iterator
+				.hasNext();) {
+			Map.Entry entry = (Map.Entry) iterator.next();
+			wrappedMap.put(IdentityWrapper.wrap(entry.getKey()), entry
+					.getValue());
+		}
+	}
+
+	public Object remove(Object key) {
+		return wrappedMap.remove(IdentityWrapper.wrap(key));
+	}
+
+	public int size() {
+		return wrappedMap.size();
+	}
+
+	public Collection values() {
+		return wrappedMap.values();
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null || !(obj instanceof Map))
+			return false;
+		Map that = (Map) obj;
+		return this.entrySet().equals(that.entrySet());
+	}
+
+	public int hashCode() {
+		return wrappedMap.hashCode();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentitySet.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentitySet.java
new file mode 100644
index 0000000..03790cf
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentitySet.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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
+ *         (through ViewerElementSet.java)
+ *     Matthew Hall - bugs 262269, 303847
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A {@link Set} of elements where elements are added, removed and compared by
+ * identity. Elements of the set are compared using <code>==</code> 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.
+ * 
+ * @since 1.2
+ */
+public class IdentitySet implements Set {
+	private final Set wrappedSet;
+
+	/**
+	 * Constructs an IdentitySet.
+	 */
+	public IdentitySet() {
+		this.wrappedSet = new HashSet();
+	}
+
+	/**
+	 * Constructs an IdentitySet containing all the unique instances in the
+	 * specified collection.
+	 * 
+	 * @param collection
+	 *            the collection whose elements are to be added to this set.
+	 */
+	public IdentitySet(Collection collection) {
+		this();
+		addAll(collection);
+	}
+
+	public boolean add(Object o) {
+		return wrappedSet.add(IdentityWrapper.wrap(o));
+	}
+
+	public boolean addAll(Collection c) {
+		boolean changed = false;
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			changed |= wrappedSet.add(IdentityWrapper.wrap(iterator.next()));
+		return changed;
+	}
+
+	public void clear() {
+		wrappedSet.clear();
+	}
+
+	public boolean contains(Object o) {
+		return wrappedSet.contains(IdentityWrapper.wrap(o));
+	}
+
+	public boolean containsAll(Collection c) {
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			if (!wrappedSet.contains(IdentityWrapper.wrap(iterator.next())))
+				return false;
+		return true;
+	}
+
+	public boolean isEmpty() {
+		return wrappedSet.isEmpty();
+	}
+
+	public Iterator iterator() {
+		final Iterator wrappedIterator = wrappedSet.iterator();
+		return new Iterator() {
+			public boolean hasNext() {
+				return wrappedIterator.hasNext();
+			}
+
+			public Object next() {
+				return ((IdentityWrapper) wrappedIterator.next()).unwrap();
+			}
+
+			public void remove() {
+				wrappedIterator.remove();
+			}
+		};
+	}
+
+	public boolean remove(Object o) {
+		return wrappedSet.remove(IdentityWrapper.wrap(o));
+	}
+
+	public boolean removeAll(Collection c) {
+		boolean changed = false;
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			changed |= remove(iterator.next());
+		return changed;
+	}
+
+	public boolean 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.
+		boolean 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 (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();
+		IdentityWrapper[] wrappedArray = (IdentityWrapper[]) wrappedSet
+				.toArray(new IdentityWrapper[size]);
+		Object[] result = a;
+		if (a.length < size) {
+			result = (Object[]) Array.newInstance(a.getClass()
+					.getComponentType(), size);
+		}
+		for (int i = 0; i < size; i++)
+			result[i] = wrappedArray[i].unwrap();
+		return result;
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (!(obj instanceof Set))
+			return false;
+		Set that = (Set) obj;
+		return size() == that.size() && containsAll(that);
+	}
+
+	public int hashCode() {
+		int hash = 0;
+		for (Iterator iterator = iterator(); iterator.hasNext();) {
+			Object element = iterator.next();
+			hash += element == null ? 0 : element.hashCode();
+		}
+		return hash;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityWrapper.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityWrapper.java
new file mode 100644
index 0000000..c3f43de
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityWrapper.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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
+ *     Matthew Hall - bug 303847
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+/**
+ * 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 {
+
+	private static final IdentityWrapper NULL_WRAPPER = new IdentityWrapper(
+			null);
+
+	/**
+	 * @param obj
+	 *            the object to wrap
+	 * @return an IdentityWrapper wrapping the specified object
+	 */
+	public static IdentityWrapper wrap(Object obj) {
+		return obj == null ? NULL_WRAPPER : new IdentityWrapper(obj);
+	}
+
+	final Object o;
+
+	/**
+	 * @param o
+	 */
+	private IdentityWrapper(Object o) {
+		this.o = o;
+	}
+
+	/**
+	 * @return the unwrapped object
+	 */
+	public Object unwrap() {
+		return o;
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == null || obj.getClass() != IdentityWrapper.class) {
+			return false;
+		}
+		return o == ((IdentityWrapper) obj).o;
+	}
+
+	public int hashCode() {
+		return System.identityHashCode(o);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Pair.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Pair.java
new file mode 100644
index 0000000..aebed88
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Pair.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding;
+
+/**
+ * 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 Pair(Object a, Object b) {
+		this.a = a;
+		this.b = b;
+	}
+
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((a == null) ? 0 : a.hashCode());
+		result = prime * result + ((b == null) ? 0 : b.hashCode());
+		return result;
+	}
+
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Pair other = (Pair) obj;
+		if (a == null) {
+			if (other.a != null)
+				return false;
+		} else if (!a.equals(other.a))
+			return false;
+		if (b == null) {
+			if (other.b != null)
+				return false;
+		} else if (!b.equals(other.b))
+			return false;
+		return true;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Queue.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Queue.java
new file mode 100644
index 0000000..cb4a507
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Queue.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding;
+
+/**
+ * Created to avoid a dependency on java.util.LinkedList, see bug 205224.
+ * 
+ * @since 1.1
+ * 
+ */
+public class Queue {
+
+	static class Entry {
+		Object object;
+
+		Entry(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 != 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 == null) {
+			throw new IllegalStateException();
+		}
+		first = oldFirst.next;
+		if (first == 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 boolean isEmpty() {
+		return first == null;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Util.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Util.java
new file mode 100644
index 0000000..d027177
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Util.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+/**
+ * @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 boolean equals(final Object left, final Object right) {
+		return left == null ? right == null : ((right != null) && left
+				.equals(right));
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusMap.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusMap.java
new file mode 100644
index 0000000..a063f18
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusMap.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * 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 226289
+ *******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+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 extends ObservableMap {
+
+	private boolean isDirty = true;
+
+	private final WritableList bindings;
+
+	private List dependencies = new ArrayList();
+
+	private IChangeListener markDirtyChangeListener = new IChangeListener() {
+		public void handleChange(ChangeEvent event) {
+			markDirty();
+		}
+	};
+
+	/**
+	 * @param realm
+	 * @param bindings
+	 */
+	public ValidationStatusMap(Realm realm, WritableList bindings) {
+		super(realm, new HashMap());
+		this.bindings = bindings;
+		bindings.addChangeListener(markDirtyChangeListener);
+	}
+
+	public Object getKeyType() {
+		return Binding.class;
+	}
+
+	public Object getValueType() {
+		return IStatus.class;
+	}
+
+	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 MapDiff() {
+			private MapDiff cachedDiff = null;
+
+			private void ensureCached() {
+				if (cachedDiff == 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 = (Binding) it.next();
+				IObservableValue validationError = binding
+						.getValidationStatus();
+				dependencies.add(validationError);
+				validationError.addChangeListener(markDirtyChangeListener);
+				IStatus validationStatusValue = (IStatus) validationError
+						.getValue();
+				newContents.put(binding, validationStatusValue);
+			}
+			wrappedMap.putAll(newContents);
+			isDirty = false;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.databinding.observable.list.ObservableList#dispose()
+	 */
+	public synchronized void dispose() {
+		bindings.removeChangeListener(markDirtyChangeListener);
+		removeElementChangeListener();
+		super.dispose();
+	}
+
+	private void removeElementChangeListener() {
+		for (Iterator it = dependencies.iterator(); it.hasNext();) {
+			IObservableValue observableValue = (IObservableValue) it.next();
+			observableValue.removeChangeListener(markDirtyChangeListener);
+		}
+	}
+	
+	public synchronized void addChangeListener(IChangeListener listener) {
+		// this ensures that the next change will be seen by the new listener.
+		recompute();
+		super.addChangeListener(listener);
+	}
+	
+	public synchronized void addMapChangeListener(IMapChangeListener listener) {
+		// this ensures that the next change will be seen by the new listener.
+		recompute();
+		super.addMapChangeListener(listener);
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderModelsProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderModelsProperty.java
new file mode 100644
index 0000000..845b94c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderModelsProperty.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.list.ListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ValidationStatusProviderModelsProperty extends ListProperty {
+	public Object getElementType() {
+		return IObservable.class;
+	}
+
+	protected List doGetList(Object source) {
+		return ((ValidationStatusProvider) source).getModels();
+	}
+
+	protected void doSetList(Object source, List list) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return ((ValidationStatusProvider) source).getModels();
+	}
+
+	public String toString() {
+		return "ValidationStatusProvider#models[] <IObservable>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderTargetsProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderTargetsProperty.java
new file mode 100644
index 0000000..10a0971
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderTargetsProperty.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.list.ListProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ValidationStatusProviderTargetsProperty extends ListProperty {
+	public Object getElementType() {
+		return IObservable.class;
+	}
+
+	protected List doGetList(Object source) {
+		return ((ValidationStatusProvider) source).getTargets();
+	}
+
+	protected void doSetList(Object source, List list) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		throw new UnsupportedOperationException(toString() + " is unmodifiable"); //$NON-NLS-1$
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return ((ValidationStatusProvider) source).getTargets();
+	}
+
+	public String toString() {
+		return "ValidationStatusProvider#targets[] <IObservable>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderValidationStatusProperty.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderValidationStatusProperty.java
new file mode 100644
index 0000000..7075357
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusProviderValidationStatusProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 263709)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class ValidationStatusProviderValidationStatusProperty extends
+		SimpleValueProperty {
+	public Object getValueType() {
+		return IObservableValue.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((ValidationStatusProvider) source).getValidationStatus();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		// no setter API
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		// no listener API
+		return null;
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+
+	public String toString() {
+		return "ValidationStatusProvider#validationStatus <IObservableValue>"; //$NON-NLS-1$
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/CharacterToStringConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/CharacterToStringConverter.java
new file mode 100644
index 0000000..e81091e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/CharacterToStringConverter.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.Converter;
+
+/**
+ * Converts a character to a string.
+ */
+public class CharacterToStringConverter extends Converter {
+	private final boolean primitive;
+
+	/**
+	 * @param primitive
+	 */
+	private CharacterToStringConverter(boolean primitive) {
+		super(primitive ? Character.TYPE : Character.class, String.class);
+		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 == null) {
+			if (primitive)
+				throw new IllegalArgumentException(
+						"'fromObject' is null. Cannot convert to primitive char."); //$NON-NLS-1$
+			return ""; //$NON-NLS-1$
+		}
+
+		if (!(fromObject instanceof Character)) {
+			throw new IllegalArgumentException(
+					"'fromObject' is not of type [Character]."); //$NON-NLS-1$
+		}
+
+		return String.valueOf(((Character) fromObject).charValue());
+	}
+
+	/**
+	 * @param primitive
+	 * @return converter
+	 */
+	public static CharacterToStringConverter fromCharacter(boolean primitive) {
+		return new CharacterToStringConverter(primitive);
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java
new file mode 100755
index 0000000..424451f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (C) 2005, 2009 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
+ *     Matthew Hall - bug 121110
+ ******************************************************************************/
+package org.eclipse.core.internal.databinding.conversion;
+
+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.getString(BindingMessages.DATE_FORMAT_DATE_TIME)),
+			new SimpleDateFormat(BindingMessages.getString(BindingMessages.DATEFORMAT_TIME)),
+			DateFormat.getDateTimeInstance(DATE_FORMAT, DateFormat.SHORT),
+			DateFormat.getDateInstance(DATE_FORMAT),
+			DateFormat.getTimeInstance(DateFormat.SHORT),
+            DateFormat.getDateTimeInstance(DATE_FORMAT,DateFormat.MEDIUM),
+            DateFormat.getTimeInstance(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!=null) {
+				return parsed;
+			}
+		}
+		return null;
+	}
+
+	protected Date parse(String str,int formatterIdx) {
+		if(formatterIdx>=0) {
+				ParsePosition pos=new ParsePosition(0);
+				if (str == null) {
+					return null;
+				}
+				Date date=formatters[formatterIdx].parse(str,pos);
+				if(pos.getErrorIndex()!=-1||pos.getIndex()!=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 (date == null)
+			return null;
+		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];
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java
new file mode 100755
index 0000000..db4b021
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005, 2007 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
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends DateConversionSupport implements IConverter {	
+	public Object convert(Object source) {
+		if (source != null)
+			return format((Date)source);
+		return ""; //$NON-NLS-1$
+	}
+
+	public Object getFromType() {
+		return Date.class;
+	}
+
+	public Object getToType() {
+		return String.class;
+	}	
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.java
new file mode 100644
index 0000000..bd7ec66
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2005, 2007 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)
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.BindingException;
+import org.eclipse.core.databinding.conversion.IConverter;
+
+/**
+ * TheIdentityConverter. Returns the source value (the identity function).
+ */
+public class IdentityConverter implements IConverter {
+
+	private Class fromType;
+
+	private Class toType;
+
+	/**
+	 * @param type
+	 */
+	public IdentityConverter(Class type) {
+		this.fromType = type;
+		this.toType = type;
+	}
+
+	/**
+	 * @param fromType
+	 * @param toType
+	 */
+	public IdentityConverter(Class fromType, Class toType) {
+		this.fromType = fromType;
+		this.toType = toType;
+	}
+
+	private Class[][] primitiveMap = new Class[][] {
+			{ Integer.TYPE, Integer.class }, { Short.TYPE, Short.class },
+			{ Long.TYPE, Long.class }, { Double.TYPE, Double.class },
+			{ Byte.TYPE, Byte.class }, { Float.TYPE, Float.class },
+			{ Boolean.TYPE, Boolean.class },
+			{ Character.TYPE, Character.class } };
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object)
+	 */
+	public Object convert(Object source) {
+		if (toType.isPrimitive()) {
+			if (source == null) {
+				throw new BindingException("Cannot convert null to a primitive"); //$NON-NLS-1$
+			}
+		}
+		if (source != null) {
+			Class 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 boolean isPrimitiveTypeMatchedWithBoxed(Class sourceClass,
+			Class toClass) {
+		for (int i = 0; i < primitiveMap.length; i++) {
+			if (toClass.equals(primitiveMap[i][0])
+					&& sourceClass.equals(primitiveMap[i][1])) {
+				return true;
+			}
+			if (sourceClass.equals(primitiveMap[i][0])
+					&& toClass.equals(primitiveMap[i][1])) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public Object getFromType() {
+		return fromType;
+	}
+
+	public Object getToType() {
+		return toType;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IntegerToStringConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IntegerToStringConverter.java
new file mode 100644
index 0000000..c8b177c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IntegerToStringConverter.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends Converter {
+	private final boolean primitive;
+	private final NumberFormat numberFormat;
+	private final Class boxedType;
+
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 * @param boxedType
+	 */
+	private IntegerToStringConverter(NumberFormat numberFormat, Class fromType,
+			Class boxedType) {
+		super(fromType, String.class);
+		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 == 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(((Number) fromObject).longValue());
+	}
+
+	/**
+	 * @param primitive
+	 * @return converter
+	 */
+	public static IntegerToStringConverter fromShort(boolean primitive) {
+		return fromShort(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return converter
+	 */
+	public static IntegerToStringConverter fromShort(NumberFormat numberFormat,
+			boolean primitive) {
+		return new IntegerToStringConverter(numberFormat,
+				primitive ? Short.TYPE : Short.class, Short.class);
+	}
+
+	/**
+	 * @param primitive
+	 * @return converter
+	 */
+	public static IntegerToStringConverter fromByte(boolean primitive) {
+		return fromByte(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return converter
+	 */
+	public static IntegerToStringConverter fromByte(NumberFormat numberFormat,
+			boolean primitive) {
+		return new IntegerToStringConverter(numberFormat, primitive ? Byte.TYPE
+				: Byte.class, Byte.class);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigDecimalConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigDecimalConverter.java
new file mode 100644
index 0000000..2d3e38a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigDecimalConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 */
+	public NumberToBigDecimalConverter(NumberFormat numberFormat, Class fromType) {		
+		super(numberFormat, fromType, BigDecimal.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (number instanceof BigInteger) {
+			return new BigDecimal((BigInteger) number);
+		}
+		
+		return new BigDecimal(number.doubleValue());
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigIntegerConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigIntegerConverter.java
new file mode 100644
index 0000000..23f4261
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigIntegerConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 */
+	public NumberToBigIntegerConverter(NumberFormat numberFormat, Class fromType) {
+		super(numberFormat, fromType, BigInteger.class);
+	}
+
+	/* (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 (number instanceof BigDecimal) {
+			return (BigDecimal) number;
+		}
+		
+		return new BigDecimal(number.doubleValue());
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToByteConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToByteConverter.java
new file mode 100644
index 0000000..88bf643
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToByteConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+	/**
+	 * @param numberFormat 
+	 * @param fromType
+	 * @param primitive
+	 */
+	public NumberToByteConverter(NumberFormat numberFormat, Class fromType,
+			boolean primitive) {
+		super(numberFormat, fromType, (primitive) ? Byte.TYPE : Byte.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (StringToNumberParser.inByteRange(number)) {
+			return new Byte(number.byteValue());
+		}
+		
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToDoubleConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToDoubleConverter.java
new file mode 100644
index 0000000..4e30390
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToDoubleConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 * @param primitive
+	 */
+	public NumberToDoubleConverter(NumberFormat numberFormat, Class fromType,
+			boolean primitive) {
+		super(numberFormat, fromType, (primitive) ? Double.TYPE : Double.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (StringToNumberParser.inDoubleRange(number)) {
+			return new Double(number.doubleValue());
+		}
+		
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToFloatConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToFloatConverter.java
new file mode 100644
index 0000000..64745a5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToFloatConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 * @param primitive
+	 */
+	public NumberToFloatConverter(NumberFormat numberFormat, Class fromType,
+			boolean primitive) {
+		super(numberFormat, fromType, (primitive) ? Float.TYPE : Float.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (StringToNumberParser.inFloatRange(number)) {
+			return new Float(number.floatValue());
+		}
+		
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToIntegerConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToIntegerConverter.java
new file mode 100644
index 0000000..fdcf23a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToIntegerConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter implements
+		IConverter {
+
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 * @param primitive
+	 */
+	public NumberToIntegerConverter(NumberFormat numberFormat,
+			Class fromType, boolean primitive) {
+		super(numberFormat, fromType, (primitive) ? Integer.TYPE : Integer.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (StringToNumberParser.inIntegerRange(number)) {
+			return new Integer(number.intValue());
+		}
+		
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToLongConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToLongConverter.java
new file mode 100644
index 0000000..6addb0e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToLongConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 * @param primitive
+	 */
+	public NumberToLongConverter(NumberFormat numberFormat, Class fromType,
+			boolean primitive) {
+		super(numberFormat, fromType, (primitive) ? Long.TYPE : Long.class);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (StringToNumberParser.inLongRange(number)) {
+			return new Long(number.longValue());
+		}
+		
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToNumberConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToNumberConverter.java
new file mode 100644
index 0000000..89f763b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToNumberConverter.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends Converter {
+	private NumberFormat numberFormat;
+
+	private boolean primitive;
+
+	private String outOfRangeMessage;
+
+	protected NumberToNumberConverter(NumberFormat numberFormat,
+			Class fromType, Class 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 == null) {
+			if (primitive) {
+				throw new IllegalArgumentException(
+						"Parameter 'fromObject' cannot be null."); //$NON-NLS-1$	
+			}
+
+			return null;
+		}
+
+		if (!(fromObject instanceof Number)) {
+			throw new IllegalArgumentException(
+					"Parameter 'fromObject' must be of type Number."); //$NON-NLS-1$
+		}
+
+		Number number = (Number) fromObject;
+		Number result = doConvert(number);
+
+		if (result != null) {
+			return result;
+		}
+
+		synchronized (this) {
+			if (outOfRangeMessage == null) {
+				outOfRangeMessage = StringToNumberParser
+						.createOutOfRangeMessage(new Short(Short.MIN_VALUE),
+								new Short(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;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToShortConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToShortConverter.java
new file mode 100644
index 0000000..badb8c0
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToShortConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberToNumberConverter {
+	/**
+	 * @param numberFormat
+	 * @param fromType
+	 * @param primitive
+	 */
+	public NumberToShortConverter(NumberFormat numberFormat, Class fromType,
+			boolean primitive) {
+
+		super(numberFormat, fromType, (primitive) ? Short.TYPE : Short.class);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number)
+	 */
+	protected Number doConvert(Number number) {
+		if (StringToNumberParser.inShortRange(number)) {
+			return new Short(number.shortValue());
+		}
+
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.java
new file mode 100644
index 0000000..a7e39fa
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005, 2007 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
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+
+/**
+ * Converts any object to a string by calling its toString() method.
+ */
+public class ObjectToStringConverter implements IConverter {
+	private final Class fromClass;
+
+	/**
+	 * 
+	 */
+	public ObjectToStringConverter() {
+		this(Object.class);
+	}
+
+	/**
+	 * @param fromClass
+	 */
+	public ObjectToStringConverter(Class fromClass) {
+		this.fromClass = fromClass;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object)
+	 */
+	public Object convert(Object source) {
+		if (source == null) {
+			return ""; //$NON-NLS-1$
+		}
+		return source.toString();
+	}
+
+	public Object getFromType() {
+		return fromClass;
+	}
+
+	public Object getToType() {
+		return String.class;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StatusToStringConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StatusToStringConverter.java
new file mode 100644
index 0000000..72d7716
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StatusToStringConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends Converter implements IConverter {
+	/**
+	 * Constructs a new instance.
+	 */
+	public StatusToStringConverter() {
+		super(IStatus.class, String.class);
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object)
+	 */
+	public Object convert(Object fromObject) {
+		if (fromObject == null) {
+			throw new IllegalArgumentException("Parameter 'fromObject' was null."); //$NON-NLS-1$
+		}
+		
+		IStatus status = (IStatus) fromObject;
+		return status.getMessage();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanConverter.java
new file mode 100755
index 0000000..001ca2f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanConverter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2005, 2007 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
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+/**
+ * StringToBooleanConverter.
+ */
+public class StringToBooleanConverter extends StringToBooleanPrimitiveConverter {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object)
+	 */
+	public Object convert(Object source) {
+		String sourceString = (String) source;
+		if ("".equals(sourceString.trim())) { //$NON-NLS-1$
+			return null;
+		}
+		return super.convert(source);
+	}
+
+	public Object getToType() {
+		return Boolean.class;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java
new file mode 100644
index 0000000..1642540
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005, 2008 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
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+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 implements IConverter {
+	private static final String[] trueValues;
+
+	private static final String[] falseValues;
+
+	static {
+		String delimiter = BindingMessages.getString(BindingMessages.VALUE_DELIMITER);
+		String values = BindingMessages.getString(BindingMessages.TRUE_STRING_VALUES);
+		trueValues = valuesToSortedArray(delimiter, values);
+
+		values = BindingMessages.getString(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 = (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 = (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 boolean value"); //$NON-NLS-1$
+	}
+
+	public Object getFromType() {
+		return String.class;
+	}
+
+	public Object getToType() {
+		return Boolean.TYPE;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToByteConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToByteConverter.java
new file mode 100755
index 0000000..4bb1084
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToByteConverter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005, 2007 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
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberFormatConverter {	
+	private String outOfRangeMessage;
+	private NumberFormat numberFormat;
+	private boolean primitive;
+	
+	/**
+	 * @param numberFormat
+	 * @param toType
+	 */
+	private StringToByteConverter(NumberFormat numberFormat, Class toType) {
+		super(String.class, toType, numberFormat);
+		primitive = toType.isPrimitive();
+		this.numberFormat = numberFormat;
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return converter
+	 */
+	public static StringToByteConverter toByte(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToByteConverter(numberFormat, (primitive) ? Byte.TYPE : Byte.class);
+	}
+
+	/**
+	 * @param primitive
+	 * @return converter
+	 */
+	public static StringToByteConverter toByte(boolean 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() != 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((String) fromObject, result
+							.getPosition()));
+		} else if (result.getNumber() == 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 == null) {
+				outOfRangeMessage = StringToNumberParser
+				.createOutOfRangeMessage(new Byte(Byte.MIN_VALUE), new Byte(Byte.MAX_VALUE), numberFormat);
+			}
+						
+			throw new IllegalArgumentException(outOfRangeMessage);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.java
new file mode 100644
index 0000000..0a8ef55
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2005, 2007 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)
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+
+/**
+ * StringToCharacterConverter.
+ */
+public class StringToCharacterConverter implements IConverter {
+
+	private final boolean primitiveTarget;
+
+	/**
+	 * 
+	 * @param primitiveTarget
+	 */
+	public StringToCharacterConverter(boolean primitiveTarget) {
+		this.primitiveTarget = primitiveTarget;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object)
+	 */
+	public Object convert(Object source) {
+		if (source != null && !(source instanceof String))
+			throw new IllegalArgumentException(
+					"String2Character: Expected type String, got type [" + source.getClass().getName() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+
+		String s = (String) source;
+		if (source == 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.class;
+	}
+
+	public Object getToType() {
+		return primitiveTarget ? Character.TYPE : Character.class;
+	}
+
+	/**
+	 * @param primitive
+	 * @return converter
+	 */
+	public static StringToCharacterConverter toCharacter(boolean primitive) {
+		return new StringToCharacterConverter(primitive);
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java
new file mode 100644
index 0000000..eb5b9f1
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2005, 2007 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
+ */
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends DateConversionSupport implements IConverter {
+	public Object convert(Object source) {
+		return parse(source.toString());
+	}
+
+	public Object getFromType() {
+		return String.class;
+	}
+
+	public Object getToType() {
+		return Date.class;
+	}	
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java
new file mode 100644
index 0000000..effd816
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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,
+			boolean primitive) {
+		if (!(value instanceof String)) {
+			throw new IllegalArgumentException(
+					"Value to convert is not a String"); //$NON-NLS-1$
+		}
+
+		String source = (String) value;
+		ParseResult result = new ParseResult();
+		if (!primitive && source.trim().length() == 0) {
+			return result;
+		}
+
+		synchronized (numberFormat) {
+			ParsePosition position = new ParsePosition(0);
+			Number parseResult = null;
+			parseResult = numberFormat.parse(source, position);
+
+			if (position.getIndex() != 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, new Object[] {
+							value, new Integer(errorIndex + 1),
+							new Character(value.charAt(errorIndex)) });
+		}
+		return BindingMessages.formatString(
+				BindingMessages.VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER,
+				new Object[] { 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", new 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 boolean 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 boolean checkInteger(Number number, int bitLength) {
+		BigInteger bigInteger = null;
+
+		if (number instanceof Integer || number instanceof Long) {
+			bigInteger = BigInteger.valueOf(number.longValue());
+		} else if (number instanceof Float || number instanceof Double) {
+			double doubleValue = number.doubleValue();
+			if (!Double.isNaN(doubleValue)
+					&& doubleValue != Double.NEGATIVE_INFINITY
+					&& doubleValue != Double.POSITIVE_INFINITY) {
+				bigInteger = new BigDecimal(doubleValue).toBigInteger();
+			} else {
+				return false;
+			}
+		} else if (number instanceof BigInteger) {
+			bigInteger = (BigInteger) number;
+		} else if (number instanceof BigDecimal) {
+			bigInteger = ((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 != 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 boolean 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 boolean inFloatRange(Number number) {
+		return checkDecimal(number, FLOAT_MIN_BIG_DECIMAL,
+				FLOAT_MAX_BIG_DECIMAL);
+	}
+
+	private static boolean checkDecimal(Number number, BigDecimal min,
+			BigDecimal max) {
+		BigDecimal bigDecimal = null;
+		if (number instanceof Integer || number instanceof Long) {
+			bigDecimal = new BigDecimal(number.doubleValue());
+		} else if (number instanceof Float || number instanceof Double) {
+			double doubleValue = number.doubleValue();
+
+			if (!Double.isNaN(doubleValue)
+					&& doubleValue != Double.NEGATIVE_INFINITY
+					&& doubleValue != Double.POSITIVE_INFINITY) {
+				bigDecimal = new BigDecimal(doubleValue);
+			} else {
+				return false;
+			}
+		} else if (number instanceof BigInteger) {
+			bigDecimal = new BigDecimal((BigInteger) number);
+		} else if (number instanceof BigDecimal) {
+			bigDecimal = (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.
+			 */
+			// if this is ever taken out, take care to un-comment the throw
+			// clause and the if condition below, they were commented because
+			// the
+			// compiler complained about dead code..
+			bigDecimal = new BigDecimal(number.doubleValue());
+		}
+
+		/* if (bigDecimal != 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 boolean 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 boolean 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 boolean inByteRange(Number number) {
+		return checkInteger(number, 7);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToShortConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToShortConverter.java
new file mode 100755
index 0000000..3c05a5c
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToShortConverter.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.conversion;
+
+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 extends NumberFormatConverter {
+	private final NumberFormat numberFormat;
+	private final boolean primitive;
+	
+	private String outOfRangeMessage;
+
+	/**
+	 * Constructs a new instance.
+	 */
+	private StringToShortConverter(NumberFormat numberFormat, Class toType) {
+		super(String.class, 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() != 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((String) fromObject, result
+							.getPosition()));
+		} else if (result.getNumber() == 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 == null) {
+				outOfRangeMessage = StringToNumberParser
+				.createOutOfRangeMessage(new Short(Short.MIN_VALUE), new Short(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(boolean primitive) {
+		return toShort(NumberFormat.getIntegerInstance(), primitive);
+	}
+
+	/**
+	 * @param numberFormat
+	 * @param primitive
+	 * @return to Short converter with the provided numberFormat
+	 */
+	public static StringToShortConverter toShort(NumberFormat numberFormat,
+			boolean primitive) {
+		return new StringToShortConverter(numberFormat,
+				(primitive) ? Short.TYPE : Short.class);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties
new file mode 100644
index 0000000..7540ebc
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties
@@ -0,0 +1,56 @@
+###############################################################################
+# Copyright (c) 2000, 2008 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+# NLS file for JFace Data Binding
+###############################################################################
+## Uneeded value
+#
+#Yes=Yes
+#yes=yes
+#No=No
+#no=no
+#True=True
+#true=true
+#False=False
+#false=false
+#and=and
+#or=or
+#Validate_BooleanHelp=Please type "Yes", "No", "True", or "False"
+#
+#Validate_RangeStart=Please enter a number between
+#Validate_Like=Please enter a number like
+#
+###############
+
+IndexOutOfRange=Index out of Range.
+MultipleProblems=Multiple Problems.
+
+ValueBinding_ErrorWhileSettingValue=An error occurred while setting the value.
+DateFormat_DateTime=dd.MM.yyyy HH:mm:ss.SSS Z
+DateFormat_Time=HH:mm:ss.SSS
+
+#ValueDelimiter should be used to separate multiple values that are stored in one key
+ValueDelimiter=,
+
+#Values must be separated by ValueDelimiter
+TrueStringValues=yes,true
+FalseStringValues=no,false
+
+Validate_NumberOutOfRangeError=Please enter a value between [{0}] and [{1}] and with a similar format.
+Validate_NumberParseError=Invalid character for value [{0}] at position [{1}] character [{2}].
+Validate_NumberParseErrorNoCharacter=Missing character for value [{0}] at position [{1}].
+
+Validate_ConversionToPrimitive="Null object values can not be converted to primitives."
+Validate_ConversionFromClassToPrimitive="Wrong object type to convert to primitive."
+
+Validate_NoChangeAllowedHelp=Changes are not allowed in this field
+Validate_CharacterHelp=Please type a character
+
+Examples=Examples
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java
new file mode 100644
index 0000000..3cf8f0b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 implements IValidator {
+	private final NumberFormatConverter converter;
+	private final boolean 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 AbstractStringToNumberValidator(NumberFormatConverter converter,
+			Number min, Number max) {
+		this.converter = converter;
+		this.min = min;
+		this.max = max;
+
+		if (converter.getToType() instanceof Class) {
+			Class clazz = (Class) 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() != null) {
+			if (!isInRange(result.getNumber())) {
+				if (outOfRangeMessage == null) {
+					outOfRangeMessage = StringToNumberParser
+							.createOutOfRangeMessage(min, max, converter
+									.getNumberFormat());
+				}
+
+				return ValidationStatus.error(outOfRangeMessage);
+			}
+		} else if (result.getPosition() != null) {
+			String parseErrorMessage = StringToNumberParser.createParseErrorMessage(
+					(String) value, result.getPosition());
+
+			return ValidationStatus.error(parseErrorMessage);
+		}
+
+		return Status.OK_STATUS;
+	}
+
+	/**
+	 * Invoked by {@link #validate(Object)} when the range is to be validated.
+	 * 
+	 * @param number
+	 * @return <code>true</code> if in range
+	 */
+	protected abstract boolean isInRange(Number number);
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberFormatConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberFormatConverter.java
new file mode 100644
index 0000000..f849329
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberFormatConverter.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends Converter {
+	private final NumberFormat numberFormat;
+	
+	/**
+	 * @param fromType
+	 * @param toType
+	 * @param numberFormat 
+	 */
+	public NumberFormatConverter(Object fromType, Object toType, NumberFormat numberFormat) {
+		super(fromType, toType);
+		
+		this.numberFormat = numberFormat;
+	}
+
+	/**
+	 * @return number format
+	 */
+	/*package */ NumberFormat getNumberFormat() {
+		return numberFormat;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToByteValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToByteValidator.java
new file mode 100644
index 0000000..cdb16f5
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToByteValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	private static final Byte MAX = new Byte(Byte.MAX_VALUE);
+	private static final Byte MIN = new Byte(Byte.MIN_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public NumberToByteValidator(NumberToByteConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return StringToNumberParser.inByteRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToDoubleValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToDoubleValidator.java
new file mode 100644
index 0000000..4206520
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToDoubleValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	private static final Double MIN = new Double(Double.MIN_VALUE);
+	private static final Double MAX = new Double(Double.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public NumberToDoubleValidator(NumberToDoubleConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return StringToNumberParser.inDoubleRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToFloatValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToFloatValidator.java
new file mode 100644
index 0000000..4fe3b8e
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToFloatValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	private static final Float MIN = new Float(Float.MIN_VALUE);
+	private static final Float MAX = new Float(Float.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public NumberToFloatValidator(NumberToFloatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return StringToNumberParser.inFloatRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToIntegerValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToIntegerValidator.java
new file mode 100644
index 0000000..fe78bce
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToIntegerValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	private static final Integer MIN = new Integer(Integer.MIN_VALUE);
+	private static final Integer MAX = new Integer(Integer.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public NumberToIntegerValidator(NumberToIntegerConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return StringToNumberParser.inIntegerRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToLongValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToLongValidator.java
new file mode 100644
index 0000000..2c2fdb8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToLongValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	private static final Long MIN = new Long(Long.MIN_VALUE);
+	private static final Long MAX = new Long(Long.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public NumberToLongValidator(NumberToLongConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return StringToNumberParser.inLongRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToNumberValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToNumberValidator.java
new file mode 100644
index 0000000..957e75b
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToNumberValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 implements IValidator {
+	private final NumberToNumberConverter converter;
+
+	private final Number min;
+
+	private final Number max;
+
+	private String outOfRangeMessage;
+
+	private final boolean primitive;
+
+	/**
+	 * @param converter
+	 * @param min
+	 *            can be <code>null</code>
+	 * @param max
+	 *            can be <code>null</code>
+	 */
+	protected NumberToNumberValidator(NumberToNumberConverter converter,
+			Number min, Number max) {
+		this.converter = converter;
+		this.min = min;
+		this.max = max;
+
+		primitive = ((Class) converter.getToType()).isPrimitive();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object)
+	 */
+	public final IStatus validate(Object value) {
+		if (value == null) {
+			if (primitive) {
+				throw new IllegalArgumentException(
+						"Parameter 'value' cannot be null."); //$NON-NLS-1$
+			}
+
+			return Status.OK_STATUS;
+		}
+
+		if (!(value instanceof Number)) {
+			throw new IllegalArgumentException(
+					"Parameter 'value' is not of type Number."); //$NON-NLS-1$
+		}
+
+		Number number = (Number) value;
+		if (inRange(number)) {
+			return Status.OK_STATUS;
+		}
+
+		synchronized (this) {
+			if (outOfRangeMessage == null && min != null && max != 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 boolean inRange(Number number);
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToShortValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToShortValidator.java
new file mode 100644
index 0000000..c7fa8d8
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToShortValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	private static final Short MIN = new Short(Short.MIN_VALUE);
+	private static final Short MAX = new Short(Short.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public NumberToShortValidator(NumberToShortConverter converter) {
+		super(converter, MIN, MAX);
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return StringToNumberParser.inShortRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToUnboundedNumberValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToUnboundedNumberValidator.java
new file mode 100644
index 0000000..7919c5a
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToUnboundedNumberValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends NumberToNumberValidator {
+	/**
+	 * @param converter
+	 */
+	public NumberToUnboundedNumberValidator(NumberToNumberConverter converter) {
+		super(converter, null, null);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean inRange(Number number) {
+		return true;
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ObjectToPrimitiveValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ObjectToPrimitiveValidator.java
new file mode 100755
index 0000000..67b46f9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ObjectToPrimitiveValidator.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 implements IValidator {
+
+	private Class toType;
+
+	private Class[][] primitiveMap = new Class[][] {
+			{ Integer.TYPE, Integer.class }, { Short.TYPE, Short.class },
+			{ Long.TYPE, Long.class }, { Double.TYPE, Double.class },
+			{ Byte.TYPE, Byte.class }, { Float.TYPE, Float.class },
+			{ Boolean.TYPE, Boolean.class },
+			{ Character.TYPE, Character.class } };
+
+	/**
+	 * @param toType
+	 */
+	public ObjectToPrimitiveValidator(Class toType) {
+		this.toType = toType;
+	}
+
+	protected Class getToType() {
+		return this.toType;
+	}
+
+	public IStatus validate(Object value) {
+		return doValidate(value);
+	}
+
+	private IStatus doValidate(Object value) {
+		if (value != null) {
+			if (!mapContainsValues(toType, value.getClass())) {
+				return ValidationStatus.error(getClassHint());
+			}
+			return Status.OK_STATUS;
+		}
+		return ValidationStatus.error(getNullHint());
+	}
+
+	private boolean mapContainsValues(Class toType, Class 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.getString(BindingMessages.VALIDATE_CONVERSION_TO_PRIMITIVE);
+	}
+
+	/**
+	 * @return a hint string
+	 */
+	public String getClassHint() {
+		return BindingMessages
+				.getString(BindingMessages.VALIDATE_CONVERSION_FROM_CLASS_TO_PRIMITIVE);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ReadOnlyValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ReadOnlyValidator.java
new file mode 100755
index 0000000..78250fd
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ReadOnlyValidator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005, 2008 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
+ */
+package org.eclipse.core.internal.databinding.validation;
+
+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 implements IValidator {
+
+	private static ReadOnlyValidator singleton = null;
+
+	/**
+	 * Returns the ReadOnlyValidator
+	 *
+	 * @return the ReadOnlyValidator
+	 */
+	public static ReadOnlyValidator getDefault() {
+		if (singleton == null) {
+			singleton = new ReadOnlyValidator();
+		}
+		return singleton;
+	}
+
+	public IStatus validate(Object value) {
+		// No changes are allowed
+		return ValidationStatus.error(BindingMessages
+				.getString(BindingMessages.VALIDATE_NO_CHANGE_ALLOWED_HELP));
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToByteValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToByteValidator.java
new file mode 100644
index 0000000..2b7bdc4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToByteValidator.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 1.0
+ */
+public class StringToByteValidator extends AbstractStringToNumberValidator {
+	private static final Byte MIN = new Byte(Byte.MIN_VALUE);
+	private static final Byte MAX = new Byte(Byte.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public StringToByteValidator(NumberFormatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#isInRange(java.lang.Number)
+	 */
+	protected boolean isInRange(Number number) {
+		return StringToNumberParser.inByteRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java
new file mode 100644
index 0000000..894b580
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ *     Tom Schindl<tom.schindl@bestsolution.at> - bugfix for 217940
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 implements IValidator {
+
+	private final StringToCharacterConverter converter;
+
+	/**
+	 * @param converter
+	 */
+	public StringToCharacterValidator(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
+					.getString(BindingMessages.VALIDATE_CHARACTER_HELP));
+		}
+		return Status.OK_STATUS;
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java
new file mode 100644
index 0000000..9aaf642
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 implements IValidator {
+	private final StringToDateConverter converter;
+
+	/**
+	 * @param converter
+	 */
+	public StringToDateValidator(StringToDateConverter converter) {
+		this.converter = converter;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object)
+	 */
+	public IStatus validate(Object value) {
+		if (value instanceof String && ((String)value).trim().length()==0) {
+			return Status.OK_STATUS;
+		}
+		Object convertedValue = converter.convert(value);
+		//The StringToDateConverter returns null if it can't parse the date.
+		if (convertedValue == 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.getString(BindingMessages.EXAMPLES) + ": " + samples + ",..."; //$NON-NLS-1$//$NON-NLS-2$
+	}
+
+	private static class FormatUtil extends DateConversionSupport {
+		/*
+		 * (non-Javadoc)
+		 *
+		 * @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#numFormatters()
+		 */
+		protected int numFormatters() {
+			return super.numFormatters();
+		}
+
+		/*
+		 * (non-Javadoc)
+		 *
+		 * @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#format(java.util.Date)
+		 */
+		protected String format(Date date) {
+			return super.format(date);
+		}
+
+		/*
+		 * (non-Javadoc)
+		 *
+		 * @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#format(java.util.Date,
+		 *      int)
+		 */
+		protected String format(Date date, int formatterIdx) {
+			return super.format(date, formatterIdx);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDoubleValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDoubleValidator.java
new file mode 100644
index 0000000..2e0d0fc
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDoubleValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 1.0
+ */
+public class StringToDoubleValidator extends AbstractStringToNumberValidator {
+	private static final Double MIN = new Double(-Double.MAX_VALUE);
+	private static final Double MAX = new Double(Double.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public StringToDoubleValidator(NumberFormatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean isInRange(Number number) {
+		return StringToNumberParser.inDoubleRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToFloatValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToFloatValidator.java
new file mode 100644
index 0000000..6ce01e4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToFloatValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends AbstractStringToNumberValidator {
+
+	private static final Float MIN = new Float(-Float.MAX_VALUE);
+	private static final Float MAX = new Float(Float.MAX_VALUE);
+
+	/**
+	 * @param converter
+	 */
+	public StringToFloatValidator(NumberFormatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean isInRange(Number number) {
+		return StringToNumberParser.inFloatRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java
new file mode 100644
index 0000000..6b8fb24
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends AbstractStringToNumberValidator {
+	private static final Integer MIN = new Integer(Integer.MIN_VALUE);
+	private static final Integer MAX = new Integer(Integer.MAX_VALUE);
+
+	/**
+	 * @param converter
+	 */
+	public StringToIntegerValidator(NumberFormatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean isInRange(Number number) {
+		return StringToNumberParser.inIntegerRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToLongValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToLongValidator.java
new file mode 100644
index 0000000..10cd47f
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToLongValidator.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends AbstractStringToNumberValidator {
+	private static final Long MIN = new Long(Long.MIN_VALUE);
+	private static final Long MAX = new Long(Long.MAX_VALUE);
+
+	/**
+	 * @param converter
+	 */
+	public StringToLongValidator(NumberFormatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean isInRange(Number number) {
+		return StringToNumberParser.inLongRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToShortValidator.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToShortValidator.java
new file mode 100644
index 0000000..eaf16b2
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToShortValidator.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 1.0
+ */
+public class StringToShortValidator extends AbstractStringToNumberValidator {
+	private static final Short MIN = new Short(Short.MIN_VALUE);
+	private static final Short MAX = new Short(Short.MAX_VALUE);
+	
+	/**
+	 * @param converter
+	 */
+	public StringToShortValidator(NumberFormatConverter converter) {
+		super(converter, MIN, MAX);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number)
+	 */
+	protected boolean isInRange(Number number) {
+		return StringToNumberParser.inShortRange(number);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableList.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableList.java
new file mode 100644
index 0000000..7bd5950
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableList.java
@@ -0,0 +1,393 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends ObservableList {
+	private IObservableList target;
+	private IObservableValue validationStatus;
+
+	// Only true when out of sync with target due to validation status
+	private boolean stale;
+
+	// True when validaton status changes from invalid to valid.
+	private boolean computeNextDiff = false;
+
+	private boolean updatingTarget = false;
+
+	private IListChangeListener targetChangeListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			if (updatingTarget)
+				return;
+			IStatus status = (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 boolean isValid(IStatus status) {
+		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
+	}
+
+	private IStaleListener targetStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			IStatus oldStatus = (IStatus) event.diff.getOldValue();
+			IStatus newStatus = (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 ValidatedObservableList(final IObservableList target,
+			final 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, final List list) {
+		diff.accept(new ListDiffVisitor() {
+			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 boolean 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 boolean add(Object o) {
+		checkRealm();
+		add(wrappedList.size(), o);
+		return true;
+	}
+
+	public boolean addAll(Collection c) {
+		checkRealm();
+		return addAll(wrappedList.size(), c);
+	}
+
+	public boolean 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 Iterator() {
+			Object last = null;
+
+			public boolean 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 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 boolean hasNext() {
+				return wrappedIterator.hasNext();
+			}
+
+			public boolean 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 == 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 boolean remove(Object o) {
+		checkRealm();
+		int index = wrappedList.indexOf(o);
+		if (index == -1)
+			return false;
+		remove(index);
+		return true;
+	}
+
+	public boolean removeAll(Collection c) {
+		checkRealm();
+		List list = new ArrayList(wrappedList);
+		boolean changed = list.removeAll(c);
+		if (changed) {
+			ListDiff diff = Diffs.computeListDiff(wrappedList, list);
+			wrappedList = list;
+			updateTargetList(diff);
+			fireListChange(diff);
+		}
+		return changed;
+	}
+
+	public boolean retainAll(Collection c) {
+		checkRealm();
+		List list = new ArrayList(wrappedList);
+		boolean changed = list.retainAll(c);
+		if (changed) {
+			ListDiff diff = Diffs.computeListDiff(wrappedList, list);
+			wrappedList = list;
+			updateTargetList(diff);
+			fireListChange(diff);
+		}
+		return changed;
+	}
+
+	public Object set(int index, Object element) {
+		checkRealm();
+		Object oldElement = wrappedList.set(index, element);
+		ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index,
+				false, oldElement), Diffs.createListDiffEntry(index, true,
+				element));
+		updateTargetList(diff);
+		fireListChange(diff);
+		return oldElement;
+	}
+
+	public synchronized void dispose() {
+		target.removeListChangeListener(targetChangeListener);
+		target.removeStaleListener(targetStaleListener);
+		validationStatus
+				.removeValueChangeListener(validationStatusChangeListener);
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableMap.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableMap.java
new file mode 100644
index 0000000..4ea9263
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableMap.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bug 226289
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends ObservableMap {
+	private IObservableMap target;
+	private IObservableValue validationStatus;
+
+	// Only true when out of sync with target due to validation status
+	private boolean stale;
+
+	// True when validation status changes from invalid to valid.
+	private boolean computeNextDiff = false;
+
+	private boolean updatingTarget = false;
+
+	private IMapChangeListener targetChangeListener = new IMapChangeListener() {
+		public void handleMapChange(MapChangeEvent event) {
+			if (updatingTarget)
+				return;
+			IStatus status = (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 IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			IStatus oldStatus = (IStatus) event.diff.getOldValue();
+			IStatus newStatus = (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 ValidatedObservableMap(final IObservableMap target,
+			final 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 boolean 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 boolean 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 Object getKeyType() {
+		return target.getKeyType();
+	}
+
+	public Object getValueType() {
+		return target.getValueType();
+	}
+
+	public synchronized void dispose() {
+		target.removeMapChangeListener(targetChangeListener);
+		target.removeStaleListener(targetStaleListener);
+		validationStatus
+				.removeValueChangeListener(validationStatusChangeListener);
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableSet.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableSet.java
new file mode 100644
index 0000000..450ee50
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableSet.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 extends ObservableSet {
+	private IObservableSet target;
+	private IObservableValue validationStatus;
+
+	// Only true when out of sync with target due to validation status
+	private boolean stale;
+
+	// True when validation status changes from invalid to valid.
+	private boolean computeNextDiff = false;
+
+	private boolean updatingTarget = false;
+
+	private ISetChangeListener targetChangeListener = new ISetChangeListener() {
+		public void handleSetChange(SetChangeEvent event) {
+			if (updatingTarget)
+				return;
+			IStatus status = (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 IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			IStatus oldStatus = (IStatus) event.diff.getOldValue();
+			IStatus newStatus = (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 ValidatedObservableSet(final IObservableSet target,
+			final 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 boolean 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 boolean isStale() {
+		getterCalled();
+		return stale || target.isStale();
+	}
+
+	public boolean add(Object o) {
+		getterCalled();
+		boolean changed = wrappedSet.add(o);
+		if (changed) {
+			SetDiff diff = Diffs.createSetDiff(Collections.singleton(o),
+					Collections.EMPTY_SET);
+			updateTargetSet(diff);
+			fireSetChange(diff);
+		}
+		return changed;
+	}
+
+	public boolean addAll(Collection c) {
+		getterCalled();
+		HashSet set = new HashSet(wrappedSet);
+		boolean 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 Iterator() {
+			Object last = null;
+
+			public boolean 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 boolean remove(Object o) {
+		getterCalled();
+		boolean changed = wrappedSet.remove(o);
+		if (changed) {
+			SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET,
+					Collections.singleton(o));
+			updateTargetSet(diff);
+			fireSetChange(diff);
+		}
+		return changed;
+	}
+
+	public boolean removeAll(Collection c) {
+		getterCalled();
+		Set set = new HashSet(wrappedSet);
+		boolean changed = set.removeAll(c);
+		if (changed) {
+			SetDiff diff = Diffs.computeSetDiff(wrappedSet, set);
+			wrappedSet = set;
+			updateTargetSet(diff);
+			fireSetChange(diff);
+		}
+		return changed;
+	}
+
+	public boolean retainAll(Collection c) {
+		getterCalled();
+		Set set = new HashSet(wrappedSet);
+		boolean changed = set.retainAll(c);
+		if (changed) {
+			SetDiff diff = Diffs.computeSetDiff(wrappedSet, set);
+			wrappedSet = set;
+			updateTargetSet(diff);
+			fireSetChange(diff);
+		}
+		return changed;
+	}
+
+	public synchronized void dispose() {
+		target.removeSetChangeListener(targetChangeListener);
+		target.removeStaleListener(targetStaleListener);
+		validationStatus
+				.removeValueChangeListener(validationStatusChangeListener);
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableValue.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableValue.java
new file mode 100644
index 0000000..3b08d49
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ValidatedObservableValue.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.validation;
+
+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 #setValue(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 extends AbstractObservableValue {
+	private IObservableValue target;
+	private IObservableValue validationStatus;
+
+	private Object cachedValue;
+	private boolean stale;
+	private boolean updatingTarget = false;
+
+	private IValueChangeListener targetChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (updatingTarget)
+				return;
+			IStatus status = (IStatus) validationStatus.getValue();
+			if (isValid(status))
+				internalSetValue(event.diff.getNewValue(), false);
+			else
+				makeStale();
+		}
+	};
+
+	private static boolean isValid(IStatus status) {
+		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
+	}
+
+	private IStaleListener targetStaleListener = new IStaleListener() {
+		public void handleStale(StaleEvent staleEvent) {
+			fireStale();
+		}
+	};
+
+	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			IStatus oldStatus = (IStatus) event.diff.getOldValue();
+			IStatus newStatus = (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}.class which
+	 *            contains the current validation status
+	 */
+	public ValidatedObservableValue(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 boolean isStale() {
+		ObservableTracker.getterCalled(this);
+		return stale || target.isStale();
+	}
+
+	protected Object doGetValue() {
+		return cachedValue;
+	}
+
+	private void internalSetValue(Object value, boolean updateTarget) {
+		Object oldValue = cachedValue;
+		cachedValue = value;
+		if (updateTarget) {
+			updatingTarget = true;
+			try {
+				target.setValue(value);
+				cachedValue = target.getValue();
+			} finally {
+				updatingTarget = false;
+			}
+		}
+		stale = false;
+		if (!Util.equals(oldValue, cachedValue))
+			fireValueChange(Diffs.createValueDiff(oldValue, cachedValue));
+	}
+
+	protected void doSetValue(Object value) {
+		internalSetValue(value, true);
+	}
+
+	public Object getValueType() {
+		return target.getValueType();
+	}
+
+	public synchronized void dispose() {
+		target.removeValueChangeListener(targetChangeListener);
+		target.removeStaleListener(targetStaleListener);
+		validationStatus
+				.removeValueChangeListener(validationStatusChangeListener);
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/.classpath b/bundles/org.eclipse.jface.databinding/.classpath
new file mode 100644
index 0000000..c2ce266
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.0%Foundation-1.0"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.jface.databinding/.cvsignore b/bundles/org.eclipse.jface.databinding/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/bundles/org.eclipse.jface.databinding/.project b/bundles/org.eclipse.jface.databinding/.project
new file mode 100644
index 0000000..c1c9d28
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jface.databinding</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.jface.databinding/.settings/.api_filters b/bundles/org.eclipse.jface.databinding/.settings/.api_filters
new file mode 100644
index 0000000..12d8818
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.settings/.api_filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jface.databinding" version="2">
+    <resource path="src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.java" type="org.eclipse.jface.databinding.viewers.ListeningLabelProvider">
+        <filter id="576720909">
+            <message_arguments>
+                <message_argument value="ViewerLabelProvider"/>
+                <message_argument value="ListeningLabelProvider"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.jdt.core.prefs b/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..c42c217
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,353 @@
+#Thu Feb 05 11:36:06 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.3
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=error
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=error
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.jdt.ui.prefs b/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..0459eaa
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:05:57 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.3\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.pde.prefs b/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..2ad454b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:51:53 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=0
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=0
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/bundles/org.eclipse.jface.databinding/.settings/org.moreunit.prefs b/bundles/org.eclipse.jface.databinding/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..7592747
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/.settings/org.moreunit.prefs
@@ -0,0 +1,5 @@
+#Tue Feb 02 23:25:33 MST 2010
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=org.eclipse.jface.databinding\:src\:org.eclipse.jface.tests.databinding\:src
+org.moreunit.useprojectsettings=true
diff --git a/bundles/org.eclipse.jface.databinding/META-INF/MANIFEST.MF b/bundles/org.eclipse.jface.databinding/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..274f3aa
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/META-INF/MANIFEST.MF
@@ -0,0 +1,29 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jface.databinding
+Bundle-Version: 1.5.0.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Export-Package: org.eclipse.jface.databinding.dialog,
+ org.eclipse.jface.databinding.fieldassist,
+ org.eclipse.jface.databinding.preference,
+ org.eclipse.jface.databinding.swt,
+ org.eclipse.jface.databinding.util,
+ org.eclipse.jface.databinding.viewers,
+ org.eclipse.jface.databinding.wizard,
+ org.eclipse.jface.internal.databinding.provisional.swt;x-internal:=true,
+ org.eclipse.jface.internal.databinding.provisional.viewers;x-internal:=true,
+ org.eclipse.jface.internal.databinding.swt;x-internal:=true,
+ org.eclipse.jface.internal.databinding.util;x-internal:=true,
+ org.eclipse.jface.internal.databinding.viewers;x-internal:=true
+Require-Bundle: org.eclipse.swt;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.jface;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)",
+ org.eclipse.core.databinding.observable;bundle-version="[1.3.0,2.0.0)",
+ org.eclipse.core.databinding.property;bundle-version="[1.3.0,2.0.0)",
+ org.eclipse.core.databinding;bundle-version="[1.2.0,2.0.0)"
+Import-Package: com.ibm.icu.text
+Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,
+ J2SE-1.3
diff --git a/bundles/org.eclipse.jface.databinding/about.html b/bundles/org.eclipse.jface.databinding/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/build.properties b/bundles/org.eclipse.jface.databinding/build.properties
new file mode 100644
index 0000000..6f0a513
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# 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
+###############################################################################
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+output.databinding.jar = bin/
+src.includes = about.html
+source.. = src/
diff --git a/bundles/org.eclipse.jface.databinding/plugin.properties b/bundles/org.eclipse.jface.databinding/plugin.properties
new file mode 100644
index 0000000..2880743
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# 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
+###############################################################################
+pluginName = JFace Data Binding for SWT and JFace
+providerName = Eclipse.org
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/DialogPageSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/DialogPageSupport.java
new file mode 100644
index 0000000..aa9eca7
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/DialogPageSupport.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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:
+ *     IBM Corporation - initial API and implementation
+ *         (through WizardPageSupport.java)
+ *     Matthew Hall - initial API and implementation (bug 239900)
+ *     Matthew Hall - bugs 237856, 275058, 278550
+ *     Ovidio Mallo - bugs 237856, 248877
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.dialog;
+
+import java.util.Iterator;
+
+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.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.value.IObservableValue;
+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.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+/**
+ * Connects the validation result from the given data binding context to the
+ * given dialog page, updating the page's error message accordingly.
+ * 
+ * @since 1.3
+ */
+public class DialogPageSupport {
+	/**
+	 * Connect the validation result from the given data binding context to the
+	 * given dialog page. The page's error message will not be set at time of
+	 * creation, ensuring that the dialog page does not show an error right
+	 * away. Upon any validation result change, the dialog page's error message
+	 * will be updated according to the current validation result.
+	 * 
+	 * @param dialogPage
+	 * @param dbc
+	 * @return an instance of WizardPageSupport
+	 */
+	public static DialogPageSupport create(DialogPage dialogPage,
+			DataBindingContext dbc) {
+		return new DialogPageSupport(dialogPage, dbc);
+	}
+
+	private DialogPage dialogPage;
+	private DataBindingContext dbc;
+	private IValidationMessageProvider messageProvider = new ValidationMessageProvider();
+	private IObservableValue aggregateStatusProvider;
+	private boolean uiChanged = false;
+	private IChangeListener uiChangeListener = new IChangeListener() {
+		public void handleChange(ChangeEvent event) {
+			handleUIChanged();
+		}
+	};
+	private IListChangeListener validationStatusProvidersListener = new 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 = (ValidationStatusProvider) listDiffEntry
+						.getElement();
+				IObservableList targets = validationStatusProvider.getTargets();
+				if (listDiffEntry.isAddition()) {
+					targets
+							.addListChangeListener(validationStatusProviderTargetsListener);
+					for (Iterator it = targets.iterator(); it.hasNext();) {
+						((IObservable) it.next())
+								.addChangeListener(uiChangeListener);
+					}
+				} else {
+					targets
+							.removeListChangeListener(validationStatusProviderTargetsListener);
+					for (Iterator it = targets.iterator(); it.hasNext();) {
+						((IObservable) it.next())
+								.removeChangeListener(uiChangeListener);
+					}
+				}
+			}
+		}
+	};
+	private IListChangeListener validationStatusProviderTargetsListener = new 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 = (IObservable) listDiffEntry.getElement();
+				if (listDiffEntry.isAddition()) {
+					target.addChangeListener(uiChangeListener);
+				} else {
+					target.removeChangeListener(uiChangeListener);
+				}
+			}
+		}
+	};
+	private ValidationStatusProvider currentStatusProvider;
+	protected IStatus currentStatus;
+	protected boolean currentStatusStale;
+
+	/**
+	 * @param dialogPage
+	 * @param dbc
+	 * @noreference This constructor is not intended to be referenced by
+	 *              clients.
+	 */
+	protected DialogPageSupport(DialogPage dialogPage, DataBindingContext dbc) {
+		this.dialogPage = dialogPage;
+		this.dbc = dbc;
+		init();
+	}
+
+	/**
+	 * Sets the {@link IValidationMessageProvider} to use for providing the
+	 * message text and message type to display on the dialog page.
+	 * 
+	 * @param messageProvider
+	 *            The {@link IValidationMessageProvider} to use for providing
+	 *            the message text and message type to display on the dialog
+	 *            page.
+	 * 
+	 * @since 1.4
+	 */
+	public void setValidationMessageProvider(
+			IValidationMessageProvider messageProvider) {
+		this.messageProvider = messageProvider;
+		handleStatusChanged();
+	}
+
+	/**
+	 * @return the dialog page
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected DialogPage getDialogPage() {
+		return dialogPage;
+	}
+
+	/**
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void init() {
+		ObservableTracker.setIgnore(true);
+		try {
+			aggregateStatusProvider = new MaxSeverityValidationStatusProvider(
+					dbc);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		aggregateStatusProvider
+				.addValueChangeListener(new IValueChangeListener() {
+					public void handleValueChange(ValueChangeEvent event) {
+						statusProviderChanged();
+					}
+				});
+		dialogPage.getShell().addListener(SWT.Dispose, new Listener() {
+			public void handleEvent(Event event) {
+				dispose();
+			}
+		});
+		aggregateStatusProvider.addStaleListener(new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+				currentStatusStale = true;
+				handleStatusChanged();
+			}
+		});
+		statusProviderChanged();
+		dbc.getValidationStatusProviders().addListChangeListener(
+				validationStatusProvidersListener);
+		for (Iterator it = dbc.getValidationStatusProviders().iterator(); it
+				.hasNext();) {
+			ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it
+					.next();
+			IObservableList targets = validationStatusProvider.getTargets();
+			targets
+					.addListChangeListener(validationStatusProviderTargetsListener);
+			for (Iterator iter = targets.iterator(); iter.hasNext();) {
+				((IObservable) iter.next()).addChangeListener(uiChangeListener);
+			}
+		}
+	}
+
+	private void statusProviderChanged() {
+		currentStatusProvider = (ValidationStatusProvider) aggregateStatusProvider
+				.getValue();
+		if (currentStatusProvider != null) {
+			currentStatus = (IStatus) currentStatusProvider
+					.getValidationStatus().getValue();
+		} else {
+			currentStatus = null;
+		}
+		currentStatusStale = aggregateStatusProvider.isStale();
+		handleStatusChanged();
+	}
+
+	/**
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void handleUIChanged() {
+		uiChanged = true;
+		if (currentStatus != null) {
+			handleStatusChanged();
+		}
+		dbc.getValidationStatusProviders().removeListChangeListener(
+				validationStatusProvidersListener);
+		for (Iterator it = dbc.getValidationStatusProviders().iterator(); it
+				.hasNext();) {
+			ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it
+					.next();
+			IObservableList targets = validationStatusProvider.getTargets();
+			targets
+					.removeListChangeListener(validationStatusProviderTargetsListener);
+			for (Iterator iter = targets.iterator(); iter.hasNext();) {
+				((IObservable) iter.next())
+						.removeChangeListener(uiChangeListener);
+			}
+		}
+	}
+
+	/**
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void handleStatusChanged() {
+		String message = messageProvider.getMessage(currentStatusProvider);
+		int type = messageProvider.getMessageType(currentStatusProvider);
+		if (type == IMessageProvider.ERROR) {
+			dialogPage.setMessage(null);
+			dialogPage.setErrorMessage(uiChanged ? message : null);
+			if (currentStatus != null && currentStatusHasException()) {
+				handleStatusException();
+			}
+		} else {
+			dialogPage.setErrorMessage(null);
+			dialogPage.setMessage(message, type);
+		}
+	}
+
+	private boolean currentStatusHasException() {
+		boolean hasException = false;
+		if (currentStatus.getException() != null) {
+			hasException = true;
+		}
+		if (currentStatus instanceof MultiStatus) {
+			MultiStatus multiStatus = (MultiStatus) currentStatus;
+
+			for (int i = 0; i < multiStatus.getChildren().length; i++) {
+				IStatus status = multiStatus.getChildren()[i];
+				if (status.getException() != null) {
+					hasException = true;
+					break;
+				}
+			}
+		}
+		return hasException;
+	}
+
+	/**
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	protected void handleStatusException() {
+		if (currentStatus.getException() != null) {
+			logThrowable(currentStatus.getException());
+		} else if (currentStatus instanceof MultiStatus) {
+			MultiStatus multiStatus = (MultiStatus) currentStatus;
+			for (int i = 0; i < multiStatus.getChildren().length; i++) {
+				IStatus status = multiStatus.getChildren()[i];
+				if (status.getException() != 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() {
+		if (aggregateStatusProvider != null)
+			aggregateStatusProvider.dispose();
+		if (dbc != null && !uiChanged) {
+			for (Iterator it = dbc.getValidationStatusProviders().iterator(); it
+					.hasNext();) {
+				ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it
+						.next();
+				IObservableList targets = validationStatusProvider.getTargets();
+				targets
+						.removeListChangeListener(validationStatusProviderTargetsListener);
+				for (Iterator iter = targets.iterator(); iter.hasNext();) {
+					((IObservable) iter.next())
+							.removeChangeListener(uiChangeListener);
+				}
+			}
+			dbc.getValidationStatusProviders().removeListChangeListener(
+					validationStatusProvidersListener);
+		}
+		aggregateStatusProvider = null;
+		dbc = null;
+		uiChangeListener = null;
+		validationStatusProvidersListener = null;
+		validationStatusProviderTargetsListener = null;
+		dialogPage = null;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/IValidationMessageProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/IValidationMessageProvider.java
new file mode 100644
index 0000000..1da2b23
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/IValidationMessageProvider.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 248877)
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.dialog;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.jface.dialogs.IMessageProvider;
+
+/**
+ * Simple interface to provide a validation message text and a message type for
+ * a given {@link ValidationStatusProvider}.
+ * 
+ * <p>
+ * Can be used in dialogs to display a message text along with an icon
+ * reflecting the validation status.
+ * </p>
+ * 
+ * @since 1.4
+ */
+public interface IValidationMessageProvider {
+
+	/**
+	 * Returns the validation message text for the given validation status
+	 * provider.
+	 * 
+	 * @param statusProvider
+	 *            the {@link ValidationStatusProvider} for which to provide a
+	 *            message text. May be <code>null</code>.
+	 * @return The validation message text for the given
+	 *         <code>validationStatusProvider</code>. May be <code>null</code>.
+	 */
+	public String getMessage(ValidationStatusProvider statusProvider);
+
+	/**
+	 * Returns the validation message type as one of the constants defined in
+	 * {@link IMessageProvider} for the given validation status provider.
+	 * 
+	 * @param statusProvider
+	 *            the {@link ValidationStatusProvider} for which to provide a
+	 *            message type. May be <code>null</code>.
+	 * @return The validation message type for the given
+	 *         <code>validationStatusProvider</code>.
+	 */
+	public int getMessageType(ValidationStatusProvider statusProvider);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/MaxSeverityValidationStatusProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/MaxSeverityValidationStatusProvider.java
new file mode 100644
index 0000000..50fea47
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/MaxSeverityValidationStatusProvider.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 248877)
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.dialog;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.runtime.IStatus;
+
+/*package*/ class MaxSeverityValidationStatusProvider extends ComputedValue {
+
+	private Collection validationStatusProviders;
+
+	public MaxSeverityValidationStatusProvider(DataBindingContext dbc) {
+		super(ValidationStatusProvider.class);
+		this.validationStatusProviders = dbc.getValidationStatusProviders();
+	}
+
+	protected Object calculate() {
+		int maxSeverity = IStatus.OK;
+		ValidationStatusProvider maxSeverityProvider = null;
+		for (Iterator it = validationStatusProviders.iterator(); it.hasNext();) {
+			ValidationStatusProvider provider = (ValidationStatusProvider) it
+					.next();
+			IStatus status = (IStatus) provider.getValidationStatus()
+					.getValue();
+			if (status.getSeverity() > maxSeverity) {
+				maxSeverity = status.getSeverity();
+				maxSeverityProvider = provider;
+			}
+		}
+		return maxSeverityProvider;
+	}
+
+	public synchronized void dispose() {
+		validationStatusProviders = null;
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/TitleAreaDialogSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/TitleAreaDialogSupport.java
new file mode 100644
index 0000000..eafba89
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/TitleAreaDialogSupport.java
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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:
+ *     IBM Corporation - initial API and implementation
+ *        (through WizardPageSupport.java)
+ *     Matthew Hall - initial API and implementation (bug 239900)
+ *     Ben Vitale <bvitale3002@yahoo.com> - bug 263100
+ *     Kai Schlamp - bug 275058
+ *     Matthew Hall - bugs 275058, 278550
+ *     Ovidio Mallo - bug 248877
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.dialog;
+
+import java.util.Iterator;
+
+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.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.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+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.util.Policy;
+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.dialogs.TitleAreaDialog;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+
+/**
+ * Connects the validation result from the given data binding context to the
+ * given TitleAreaDialog, updating the dialog's error message accordingly.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * 
+ * @since 1.3
+ */
+public class TitleAreaDialogSupport {
+	/**
+	 * Connect the validation result from the given data binding context to the
+	 * given TitleAreaDialog. The page's error message will not be set at time
+	 * of creation, ensuring that the dialog does not show an error right away.
+	 * Upon any validation result change, the dialog's error message will be
+	 * updated according to the current validation result.
+	 * 
+	 * @param dialog
+	 * @param dbc
+	 * @return an instance of TitleAreaDialogSupport
+	 */
+	public static TitleAreaDialogSupport create(TitleAreaDialog dialog,
+			DataBindingContext dbc) {
+		return new TitleAreaDialogSupport(dialog, dbc);
+	}
+
+	private TitleAreaDialog dialog;
+	private DataBindingContext dbc;
+	private IValidationMessageProvider messageProvider = new ValidationMessageProvider();
+	private IObservableValue aggregateStatusProvider;
+	private boolean uiChanged = false;
+	private IChangeListener uiChangeListener = new IChangeListener() {
+		public void handleChange(ChangeEvent event) {
+			handleUIChanged();
+		}
+	};
+	private IListChangeListener validationStatusProvidersListener = new 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 = (ValidationStatusProvider) listDiffEntry
+						.getElement();
+				IObservableList targets = validationStatusProvider.getTargets();
+				if (listDiffEntry.isAddition()) {
+					targets
+							.addListChangeListener(validationStatusProviderTargetsListener);
+					for (Iterator it = targets.iterator(); it.hasNext();) {
+						((IObservable) it.next())
+								.addChangeListener(uiChangeListener);
+					}
+				} else {
+					targets
+							.removeListChangeListener(validationStatusProviderTargetsListener);
+					for (Iterator it = targets.iterator(); it.hasNext();) {
+						((IObservable) it.next())
+								.removeChangeListener(uiChangeListener);
+					}
+				}
+			}
+		}
+	};
+	private IListChangeListener validationStatusProviderTargetsListener = new 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 = (IObservable) listDiffEntry.getElement();
+				if (listDiffEntry.isAddition()) {
+					target.addChangeListener(uiChangeListener);
+				} else {
+					target.removeChangeListener(uiChangeListener);
+				}
+			}
+		}
+	};
+	private ValidationStatusProvider currentStatusProvider;
+	private IStatus currentStatus;
+
+	private TitleAreaDialogSupport(TitleAreaDialog dialogPage,
+			DataBindingContext dbc) {
+		this.dialog = dialogPage;
+		this.dbc = dbc;
+		init();
+	}
+
+	/**
+	 * Sets the {@link IValidationMessageProvider} to use for providing the
+	 * message text and message type to display on the title area dialog.
+	 * 
+	 * @param messageProvider
+	 *            The {@link IValidationMessageProvider} to use for providing
+	 *            the message text and message type to display on the title area
+	 *            dialog.
+	 * 
+	 * @since 1.4
+	 */
+	public void setValidationMessageProvider(
+			IValidationMessageProvider messageProvider) {
+		this.messageProvider = messageProvider;
+		handleStatusChanged();
+	}
+
+	private void init() {
+		ObservableTracker.setIgnore(true);
+		try {
+			aggregateStatusProvider = new MaxSeverityValidationStatusProvider(
+					dbc);
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+
+		aggregateStatusProvider
+				.addValueChangeListener(new IValueChangeListener() {
+					public void handleValueChange(ValueChangeEvent event) {
+						statusProviderChanged();
+					}
+				});
+		dialog.getShell().addDisposeListener(new DisposeListener() {
+			public void widgetDisposed(DisposeEvent e) {
+				dispose();
+			}
+		});
+		statusProviderChanged();
+		dbc.getValidationStatusProviders().addListChangeListener(
+				validationStatusProvidersListener);
+		for (Iterator it = dbc.getValidationStatusProviders().iterator(); it
+				.hasNext();) {
+			ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it
+					.next();
+			IObservableList targets = validationStatusProvider.getTargets();
+			targets
+					.addListChangeListener(validationStatusProviderTargetsListener);
+			for (Iterator iter = targets.iterator(); iter.hasNext();) {
+				((IObservable) iter.next()).addChangeListener(uiChangeListener);
+			}
+		}
+	}
+
+	private void statusProviderChanged() {
+		currentStatusProvider = (ValidationStatusProvider) aggregateStatusProvider
+				.getValue();
+		if (currentStatusProvider != null) {
+			currentStatus = (IStatus) currentStatusProvider
+					.getValidationStatus().getValue();
+		} else {
+			currentStatus = null;
+		}
+		handleStatusChanged();
+	}
+
+	private void handleUIChanged() {
+		uiChanged = true;
+		if (currentStatus != null) {
+			handleStatusChanged();
+		}
+		dbc.getValidationStatusProviders().removeListChangeListener(
+				validationStatusProvidersListener);
+		for (Iterator it = dbc.getValidationStatusProviders().iterator(); it
+				.hasNext();) {
+			ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it
+					.next();
+			IObservableList targets = validationStatusProvider.getTargets();
+			targets
+					.removeListChangeListener(validationStatusProviderTargetsListener);
+			for (Iterator iter = targets.iterator(); iter.hasNext();) {
+				((IObservable) iter.next())
+						.removeChangeListener(uiChangeListener);
+			}
+		}
+	}
+
+	private void handleStatusChanged() {
+		if (dialog.getShell() == null || dialog.getShell().isDisposed())
+			return;
+		String message = messageProvider.getMessage(currentStatusProvider);
+		int type = messageProvider.getMessageType(currentStatusProvider);
+		if (type == IMessageProvider.ERROR) {
+			dialog.setMessage(null);
+			dialog.setErrorMessage(uiChanged ? message : null);
+			if (currentStatus != null && currentStatusHasException()) {
+				handleStatusException();
+			}
+		} else {
+			dialog.setErrorMessage(null);
+			dialog.setMessage(message, type);
+		}
+	}
+
+	private boolean currentStatusHasException() {
+		boolean hasException = false;
+		if (currentStatus.getException() != null) {
+			hasException = true;
+		}
+		if (currentStatus instanceof MultiStatus) {
+			MultiStatus multiStatus = (MultiStatus) currentStatus;
+
+			for (int i = 0; i < multiStatus.getChildren().length; i++) {
+				IStatus status = multiStatus.getChildren()[i];
+				if (status.getException() != null) {
+					hasException = true;
+					break;
+				}
+			}
+		}
+		return hasException;
+	}
+
+	/**
+	 * This is called when a Override to provide custom exception handling and
+	 * reporting.
+	 */
+	private void handleStatusException() {
+		if (currentStatus.getException() != null) {
+			logThrowable(currentStatus.getException());
+		} else if (currentStatus instanceof MultiStatus) {
+			MultiStatus multiStatus = (MultiStatus) currentStatus;
+			for (int i = 0; i < multiStatus.getChildren().length; i++) {
+				IStatus status = multiStatus.getChildren()[i];
+				if (status.getException() != 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 title area dialog support object, removing any listeners
+	 * it may have attached.
+	 */
+	public void dispose() {
+		if (aggregateStatusProvider != null)
+			aggregateStatusProvider.dispose();
+		if (dbc != null && !uiChanged) {
+			for (Iterator it = dbc.getValidationStatusProviders().iterator(); it
+					.hasNext();) {
+				ValidationStatusProvider validationStatusProvider = (ValidationStatusProvider) it
+						.next();
+				IObservableList targets = validationStatusProvider.getTargets();
+				targets
+						.removeListChangeListener(validationStatusProviderTargetsListener);
+				for (Iterator iter = targets.iterator(); iter.hasNext();) {
+					((IObservable) iter.next())
+							.removeChangeListener(uiChangeListener);
+				}
+			}
+			dbc.getValidationStatusProviders().removeListChangeListener(
+					validationStatusProvidersListener);
+		}
+		aggregateStatusProvider = null;
+		dbc = null;
+		uiChangeListener = null;
+		validationStatusProvidersListener = null;
+		validationStatusProviderTargetsListener = null;
+		dialog = null;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/ValidationMessageProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/ValidationMessageProvider.java
new file mode 100644
index 0000000..00fcb10
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/dialog/ValidationMessageProvider.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 248877)
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.dialog;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.IMessageProvider;
+
+/**
+ * Standard implementation of the {@link IValidationMessageProvider} interface.
+ * 
+ * @since 1.4
+ */
+public class ValidationMessageProvider implements IValidationMessageProvider {
+
+	/**
+	 * Returns the {@link IStatus#getMessage() message} of the
+	 * <code>IStatus</code> contained in the provided
+	 * <code>validationStatusProvider</code> as is or <code>null</code> if the
+	 * <code>validationStatusProvider</code> is itself <code>null</code>.
+	 */
+	public String getMessage(ValidationStatusProvider statusProvider) {
+		if (statusProvider != null) {
+			IStatus status = (IStatus) statusProvider.getValidationStatus()
+					.getValue();
+			return status.getMessage();
+		}
+		return null;
+	}
+
+	/**
+	 * Returns the message type defined in {@link IMessageProvider} which
+	 * naturally maps to the {@link IStatus#getSeverity()} of the
+	 * <code>IStatus</code> contained in the provided
+	 * <code>validationStatusProvider</code>.
+	 */
+	public int getMessageType(ValidationStatusProvider statusProvider) {
+		if (statusProvider == null) {
+			return IMessageProvider.NONE;
+		}
+
+		IStatus status = (IStatus) statusProvider.getValidationStatus()
+				.getValue();
+		int severity = status.getSeverity();
+		switch (severity) {
+		case IStatus.OK:
+			return IMessageProvider.NONE;
+		case IStatus.CANCEL:
+			return IMessageProvider.NONE;
+		case IStatus.INFO:
+			return IMessageProvider.INFORMATION;
+		case IStatus.WARNING:
+			return IMessageProvider.WARNING;
+		case IStatus.ERROR:
+			return IMessageProvider.ERROR;
+		default:
+			Assert.isTrue(false, "incomplete switch statement"); //$NON-NLS-1$
+			return -1; // unreachable
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/fieldassist/ControlDecorationSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/fieldassist/ControlDecorationSupport.java
new file mode 100644
index 0000000..2541527
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/fieldassist/ControlDecorationSupport.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 268472)
+ *     Matthew Hall - bug 300953
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.fieldassist;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDecoratingObservable;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+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.ListDiffVisitor;
+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;
+import org.eclipse.jface.databinding.swt.ISWTObservable;
+import org.eclipse.jface.databinding.viewers.IViewerObservable;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Decorates the underlying controls of the target observables of a
+ * {@link ValidationStatusProvider} with {@link ControlDecoration}s mirroring
+ * the current validation status. Only those target observables which implement
+ * {@link ISWTObservable} or {@link IViewerObservable} are decorated.
+ * 
+ * @since 1.4
+ */
+public class ControlDecorationSupport {
+	/**
+	 * Creates a ControlDecorationSupport which observes the validation status
+	 * of the specified {@link ValidationStatusProvider}, and displays a
+	 * {@link ControlDecoration} over the underlying SWT control of all target
+	 * observables that implement {@link ISWTObservable} or
+	 * {@link IViewerObservable}.
+	 * 
+	 * @param validationStatusProvider
+	 *            the {@link ValidationStatusProvider} to monitor.
+	 * @param position
+	 *            SWT alignment constant (e.g. SWT.LEFT | SWT.TOP) to use when
+	 *            constructing {@link ControlDecorationSupport}
+	 * @return a ControlDecorationSupport which observes the validation status
+	 *         of the specified {@link ValidationStatusProvider}, and displays a
+	 *         {@link ControlDecoration} over the underlying SWT control of all
+	 *         target observables that implement {@link ISWTObservable} or
+	 *         {@link IViewerObservable}.
+	 */
+	public static ControlDecorationSupport create(
+			ValidationStatusProvider validationStatusProvider, int position) {
+		return create(validationStatusProvider, position, null,
+				new ControlDecorationUpdater());
+	}
+
+	/**
+	 * Creates a ControlDecorationSupport which observes the validation status
+	 * of the specified {@link ValidationStatusProvider}, and displays a
+	 * {@link ControlDecoration} over the underlying SWT control of all target
+	 * observables that implement {@link ISWTObservable} or
+	 * {@link IViewerObservable}.
+	 * 
+	 * @param validationStatusProvider
+	 *            the {@link ValidationStatusProvider} to monitor.
+	 * @param position
+	 *            SWT alignment constant (e.g. SWT.LEFT | SWT.TOP) to use when
+	 *            constructing {@link ControlDecoration} instances.
+	 * @param composite
+	 *            the composite to use when constructing
+	 *            {@link ControlDecoration} instances.
+	 * @return a ControlDecorationSupport which observes the validation status
+	 *         of the specified {@link ValidationStatusProvider}, and displays a
+	 *         {@link ControlDecoration} over the underlying SWT control of all
+	 *         target observables that implement {@link ISWTObservable} or
+	 *         {@link IViewerObservable}.
+	 */
+	public static ControlDecorationSupport create(
+			ValidationStatusProvider validationStatusProvider, int position,
+			Composite composite) {
+		return create(validationStatusProvider, position, composite,
+				new ControlDecorationUpdater());
+	}
+
+	/**
+	 * Creates a ControlDecorationSupport which observes the validation status
+	 * of the specified {@link ValidationStatusProvider}, and displays a
+	 * {@link ControlDecoration} over the underlying SWT control of all target
+	 * observables that implement {@link ISWTObservable} or
+	 * {@link IViewerObservable}.
+	 * 
+	 * @param validationStatusProvider
+	 *            the {@link ValidationStatusProvider} to monitor.
+	 * @param position
+	 *            SWT alignment constant (e.g. SWT.LEFT | SWT.TOP) to use when
+	 *            constructing {@link ControlDecoration} instances.
+	 * @param composite
+	 *            the composite to use when constructing
+	 *            {@link ControlDecoration} instances.
+	 * @param updater
+	 *            custom strategy for updating the {@link ControlDecoration}(s)
+	 *            whenever the validation status changes.
+	 * @return a ControlDecorationSupport which observes the validation status
+	 *         of the specified {@link ValidationStatusProvider}, and displays a
+	 *         {@link ControlDecoration} over the underlying SWT control of all
+	 *         target observables that implement {@link ISWTObservable} or
+	 *         {@link IViewerObservable}.
+	 */
+	public static ControlDecorationSupport create(
+			ValidationStatusProvider validationStatusProvider, int position,
+			Composite composite, ControlDecorationUpdater updater) {
+		return new ControlDecorationSupport(validationStatusProvider, position,
+				composite, updater);
+	}
+
+	private final int position;
+	private final Composite composite;
+	private final ControlDecorationUpdater updater;
+
+	private IObservableValue validationStatus;
+	private IObservableList targets;
+
+	private IDisposeListener disposeListener = new IDisposeListener() {
+		public void handleDispose(DisposeEvent staleEvent) {
+			dispose();
+		}
+	};
+
+	private IValueChangeListener statusChangeListener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			statusChanged((IStatus) validationStatus.getValue());
+		}
+	};
+
+	private IListChangeListener targetsChangeListener = new IListChangeListener() {
+		public void handleListChange(ListChangeEvent event) {
+			event.diff.accept(new ListDiffVisitor() {
+				public void handleAdd(int index, Object element) {
+					targetAdded((IObservable) element);
+				}
+
+				public void handleRemove(int index, Object element) {
+					targetRemoved((IObservable) element);
+				}
+			});
+			statusChanged((IStatus) validationStatus.getValue());
+		}
+	};
+
+	private static class TargetDecoration {
+		public final IObservable target;
+		public final ControlDecoration decoration;
+
+		TargetDecoration(IObservable target, ControlDecoration decoration) {
+			this.target = target;
+			this.decoration = decoration;
+		}
+	}
+
+	private List targetDecorations;
+
+	private ControlDecorationSupport(
+			ValidationStatusProvider validationStatusProvider, int position,
+			Composite composite, ControlDecorationUpdater updater) {
+		this.position = position;
+		this.composite = composite;
+		this.updater = updater;
+
+		this.validationStatus = validationStatusProvider.getValidationStatus();
+		Assert.isTrue(!this.validationStatus.isDisposed());
+
+		this.targets = validationStatusProvider.getTargets();
+		Assert.isTrue(!this.targets.isDisposed());
+
+		this.targetDecorations = new ArrayList();
+
+		validationStatus.addDisposeListener(disposeListener);
+		validationStatus.addValueChangeListener(statusChangeListener);
+
+		targets.addDisposeListener(disposeListener);
+		targets.addListChangeListener(targetsChangeListener);
+
+		for (Iterator it = targets.iterator(); it.hasNext();)
+			targetAdded((IObservable) it.next());
+
+		statusChanged((IStatus) validationStatus.getValue());
+	}
+
+	private void targetAdded(IObservable target) {
+		Control control = findControl(target);
+		if (control != null)
+			targetDecorations.add(new TargetDecoration(target,
+					new ControlDecoration(control, position, composite)));
+	}
+
+	private void targetRemoved(IObservable target) {
+		for (Iterator it = targetDecorations.iterator(); it.hasNext();) {
+			TargetDecoration targetDecoration = (TargetDecoration) it.next();
+			if (targetDecoration.target == target) {
+				targetDecoration.decoration.dispose();
+				it.remove();
+			}
+		}
+	}
+
+	private Control findControl(IObservable target) {
+		if (target instanceof ISWTObservable) {
+			Widget widget = ((ISWTObservable) target).getWidget();
+			if (widget instanceof Control)
+				return (Control) widget;
+		}
+
+		if (target instanceof IViewerObservable) {
+			Viewer viewer = ((IViewerObservable) target).getViewer();
+			return viewer.getControl();
+		}
+
+		if (target instanceof IDecoratingObservable) {
+			IObservable decorated = ((IDecoratingObservable) target)
+					.getDecorated();
+			Control control = findControl(decorated);
+			if (control != null)
+				return control;
+		}
+
+		if (target instanceof IObserving) {
+			Object observed = ((IObserving) target).getObserved();
+			if (observed instanceof IObservable)
+				return findControl((IObservable) observed);
+		}
+
+		return null;
+	}
+
+	private void statusChanged(IStatus status) {
+		for (Iterator it = targetDecorations.iterator(); it.hasNext();) {
+			TargetDecoration targetDecoration = (TargetDecoration) it.next();
+			ControlDecoration decoration = targetDecoration.decoration;
+			updater.update(decoration, status);
+		}
+	}
+
+	/**
+	 * Disposes this ControlDecorationSupport, including all control decorations
+	 * managed by it. A ControlDecorationSupport is automatically disposed when
+	 * its target ValidationStatusProvider is disposed.
+	 */
+	public void dispose() {
+		if (validationStatus != null) {
+			validationStatus.removeDisposeListener(disposeListener);
+			validationStatus.removeValueChangeListener(statusChangeListener);
+			validationStatus = null;
+		}
+
+		if (targets != null) {
+			targets.removeDisposeListener(disposeListener);
+			targets.removeListChangeListener(targetsChangeListener);
+			targets = null;
+		}
+
+		disposeListener = null;
+		statusChangeListener = null;
+		targetsChangeListener = null;
+
+		if (targetDecorations != null) {
+			for (Iterator it = targetDecorations.iterator(); it.hasNext();) {
+				TargetDecoration targetDecoration = (TargetDecoration) it
+						.next();
+				targetDecoration.decoration.dispose();
+			}
+			targetDecorations.clear();
+			targetDecorations = null;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/fieldassist/ControlDecorationUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/fieldassist/ControlDecorationUpdater.java
new file mode 100644
index 0000000..db2add3
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/fieldassist/ControlDecorationUpdater.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 268472)
+ *     Matthew Hall - bug 300953
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.fieldassist;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Controls the appearance of a ControlDecoration managed by a
+ * ControlDecorationSupport.
+ * 
+ * @since 1.4
+ */
+public class ControlDecorationUpdater {
+	/**
+	 * Updates the visibility, image, and description text of the given
+	 * ControlDecoration to represent the given status.
+	 * 
+	 * @param decoration
+	 *            the ControlDecoration to update
+	 * @param status
+	 *            the status to be displayed by the decoration
+	 */
+	protected void update(ControlDecoration decoration, IStatus status) {
+		if (status == null || status.isOK()) {
+			decoration.hide();
+		} else {
+			decoration.setImage(getImage(status));
+			decoration.setDescriptionText(getDescriptionText(status));
+			decoration.show();
+		}
+	}
+
+	/**
+	 * Returns the description text to show in a ControlDecoration for the given
+	 * status. The default implementation of this method returns
+	 * status.getMessage().
+	 * 
+	 * @param status
+	 *            the status object.
+	 * @return the description text to show in a ControlDecoration for the given
+	 *         status.
+	 */
+	protected String getDescriptionText(IStatus status) {
+		return status == null ? "" : status.getMessage(); //$NON-NLS-1$
+	}
+
+	/**
+	 * Returns an image to display in a ControlDecoration which is appropriate
+	 * for the given status. The default implementation of this method returns
+	 * an image according to <code>status.getSeverity()</code>:
+	 * <ul>
+	 * <li>IStatus.OK => No image
+	 * <li>IStatus.INFO => FieldDecorationRegistry.DEC_INFORMATION
+	 * <li>IStatus.WARNING => FieldDecorationRegistry.DEC_WARNING
+	 * <li>IStatus.ERROR => FieldDecorationRegistry.DEC_ERROR
+	 * <li>IStatus.CANCEL => FieldDecorationRegistry.DEC_ERROR
+	 * <li>Other => No image
+	 * </ul>
+	 * 
+	 * @param status
+	 *            the status object.
+	 * @return an image to display in a ControlDecoration which is appropriate
+	 *         for the given status.
+	 */
+	protected Image getImage(IStatus status) {
+		if (status == null)
+			return null;
+
+		String fieldDecorationID = null;
+		switch (status.getSeverity()) {
+		case IStatus.INFO:
+			fieldDecorationID = FieldDecorationRegistry.DEC_INFORMATION;
+			break;
+		case IStatus.WARNING:
+			fieldDecorationID = FieldDecorationRegistry.DEC_WARNING;
+			break;
+		case IStatus.ERROR:
+		case IStatus.CANCEL:
+			fieldDecorationID = FieldDecorationRegistry.DEC_ERROR;
+			break;
+		}
+
+		FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault()
+				.getFieldDecoration(fieldDecorationID);
+		return fieldDecoration == null ? null : fieldDecoration.getImage();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/preference/PreferencePageSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/preference/PreferencePageSupport.java
new file mode 100644
index 0000000..34dca0a
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/preference/PreferencePageSupport.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 239900)
+ *******************************************************************************/
+package org.eclipse.jface.databinding.preference;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.dialog.DialogPageSupport;
+import org.eclipse.jface.preference.PreferencePage;
+
+/**
+ * Connects the validation result from the given data binding context to the
+ * given preference page, updating the preference page's valid state and its
+ * error message accordingly.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * @since 1.3
+ */
+public class PreferencePageSupport extends DialogPageSupport {
+	private PreferencePageSupport(PreferencePage preferencePage,
+			DataBindingContext dbc) {
+		super(preferencePage, dbc);
+	}
+
+	/**
+	 * Connect the validation result from the given data binding context to the
+	 * given preference page. Upon creation, the preference page support will
+	 * use the context's validation result to determine whether the page is
+	 * valid. The page's error message will not be set at this time ensuring
+	 * that the preference page does not show an error right away. Upon any
+	 * validation result change, {@link PreferencePage#setValid(boolean)} will
+	 * be called reflecting the new validation result, and the preference page's
+	 * error message will be updated according to the current validation result.
+	 * 
+	 * @param preferencePage
+	 * @param dbc
+	 * @return an instance of PreferencePageSupport
+	 */
+	public static PreferencePageSupport create(PreferencePage preferencePage,
+			DataBindingContext dbc) {
+		return new PreferencePageSupport(preferencePage, dbc);
+	}
+
+	protected void handleStatusChanged() {
+		super.handleStatusChanged();
+		boolean valid = true;
+		if (currentStatusStale) {
+			valid = false;
+		} else if (currentStatus != null) {
+			valid = !currentStatus.matches(IStatus.ERROR | IStatus.CANCEL);
+		}
+		((PreferencePage) getDialogPage()).setValid(valid);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservable.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservable.java
new file mode 100644
index 0000000..f3a74f8
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservable.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+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 extends IObservable {
+	
+	/**
+	 * Returns the widget of this observable
+	 * 
+	 * @return the widget
+	 */
+	public Widget getWidget();
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableList.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableList.java
new file mode 100644
index 0000000..63f9c3f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableList.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+
+/**
+ * {@link IObservableList} observing an SWT widget.
+ * 
+ * @since 1.3
+ */
+public interface ISWTObservableList extends ISWTObservable, IObservableList {
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableValue.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableValue.java
new file mode 100644
index 0000000..d25ba3a
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableValue.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * {@link IObservableValue} observing an SWT widget.
+ * 
+ * @since 1.1
+ *
+ */
+public interface ISWTObservableValue extends ISWTObservable, IObservableValue {
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/IWidgetListProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/IWidgetListProperty.java
new file mode 100644
index 0000000..2f38284
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/IWidgetListProperty.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * {@link IListProperty} for observing an SWT Widget
+ * 
+ * @since 1.3
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IWidgetListProperty extends IListProperty {
+	/**
+	 * Returns an {@link ISWTObservableList} observing this list property on the
+	 * given widget
+	 * 
+	 * @param widget
+	 *            the source widget
+	 * @return an observable list observing this list property on the given
+	 *         widget
+	 */
+	public ISWTObservableList observe(Widget widget);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/IWidgetValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/IWidgetValueProperty.java
new file mode 100644
index 0000000..2d0a3fe
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/IWidgetValueProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * {@link IValueProperty} for observing an SWT Widget
+ * 
+ * @since 1.3
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IWidgetValueProperty extends IValueProperty {
+	/**
+	 * Returns an {@link ISWTObservableValue} observing this value property on
+	 * the given widget
+	 * 
+	 * @param widget
+	 *            the source widget
+	 * @return an observable value observing this value property on the given
+	 *         widget
+	 */
+	public ISWTObservableValue observe(Widget widget);
+
+	/**
+	 * Returns an {@link ISWTObservableValue} observing this value property on
+	 * the given widget, which delays notification of value changes until at
+	 * least <code>delay</code> milliseconds have elapsed since that last change
+	 * event, or until a FocusOut event is received from the widget (whichever
+	 * happens first).
+	 * <p>
+	 * This method is equivalent to
+	 * <code>SWTObservables.observeDelayedValue(delay, observe(widget))</code>.
+	 * 
+	 * @param delay
+	 *            the delay in milliseconds.
+	 * @param widget
+	 *            the source widget
+	 * @return an observable value observing this value property on the given
+	 *         widget, and which delays change notifications for
+	 *         <code>delay</code> milliseconds.
+	 */
+	public ISWTObservableValue observeDelayed(int delay, Widget widget);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/SWTObservables.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/SWTObservables.java
new file mode 100644
index 0000000..1aeefc7
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/SWTObservables.java
@@ -0,0 +1,588 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 180746, 207844, 245647, 248621, 232917, 194734,
+ *                    195222, 256543, 213893, 262320, 264286, 266563
+ *     Michael Krauter - bug 180223
+ *     Boris Bokowski - bug 245647
+ *     Tom Schindl - bug 246462
+ *******************************************************************************/
+package org.eclipse.jface.databinding.swt;
+
+import java.util.ArrayList;
+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.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IVetoableValue;
+import org.eclipse.core.databinding.observable.value.ValueChangingEvent;
+import org.eclipse.jface.internal.databinding.swt.SWTDelayedObservableValueDecorator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * 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(final Display display) {
+		synchronized (realms) {
+			for (Iterator it = realms.iterator(); it.hasNext();) {
+				DisplayRealm displayRealm = (DisplayRealm) it.next();
+				if (displayRealm.display == 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
+	 * elapsed since the last change event, or until a FocusOut event is
+	 * received from the underlying widget (whichever happens first). This
+	 * observable helps to boost performance in situations where an observable
+	 * has computationally expensive listeners (e.g. changing filters in a
+	 * viewer) or many dependencies (master fields with multiple detail fields).
+	 * A common use of this observable is to delay validation of user input
+	 * until the user stops typing in a UI field.
+	 * <p>
+	 * To notify about pending changes, the returned observable fires a stale
+	 * event when the wrapped observable value fires a change event, and remains
+	 * stale until the delay has elapsed and the value change is fired. A call
+	 * to {@link IObservableValue#getValue() getValue()} while a value change is
+	 * pending will fire the value change immediately, short-circuiting the
+	 * delay.
+	 * <p>
+	 * Note that this observable will not forward {@link ValueChangingEvent}
+	 * events from a wrapped {@link IVetoableValue}.
+	 * 
+	 * @param delay
+	 *            the delay in milliseconds
+	 * @param observable
+	 *            the observable being delayed
+	 * @return an observable which delays notification of value change events
+	 *         from <code>observable</code> until <code>delay</code>
+	 *         milliseconds have elapsed since the last change event.
+	 * 
+	 * @since 1.2
+	 */
+	public static ISWTObservableValue observeDelayedValue(int delay,
+			ISWTObservableValue observable) {
+		return new SWTDelayedObservableValueDecorator(
+				Observables.observeDelayedValue(delay, observable),
+				observable.getWidget());
+	}
+
+	/**
+	 * Returns an observable value tracking the enabled state of the given
+	 * widget. The supported types are:
+	 * <ul>
+	 * <li>org.eclipse.swt.widgets.Control</li>
+	 * <li>org.eclipse.swt.widgets.Menu</li>
+	 * <li>org.eclipse.swt.widgets.MenuItem</li>
+	 * <li>org.eclipse.swt.widgets.ScrollBar</li>
+	 * <li>org.eclipse.swt.widgets.ToolItem</li>
+	 * </ul>
+	 * 
+	 * @param widget
+	 * @return an observable value tracking the enabled state of the given
+	 *         widget.
+	 * @since 1.5
+	 */
+	public static ISWTObservableValue observeEnabled(Widget widget) {
+		return WidgetProperties.enabled().observe(widget);
+	}
+
+	/**
+	 * Returns an observable value tracking the enabled state of the given
+	 * control
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the enabled state of the given
+	 *         control
+	 */
+	public static ISWTObservableValue observeEnabled(Control control) {
+		return observeEnabled((Widget) control);
+	}
+
+	/**
+	 * Returns an observable value tracking the visible state of the given
+	 * control
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the visible state of the given
+	 *         control
+	 */
+	public static ISWTObservableValue observeVisible(Control control) {
+		return WidgetProperties.visible().observe(control);
+	}
+
+	/**
+	 * Returns an observable tracking the tooltip text of the given item. The
+	 * supported types are:
+	 * <ul>
+	 * <li>org.eclipse.swt.widgets.Control</li>
+	 * <li>org.eclipse.swt.custom.CTabItem</li>
+	 * <li>org.eclipse.swt.widgets.TabItem</li>
+	 * <li>org.eclipse.swt.widgets.TableColumn</li>
+	 * <li>org.eclipse.swt.widgets.ToolItem</li>
+	 * <li>org.eclipse.swt.widgets.TrayItem</li>
+	 * <li>org.eclipse.swt.widgets.TreeColumn</li>
+	 * </ul>
+	 * 
+	 * @param widget
+	 * @return an observable value tracking the tooltip text of the given item
+	 * 
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeTooltipText(Widget widget) {
+		return WidgetProperties.tooltipText().observe(widget);
+	}
+
+	/**
+	 * Returns an observable value tracking the tooltip text of the given
+	 * control
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the tooltip text of the given
+	 *         control
+	 */
+	public static ISWTObservableValue observeTooltipText(Control control) {
+		return observeTooltipText((Widget) control);
+	}
+
+	/**
+	 * 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.MenuItem (since 1.5)</li>
+	 * <li>org.eclipse.swt.widgets.Scale</li>
+	 * </ul>
+	 * 
+	 * @param widget
+	 * @return observable value
+	 * @throws IllegalArgumentException
+	 *             if <code>control</code> type is unsupported
+	 * @since 1.5
+	 */
+	public static ISWTObservableValue observeSelection(Widget widget) {
+		return WidgetProperties.selection().observe(widget);
+	}
+
+	/**
+	 * Returns an observable observing the selection attribute of the provided
+	 * <code>control</code>. The supported types are:
+	 * <ul>
+	 * <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>
+	 * <li>org.eclipse.swt.widgets.Slider (since 1.5)</li>
+	 * <li>org.eclipse.swt.widgets.Spinner</li>
+	 * </ul>
+	 * 
+	 * @param control
+	 * @return observable value
+	 * @throws IllegalArgumentException
+	 *             if <code>control</code> type is unsupported
+	 */
+	public static ISWTObservableValue observeSelection(Control control) {
+		return observeSelection((Widget) control);
+	}
+
+	/**
+	 * 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.Slider (since 1.5)</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) {
+		return WidgetProperties.minimum().observe(control);
+	}
+
+	/**
+	 * 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.Slider (since 1.5)</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) {
+		return WidgetProperties.maximum().observe(control);
+	}
+
+	/**
+	 * 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>
+	 * <li>org.eclipse.swt.custom.StyledText (as of 1.3)</li>
+	 * </ul>
+	 * 
+	 * @param control
+	 * @param events
+	 *            array of SWT event types to register for change events. May
+	 *            include {@link SWT#None}, {@link SWT#Modify},
+	 *            {@link SWT#FocusOut} or {@link SWT#DefaultSelection}.
+	 * @return observable value
+	 * @throws IllegalArgumentException
+	 *             if <code>control</code> type is unsupported
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeText(Control control, int[] events) {
+		return WidgetProperties.text(events).observe(control);
+	}
+
+	/**
+	 * 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>
+	 * <li>org.eclipse.swt.custom.StyledText (as of 1.3)</li>
+	 * </ul>
+	 * 
+	 * @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) {
+		return WidgetProperties.text(event).observe(control);
+	}
+
+	/**
+	 * Returns an observable observing the text attribute of the provided
+	 * <code>widget</code>. The supported types are:
+	 * <ul>
+	 * <li>org.eclipse.swt.widgets.Button (as of 1.3)</li>
+	 * <li>org.eclipse.swt.custom.CCombo</li>
+	 * <li>org.eclipse.swt.custom.CLabel</li>
+	 * <li>org.eclipse.swt.widgets.Combo</li>
+	 * <li>org.eclipse.swt.widgets.Item</li>
+	 * <li>org.eclipse.swt.widgets.Label</li>
+	 * <li>org.eclipse.swt.widgets.Link (as of 1.2)</li>
+	 * <li>org.eclipse.swt.widgets.Shell</li>
+	 * <li>org.eclipse.swt.widgets.StyledText (as of 1.3)</li>
+	 * <li>org.eclipse.swt.widgets.Text (as of 1.3)</li>
+	 * </ul>
+	 * 
+	 * @param widget
+	 * @return observable value
+	 * @throws IllegalArgumentException
+	 *             if the type of <code>widget</code> is unsupported
+	 * 
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeText(Widget widget) {
+		return WidgetProperties.text().observe(widget);
+	}
+
+	/**
+	 * Returns an observable observing the text attribute of the provided
+	 * <code>control</code>. The supported types are:
+	 * <ul>
+	 * <li>org.eclipse.swt.widgets.Button (as of 1.3)</li>
+	 * <li>org.eclipse.swt.custom.CCombo</li>
+	 * <li>org.eclipse.swt.custom.CLabel</li>
+	 * <li>org.eclipse.swt.widgets.Combo</li>
+	 * <li>org.eclipse.swt.widgets.Label</li>
+	 * <li>org.eclipse.swt.widgets.Link (as of 1.2)</li>
+	 * <li>org.eclipse.swt.widgets.Shell</li>
+	 * <li>org.eclipse.swt.custom.StyledText (as of 1.3)</li>
+	 * <li>org.eclipse.swt.widgets.Text (as of 1.3)</li>
+	 * </ul>
+	 * 
+	 * @param control
+	 * @return observable value
+	 * @throws IllegalArgumentException
+	 *             if <code>control</code> type is unsupported
+	 */
+	public static ISWTObservableValue observeText(Control control) {
+		return observeText((Widget) control);
+	}
+
+	/**
+	 * Returns an observable observing the message attribute of the provided
+	 * <code>widget</code>. the supported types are:
+	 * <ul>
+	 * <li>org.eclipse.swt.widgets.Text</li>
+	 * <li>org.eclipse.swt.widgets.ToolTip</li>
+	 * <ul>
+	 * 
+	 * @param widget
+	 * @return an observable observing the message attribute of the provided
+	 *         <code>widget</code>.
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeMessage(Widget widget) {
+		return WidgetProperties.message().observe(widget);
+	}
+
+	/**
+	 * Returns an observable observing the image attribute of the provided
+	 * <code>widget</code>. The supported types are:
+	 * <ul>
+	 * <li>org.eclipse.swt.widgets.Button</li>
+	 * <li>org.eclipse.swt.custom.CLabel</li>
+	 * <li>org.eclipse.swt.widgets.Item</li>
+	 * <li>org.eclipse.swt.widgets.Label</li>
+	 * </ul>
+	 * 
+	 * @param widget
+	 * @return observable value
+	 * @throws IllegalArgumentException
+	 *             if <code>widget</code> type is unsupported
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeImage(Widget widget) {
+		return WidgetProperties.image().observe(widget);
+	}
+
+	/**
+	 * 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) {
+		return WidgetProperties.items().observe(control);
+	}
+
+	/**
+	 * 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) {
+		return WidgetProperties.singleSelectionIndex().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the foreground color of the given
+	 * control
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the foreground color of the given
+	 *         control
+	 */
+	public static ISWTObservableValue observeForeground(Control control) {
+		return WidgetProperties.foreground().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the background color of the given
+	 * control
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the background color of the given
+	 *         control
+	 */
+	public static ISWTObservableValue observeBackground(Control control) {
+		return WidgetProperties.background().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the font of the given control.
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the font of the given control
+	 */
+	public static ISWTObservableValue observeFont(Control control) {
+		return WidgetProperties.font().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the size of the given control.
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the size of the given control
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeSize(Control control) {
+		return WidgetProperties.size().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the location of the given control.
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the location of the given control
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeLocation(Control control) {
+		return WidgetProperties.location().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the focus of the given control.
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the focus of the given control
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeFocus(Control control) {
+		return WidgetProperties.focused().observe(control);
+	}
+
+	/**
+	 * Returns an observable value tracking the bounds of the given control.
+	 * 
+	 * @param control
+	 *            the control to observe
+	 * @return an observable value tracking the bounds of the given control
+	 * @since 1.3
+	 */
+	public static ISWTObservableValue observeBounds(Control control) {
+		return WidgetProperties.bounds().observe(control);
+	}
+
+	/**
+	 * 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) {
+		return WidgetProperties.editable().observe(control);
+	}
+
+	private static class DisplayRealm extends Realm {
+		private Display display;
+
+		/**
+		 * @param display
+		 */
+		private DisplayRealm(Display display) {
+			this.display = display;
+		}
+
+		public boolean isCurrent() {
+			return Display.getCurrent() == display;
+		}
+
+		public void asyncExec(final Runnable runnable) {
+			Runnable safeRunnable = new Runnable() {
+				public void run() {
+					safeRun(runnable);
+				}
+			};
+			if (!display.isDisposed()) {
+				display.asyncExec(safeRunnable);
+			}
+		}
+
+		public void timerExec(int milliseconds, final Runnable runnable) {
+			if (!display.isDisposed()) {
+				Runnable safeRunnable = new Runnable() {
+					public void run() {
+						safeRun(runnable);
+					}
+				};
+				display.timerExec(milliseconds, safeRunnable);
+			}
+		}
+
+		public int hashCode() {
+			return (display == null) ? 0 : display.hashCode();
+		}
+
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			final DisplayRealm other = (DisplayRealm) obj;
+			if (display == null) {
+				if (other.display != null)
+					return false;
+			} else if (!display.equals(other.display))
+				return false;
+			return true;
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetListProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetListProperty.java
new file mode 100644
index 0000000..2b8775b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetListProperty.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 263413, 264286
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+import org.eclipse.jface.internal.databinding.swt.SWTObservableListDecorator;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Abstract list property implementation for {@link Widget} properties. This
+ * class implements some basic behavior that widget properties are generally
+ * expected to have, namely:
+ * <ul>
+ * <li>Calling {@link #observe(Object)} should create the observable on the
+ * display realm of the widget, rather than the current default realm
+ * <li>All <code>observe()</code> methods should return an
+ * {@link ISWTObservable}
+ * </ul>
+ * 
+ * @since 1.3
+ */
+public abstract class WidgetListProperty extends SimpleListProperty implements
+		IWidgetListProperty {
+	public IObservableList observe(Object source) {
+		if (source instanceof Widget) {
+			return observe((Widget) source);
+		}
+		return super.observe(source);
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		return new SWTObservableListDecorator(super.observe(realm, source),
+				(Widget) source);
+	}
+
+	public ISWTObservableList observe(Widget widget) {
+		return (ISWTObservableList) observe(SWTObservables.getRealm(widget
+				.getDisplay()), widget);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetProperties.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetProperties.java
new file mode 100644
index 0000000..e9b9dc5
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetProperties.java
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 256543, 213893, 262320, 262946, 264286, 266563,
+ *                    169876
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.jface.internal.databinding.swt.ControlBackgroundProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlBoundsProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlFocusedProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlFontProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlForegroundProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlLocationProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlSizeProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlVisibleProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetEditableProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetEnabledProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetImageProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetItemsProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetMaximumProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetMessageProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetMinimumProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetSingleSelectionIndexProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetTextProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetTextWithEventsProperty;
+import org.eclipse.jface.internal.databinding.swt.WidgetTooltipTextProperty;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.ToolTip;
+import org.eclipse.swt.widgets.TrayItem;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * A factory for creating properties of SWT {@link Widget widgets}.
+ * 
+ * @since 1.3
+ */
+public class WidgetProperties {
+	/**
+	 * Returns a value property for observing the background color of a
+	 * {@link Control}.
+	 * 
+	 * @return a value property for observing the background color of a
+	 *         {@link Control}.
+	 */
+	public static IWidgetValueProperty background() {
+		return new ControlBackgroundProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the bounds of a {@link Control}.
+	 * 
+	 * @return a value property for observing the bounds of a {@link Control}.
+	 */
+	public static IWidgetValueProperty bounds() {
+		return new ControlBoundsProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the editable state of a
+	 * {@link Text}.
+	 * 
+	 * @return a value property for observing the editable state of a
+	 *         {@link Text}.
+	 */
+	public static IWidgetValueProperty editable() {
+		return new WidgetEditableProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the enablement state of a
+	 * {@link Control}, {@link Menu} (since 1.5), {@link MenuItem} (since 1.5),
+	 * {@link ScrollBar} (since 1.5) or {@link ToolItem} (since 1.5).
+	 * 
+	 * @return a value property for observing the enablement state of a
+	 *         {@link Control}, {@link Menu}, {@link MenuItem},
+	 *         {@link ScrollBar} or {@link ToolItem}.
+	 */
+	public static IWidgetValueProperty enabled() {
+		return new WidgetEnabledProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the focus state of a
+	 * {@link Control}.
+	 * 
+	 * @return a value property for observing the focus state of a
+	 *         {@link Control}.
+	 */
+	public static IWidgetValueProperty focused() {
+		return new ControlFocusedProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the font of a {@link Control}.
+	 * 
+	 * @return a value property for observing the font of a {@link Control}.
+	 */
+	public static IWidgetValueProperty font() {
+		return new ControlFontProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the foreground color of a
+	 * {@link Control}.
+	 * 
+	 * @return a value property for observing the foreground color of a
+	 *         {@link Control}.
+	 */
+	public static IWidgetValueProperty foreground() {
+		return new ControlForegroundProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the image of a {@link Button},
+	 * {@link CLabel}, {@link Item} or {@link Label}.
+	 * 
+	 * @return a value property for observing the image of a {@link Button},
+	 *         {@link CLabel}, {@link Item} or {@link Label}.
+	 */
+	public static IWidgetValueProperty image() {
+		return new WidgetImageProperty();
+	}
+
+	/**
+	 * Returns a list property for observing the items of a {@link CCombo},
+	 * {@link Combo} or {@link List}.
+	 * 
+	 * @return a list property for observing the items of a {@link CCombo},
+	 *         {@link Combo} or {@link List}.
+	 */
+	public static IWidgetListProperty items() {
+		return new WidgetItemsProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the location of a {@link Control}.
+	 * 
+	 * @return a value property for observing the location of a {@link Control}.
+	 */
+	public static IWidgetValueProperty location() {
+		return new ControlLocationProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the maximum value of a
+	 * {@link Scale}, {@link Slider} (since 1.5) or {@link Spinner}.
+	 * 
+	 * @return a value property for observing the maximum value of a
+	 *         {@link Scale}, {@link Slider} (since 1.5) or {@link Spinner}.
+	 */
+	public static IWidgetValueProperty maximum() {
+		return new WidgetMaximumProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the message of a {@link Text} or
+	 * {@link ToolTip}.
+	 * 
+	 * @return a value property for observing the message of a {@link Text} or
+	 *         {@link ToolTip}.
+	 */
+	public static IWidgetValueProperty message() {
+		return new WidgetMessageProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the minimum value of a
+	 * {@link Scale}, {@link Slider} (since 1.5) or {@link Spinner}.
+	 * 
+	 * @return a value property for observing the minimum value of a
+	 *         {@link Scale}, {@link Slider} (since 1.5) or {@link Spinner}.
+	 */
+	public static IWidgetValueProperty minimum() {
+		return new WidgetMinimumProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the selection state of a
+	 * {@link Button}, {@link CCombo}, {@link Combo}, {@link DateTime},
+	 * {@link List}, {@link MenuItem} (since 1.5), {@link Scale}, {@link Slider}
+	 * (since 1.5) or {@link Spinner}.
+	 * 
+	 * @return a value property for observing the selection state of a
+	 *         {@link Button}, {@link CCombo}, {@link Combo}, {@link DateTime},
+	 *         {@link List}, {@link MenuItem}, {@link Scale}, {@link Slider} or
+	 *         {@link Spinner}.
+	 */
+	public static IWidgetValueProperty selection() {
+		return new WidgetSelectionProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the single selection index of a
+	 * {@link CCombo}, {@link Combo}, {@link List} or {@link Table}.
+	 * 
+	 * @return a value property for the single selection index of a SWT Combo.
+	 */
+	public static IWidgetValueProperty singleSelectionIndex() {
+		return new WidgetSingleSelectionIndexProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the size of a {@link Control}.
+	 * 
+	 * @return a value property for observing the size of a {@link Control}.
+	 */
+	public static IWidgetValueProperty size() {
+		return new ControlSizeProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the text of a {@link Button},
+	 * {@link CCombo}, {@link CLabel}, {@link Combo}, {@link Item},
+	 * {@link Label}, {@link Link}, {@link Shell}, {@link StyledText} or
+	 * {@link Text}.
+	 * 
+	 * @return a value property for observing the text of a {@link Button},
+	 *         {@link CCombo}, {@link CLabel}, {@link Combo}, {@link Item},
+	 *         {@link Label}, {@link Link}, {@link Shell}, {@link StyledText} or
+	 *         {@link Text}.
+	 */
+	public static IWidgetValueProperty text() {
+		return new WidgetTextProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the text of a {@link StyledText}
+	 * or {@link Text}.
+	 * 
+	 * @param event
+	 *            the SWT event type to register for change events. May be
+	 *            {@link SWT#None}, {@link SWT#Modify}, {@link SWT#FocusOut} or
+	 *            {@link SWT#DefaultSelection}.
+	 * 
+	 * @return a value property for observing the text of a {@link StyledText}
+	 *         or {@link Text}.
+	 */
+	public static IWidgetValueProperty text(final int event) {
+		return text(new int[] { event });
+	}
+
+	/**
+	 * Returns a value property for observing the text of a {@link StyledText}
+	 * or {@link Text}.
+	 * 
+	 * @param events
+	 *            array of SWT event types to register for change events. May
+	 *            include {@link SWT#None}, {@link SWT#Modify},
+	 *            {@link SWT#FocusOut} or {@link SWT#DefaultSelection}.
+	 * 
+	 * @return a value property for observing the text of a {@link StyledText}
+	 *         or {@link Text}.
+	 */
+	public static IWidgetValueProperty text(int[] events) {
+		return new WidgetTextWithEventsProperty((int[]) events.clone());
+	}
+
+	/**
+	 * Returns a value property for observing the tooltip text of a
+	 * {@link CTabItem}, {@link Control}, {@link TabItem}, {@link TableColumn},
+	 * {@link ToolItem}, {@link TrayItem} or {@link TreeColumn}.
+	 * 
+	 * @return a value property for observing the tooltip text of a
+	 *         {@link CTabItem}, {@link Control}, {@link TabItem},
+	 *         {@link TableColumn}, {@link ToolItem}, {@link TrayItem} or
+	 *         {@link TreeColumn}.
+	 */
+	public static IWidgetValueProperty tooltipText() {
+		return new WidgetTooltipTextProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the visibility state of a
+	 * {@link Control}.
+	 * 
+	 * @return a value property for observing the visibility state of a
+	 *         {@link Control}.
+	 */
+	public static IWidgetValueProperty visible() {
+		return new ControlVisibleProperty();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetValueProperty.java
new file mode 100644
index 0000000..00cffa7
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/WidgetValueProperty.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 263413, 264286, 265561, 262287, 281723
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.jface.internal.databinding.swt.SWTObservableValueDecorator;
+import org.eclipse.jface.internal.databinding.swt.WidgetListener;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Abstract value property implementation for {@link Widget} properties. This
+ * class implements some basic behavior that widget properties are generally
+ * expected to have, namely:
+ * <ul>
+ * <li>Calling {@link #observe(Object)} should create the observable on the
+ * display realm of the widget, rather than the current default realm
+ * <li>All <code>observe()</code> methods should return an
+ * {@link ISWTObservableValue}
+ * </ul>
+ * This class also provides a default widget listener implementation using SWT's
+ * {@link Listener untyped listener API}. Subclasses may pass one or more SWT
+ * event type constants to the super constructor to indicate which events signal
+ * a property change.
+ * 
+ * @since 1.3
+ */
+public abstract class WidgetValueProperty extends SimpleValueProperty implements
+		IWidgetValueProperty {
+	private int[] changeEvents;
+	private int[] staleEvents;
+
+	/**
+	 * Constructs a WidgetValueProperty which does not listen for any SWT
+	 * events.
+	 */
+	protected WidgetValueProperty() {
+		this(null, null);
+	}
+
+	/**
+	 * Constructs a WidgetValueProperty with the specified SWT event type
+	 * 
+	 * @param changeEvent
+	 *            SWT event type constant of the event that signifies a property
+	 *            change.
+	 */
+	protected WidgetValueProperty(int changeEvent) {
+		this(new int[] { changeEvent }, null);
+	}
+
+	/**
+	 * Constructs a WidgetValueProperty with the specified SWT event type(s).
+	 * 
+	 * @param changeEvents
+	 *            array of SWT event type constants of the events that signify a
+	 *            property change.
+	 */
+	protected WidgetValueProperty(int[] changeEvents) {
+		this(changeEvents, null);
+	}
+
+	/**
+	 * Constructs a WidgetValueProperty with the specified SWT event types.
+	 * 
+	 * @param changeEvents
+	 *            array of SWT event type constants of the events that signify a
+	 *            property change.
+	 * @param staleEvents
+	 *            array of SWT event type constants of the events that signify a
+	 *            property became stale.
+	 */
+	public WidgetValueProperty(int[] changeEvents, int[] staleEvents) {
+		this.changeEvents = changeEvents;
+		this.staleEvents = staleEvents;
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		if (changeEvents == null && staleEvents == null)
+			return null;
+		return new WidgetListener(this, listener, changeEvents, staleEvents);
+	}
+
+	public IObservableValue observe(Object source) {
+		if (source instanceof Widget) {
+			return observe((Widget) source);
+		}
+		return super.observe(source);
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		return wrapObservable(super.observe(realm, source), (Widget) source);
+	}
+
+	protected ISWTObservableValue wrapObservable(IObservableValue observable,
+			Widget widget) {
+		return new SWTObservableValueDecorator(observable, widget);
+	}
+
+	public ISWTObservableValue observe(Widget widget) {
+		return (ISWTObservableValue) observe(SWTObservables.getRealm(widget
+				.getDisplay()), widget);
+	}
+
+	public ISWTObservableValue observeDelayed(int delay, Widget widget) {
+		return SWTObservables.observeDelayedValue(delay, observe(widget));
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/package.html b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/package.html
new file mode 100644
index 0000000..139ca4f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/package.html
@@ -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>
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/util/JFaceProperties.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/util/JFaceProperties.java
new file mode 100644
index 0000000..1119c1e
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/util/JFaceProperties.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.databinding.util;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.internal.databinding.util.JFaceProperty;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+/**
+ * Helper class for providing {@link IObservableValue} instances for properties
+ * of an object that fires property changes events to an
+ * {@link IPropertyChangeListener}.
+ * 
+ * @since 1.3
+ */
+public class JFaceProperties {
+
+	/**
+	 * Returns a property for observing the property of the given model object
+	 * whose getter and setter use the suffix fieldName in the same manner as a
+	 * Java bean and which fires events to an {@link IPropertyChangeListener}
+	 * for the given propertyName when the value of the field changes.
+	 * 
+	 * @param clazz
+	 *            the class defining the getter and setter
+	 * @param fieldName
+	 *            the field name
+	 * @param propertyName
+	 *            the property name
+	 * 
+	 * @return an observable value
+	 */
+	public static IValueProperty value(Class clazz, String fieldName,
+			String propertyName) {
+		return new JFaceProperty(fieldName, propertyName, clazz);
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/CellEditorProperties.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/CellEditorProperties.java
new file mode 100644
index 0000000..7212b83
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/CellEditorProperties.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 234496)
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.internal.databinding.viewers.CellEditorControlProperty;
+import org.eclipse.jface.viewers.CellEditor;
+
+/**
+ * A factory for creating properties of JFace {@link CellEditor cell editors}.
+ * 
+ * @since 1.3
+ */
+public class CellEditorProperties {
+	/**
+	 * Returns a value property for observing the control of a
+	 * {@link CellEditor}.
+	 * 
+	 * @return a value property for observing the control of a
+	 *         {@link CellEditor}.
+	 */
+	public static IValueProperty control() {
+		return new CellEditorControlProperty();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerListProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerListProperty.java
new file mode 100644
index 0000000..81d5cc2
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerListProperty.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * {@link IListProperty} for observing a JFace viewer
+ * 
+ * @since 1.3
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IViewerListProperty extends IListProperty {
+	/**
+	 * Returns an {@link IViewerObservableList} observing this list property on
+	 * the given viewer
+	 * 
+	 * @param viewer
+	 *            the source viewer
+	 * @return an observable list observing this list property on the given
+	 *         viewer
+	 */
+	public IViewerObservableList observe(Viewer viewer);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservable.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservable.java
new file mode 100644
index 0000000..50703cd
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservable.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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 extends IObservable {
+	/**
+	 * Returns the underlying viewer for this observable.
+	 * 
+	 * @return the viewer.
+	 */
+	public Viewer getViewer();
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableList.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableList.java
new file mode 100644
index 0000000..6b3ed4b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableList.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+
+/**
+ * {@link IObservableList} observing a JFace Viewer.
+ * 
+ * @since 1.2
+ * 
+ */
+public interface IViewerObservableList extends IObservableList,
+		IViewerObservable {
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableSet.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableSet.java
new file mode 100644
index 0000000..b8f98e6
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableSet.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * 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)
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+
+/**
+ * {@link IObservableSet} observing a JFace Viewer.
+ * 
+ * @since 1.2
+ * 
+ */
+public interface IViewerObservableSet extends IObservableSet, IViewerObservable {
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableValue.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableValue.java
new file mode 100644
index 0000000..6c145cf
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableValue.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * {@link IObservableValue} observing a JFace Viewer.
+ * 
+ * @since 1.2
+ * 
+ */
+public interface IViewerObservableValue extends IObservableValue,
+		IViewerObservable {
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerSetProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerSetProperty.java
new file mode 100644
index 0000000..fb842bc
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerSetProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * {@link ISetProperty} for observing a JFace viewer
+ * 
+ * @since 1.3
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IViewerSetProperty extends ISetProperty {
+	/**
+	 * Returns an {@link IViewerObservableSet} observing this set property on
+	 * the given viewer
+	 * 
+	 * @param viewer
+	 *            the source viewer
+	 * @return an observable set observing this set property on the given viewer
+	 */
+	public IViewerObservableSet observe(Viewer viewer);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerUpdater.java
new file mode 100644
index 0000000..6f5c555
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerUpdater.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 238296)
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * A strategy interface for updating the elements in a {@link StructuredViewer}.
+ * Many structured viewer classes have similar methods for adding and removing
+ * elements, few of which are defined in common ancestor classes. This interface
+ * serves as a universal adapter for updating the elements in a viewer
+ * 
+ * @since 1.3
+ * @see ObservableListContentProvider#ObservableListContentProvider(IViewerUpdater)
+ * @see ObservableSetContentProvider#ObservableSetContentProvider(IViewerUpdater)
+ */
+public interface IViewerUpdater {
+	/**
+	 * Insert the element into the viewer at the specified position.
+	 * 
+	 * @param element
+	 *            the element to add
+	 * @param position
+	 *            the position of the element
+	 */
+	public 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 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);
+
+	/**
+	 * 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);
+
+	/**
+	 * Adds the elements to the viewer.
+	 * 
+	 * @param elements
+	 *            the elements to add
+	 */
+	public void add(Object[] elements);
+
+	/**
+	 * Removes the elements from the viewer
+	 * 
+	 * @param elements
+	 *            the elements to remove
+	 */
+	public void remove(Object[] elements);
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerValueProperty.java
new file mode 100644
index 0000000..c9f8092
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerValueProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * {@link IValueProperty} for observing a JFace viewer
+ * 
+ * @since 1.3
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IViewerValueProperty extends IValueProperty {
+	/**
+	 * Returns an {@link IViewerObservableValue} observing this value property
+	 * on the given viewer
+	 * 
+	 * @param viewer
+	 *            the source viewer
+	 * @return an observable value observing this value property on the given
+	 *         viewer
+	 */
+	public IViewerObservableValue observe(Viewer viewer);
+
+	/**
+	 * Returns an {@link IViewerObservableValue} observing this value property
+	 * on the given viewer, which delays notification of value changes until at
+	 * least <code>delay</code> milliseconds have elapsed since that last change
+	 * event, or until a FocusOut event is received from the viewer's control
+	 * (whichever happens first).
+	 * <p>
+	 * This method is equivalent to
+	 * <code>ViewersObservables.observeDelayedValue(delay, observe(viewer))</code>.
+	 * 
+	 * @param delay
+	 *            the delay in milliseconds.
+	 * @param viewer
+	 *            the source viewer
+	 * @return an observable value observing this value property on the given
+	 *         viewer, and which delays change notifications for
+	 *         <code>delay</code> milliseconds.
+	 */
+	public IViewerObservableValue observeDelayed(int delay, Viewer viewer);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.java
new file mode 100644
index 0000000..5588a6f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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 extends ViewerLabelProvider {
+
+	private ISetChangeListener listener = new 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 ListeningLabelProvider(IObservableSet itemsThatNeedLabels) {
+		this.items = itemsThatNeedLabels;
+		items.addSetChangeListener(listener);
+		for (Iterator it = items.iterator(); it.hasNext();) {
+			addListenerTo(it.next());
+		}
+	}
+
+	/**
+	 * @param next
+	 */
+	protected abstract void removeListenerFrom(Object next);
+
+	/**
+	 * @param next
+	 */
+	protected abstract void addListenerTo(Object next);
+
+	public void dispose() {
+		for (Iterator iter = items.iterator(); iter.hasNext();) {
+			removeListenerFrom(iter.next());
+		}
+		items.removeSetChangeListener(listener);
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListContentProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListContentProvider.java
new file mode 100644
index 0000000..2345369
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListContentProvider.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 222991, 238296, 226292, 266038,
+ *                    283351
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * @since 1.1
+ */
+public class ObservableListContentProvider implements
+		IStructuredContentProvider {
+	private ObservableCollectionContentProvider impl;
+
+	private static class Impl extends ObservableCollectionContentProvider
+			implements IListChangeListener {
+		private Viewer viewer;
+
+		Impl(IViewerUpdater explicitViewerUpdater) {
+			super(explicitViewerUpdater);
+		}
+
+		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+			this.viewer = viewer;
+			super.inputChanged(viewer, oldInput, newInput);
+		}
+
+		protected void checkInput(Object input) {
+			Assert.isTrue(input instanceof IObservableList,
+					"This content provider only works with input of type IObservableList"); //$NON-NLS-1$
+		}
+
+		protected void addCollectionChangeListener(
+				IObservableCollection collection) {
+			((IObservableList) collection).addListChangeListener(this);
+		}
+
+		protected void removeCollectionChangeListener(
+				IObservableCollection collection) {
+			((IObservableList) collection).removeListChangeListener(this);
+		}
+
+		public void handleListChange(ListChangeEvent event) {
+			if (isViewerDisposed())
+				return;
+
+			// Determine which elements were added and removed
+			final Set knownElementAdditions = ViewerElementSet
+					.withComparer(comparer);
+			final Set knownElementRemovals = ViewerElementSet
+					.withComparer(comparer);
+			final boolean[] suspendRedraw = new boolean[] { false };
+			event.diff.accept(new ListDiffVisitor() {
+				public void handleAdd(int index, Object element) {
+					knownElementAdditions.add(element);
+				}
+
+				public void handleRemove(int index, Object element) {
+					knownElementRemovals.add(element);
+				}
+
+				public void handleMove(int oldIndex, int newIndex,
+						Object element) {
+					suspendRedraw[0] = true;
+					super.handleMove(oldIndex, newIndex, element);
+				}
+
+				public void handleReplace(int index, Object oldElement,
+						Object newElement) {
+					suspendRedraw[0] = true;
+					super.handleReplace(index, oldElement, newElement);
+				}
+			});
+			knownElementAdditions.removeAll(knownElements);
+			knownElementRemovals.removeAll(event.getObservableList());
+
+			knownElements.addAll(knownElementAdditions);
+			if (realizedElements != null) {
+				realizedElements.removeAll(knownElementRemovals);
+			}
+
+			if (suspendRedraw[0])
+				viewer.getControl().setRedraw(false);
+			try {
+				event.diff.accept(new ListDiffVisitor() {
+					public void handleAdd(int index, Object element) {
+						viewerUpdater.insert(element, index);
+					}
+
+					public void handleRemove(int index, Object element) {
+						viewerUpdater.remove(element, index);
+					}
+
+					public void handleReplace(int index, Object oldElement,
+							Object newElement) {
+						viewerUpdater.replace(oldElement, newElement, index);
+					}
+
+					public void handleMove(int oldIndex, int newIndex,
+							Object element) {
+						viewerUpdater.move(element, oldIndex, newIndex);
+					}
+				});
+			} finally {
+				if (suspendRedraw[0])
+					viewer.getControl().setRedraw(true);
+			}
+
+			if (realizedElements != null) {
+				realizedElements.addAll(knownElementAdditions);
+			}
+			knownElements.removeAll(knownElementRemovals);
+		}
+	}
+
+	/**
+	 * Constructs an ObservableListContentProvider. Must be called from the
+	 * display thread.
+	 */
+	public ObservableListContentProvider() {
+		this(null);
+	}
+
+	/**
+	 * Constructs an ObservableListContentProvider with the given viewer
+	 * updater. Must be called from the display thread.
+	 * 
+	 * @param viewerUpdater
+	 *            the viewer updater to use when elements are added, removed,
+	 *            moved or replaced in the input observable list.
+	 * @since 1.3
+	 */
+	public ObservableListContentProvider(IViewerUpdater viewerUpdater) {
+		impl = new Impl(viewerUpdater);
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+		impl.inputChanged(viewer, oldInput, newInput);
+	}
+
+	public Object[] getElements(Object inputElement) {
+		return impl.getElements(inputElement);
+	}
+
+	/**
+	 * Disposes of this content provider. This is called by the viewer when a
+	 * content provider is replaced, or when the viewer itself is disposed.
+	 * <p>
+	 * The viewer should not be updated during this call, as it is in the
+	 * process of being disposed.
+	 * </p>
+	 * <p>
+	 * <em>Note:</em> Data binding content providers become unusable on
+	 * disposal.
+	 * </p>
+	 */
+	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();
+	}
+
+	/**
+	 * Returns the set of known elements which have been realized in the viewer.
+	 * Clients may track this set in order to perform custom actions on elements
+	 * while they are known to be present in the viewer.
+	 * 
+	 * @return the set of known elements which have been realized in the viewer.
+	 * @since 1.3
+	 */
+	public IObservableSet getRealizedElements() {
+		return impl.getRealizedElements();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.java
new file mode 100644
index 0000000..fc94004
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.java
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 - bugs 226765, 222991, 226292, 266038, 283351
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * @since 1.2
+ */
+public class ObservableListTreeContentProvider implements ITreeContentProvider {
+	private final ObservableCollectionTreeContentProvider impl;
+
+	private static class Impl extends ObservableCollectionTreeContentProvider {
+		private Viewer viewer;
+
+		public Impl(IObservableFactory listFactory,
+				TreeStructureAdvisor structureAdvisor) {
+			super(listFactory, structureAdvisor);
+		}
+
+		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+			this.viewer = viewer;
+			super.inputChanged(viewer, oldInput, newInput);
+		}
+
+		private class ListChangeListener implements IListChangeListener {
+			final Object parentElement;
+
+			public ListChangeListener(Object parentElement) {
+				this.parentElement = parentElement;
+			}
+
+			public void handleListChange(ListChangeEvent event) {
+				if (isViewerDisposed())
+					return;
+
+				// Determine which elements are being added and removed
+				final Set localKnownElementAdditions = ViewerElementSet
+						.withComparer(comparer);
+				final Set localKnownElementRemovals = ViewerElementSet
+						.withComparer(comparer);
+				final boolean[] suspendRedraw = new boolean[] { false };
+				event.diff.accept(new ListDiffVisitor() {
+					public void handleAdd(int index, Object element) {
+						localKnownElementAdditions.add(element);
+					}
+
+					public void handleRemove(int index, Object element) {
+						localKnownElementRemovals.add(element);
+					}
+
+					public void handleMove(int oldIndex, int newIndex,
+							Object element) {
+						suspendRedraw[0] = true;
+						// does not affect known elements
+					}
+
+					public void handleReplace(int index, Object oldElement,
+							Object newElement) {
+						suspendRedraw[0] = true;
+						super.handleReplace(index, oldElement, newElement);
+					}
+				});
+				localKnownElementRemovals.removeAll(event.getObservableList());
+
+				Set knownElementAdditions = ViewerElementSet
+						.withComparer(comparer);
+				knownElementAdditions.addAll(localKnownElementAdditions);
+				knownElementAdditions.removeAll(knownElements);
+
+				Set knownElementRemovals = findPendingRemovals(parentElement,
+						localKnownElementRemovals);
+				knownElementRemovals.retainAll(knownElements);
+
+				knownElements.addAll(knownElementAdditions);
+				if (realizedElements != null) {
+					realizedElements.removeAll(knownElementRemovals);
+				}
+
+				for (Iterator it = localKnownElementAdditions.iterator(); it
+						.hasNext();) {
+					getOrCreateNode(it.next()).addParent(parentElement);
+				}
+
+				if (suspendRedraw[0])
+					viewer.getControl().setRedraw(false);
+				try {
+					event.diff.accept(new ListDiffVisitor() {
+						public void handleAdd(int index, Object child) {
+							viewerUpdater.insert(parentElement, child, index);
+						}
+
+						public void handleRemove(int index, Object child) {
+							viewerUpdater.remove(parentElement, child, index);
+						}
+
+						public void handleReplace(int index, Object oldChild,
+								Object newChild) {
+							viewerUpdater.replace(parentElement, oldChild,
+									newChild, index);
+						}
+
+						public void handleMove(int oldIndex, int newIndex,
+								Object child) {
+							viewerUpdater.move(parentElement, child, oldIndex,
+									newIndex);
+						}
+					});
+				} finally {
+					if (suspendRedraw[0])
+						viewer.getControl().setRedraw(true);
+				}
+
+				for (Iterator it = localKnownElementRemovals.iterator(); it
+						.hasNext();) {
+					TreeNode node = getExistingNode(it.next());
+					if (node != null) {
+						node.removeParent(parentElement);
+					}
+				}
+
+				if (realizedElements != null) {
+					realizedElements.addAll(knownElementAdditions);
+				}
+				knownElements.removeAll(knownElementRemovals);
+			}
+		}
+
+		protected IObservablesListener createCollectionChangeListener(
+				Object parentElement) {
+			return new ListChangeListener(parentElement);
+		}
+
+		protected void addCollectionChangeListener(
+				IObservableCollection collection, IObservablesListener listener) {
+			IObservableList list = (IObservableList) collection;
+			IListChangeListener listListener = (IListChangeListener) listener;
+			list.addListChangeListener(listListener);
+		}
+
+		protected void removeCollectionChangeListener(
+				IObservableCollection collection, IObservablesListener listener) {
+			IObservableList list = (IObservableList) collection;
+			IListChangeListener listListener = (IListChangeListener) listener;
+			list.removeListChangeListener(listListener);
+		}
+	}
+
+	/**
+	 * Constructs an ObservableListTreeContentProvider using the given list
+	 * factory. Must be called from the display thread.
+	 * 
+	 * @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 ObservableListTreeContentProvider(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 boolean 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);
+	}
+
+	/**
+	 * Disposes of this content provider. This is called by the viewer when a
+	 * content provider is replaced, or when the viewer itself is disposed.
+	 * <p>
+	 * The viewer should not be updated during this call, as it is in the
+	 * process of being disposed.
+	 * </p>
+	 * <p>
+	 * <em>Note:</em> Data binding content providers become unusable on
+	 * disposal.
+	 * </p>
+	 */
+	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();
+	}
+
+	/**
+	 * Returns the set of known elements which have been realized in the viewer.
+	 * Clients may track this set in order to perform custom actions on elements
+	 * while they are known to be present in the viewer.
+	 * 
+	 * @return the set of known elements which have been realized in the viewer.
+	 * @since 1.3
+	 */
+	public IObservableSet getRealizedElements() {
+		return impl.getRealizedElements();
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapCellLabelProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapCellLabelProvider.java
new file mode 100644
index 0000000..6ef1d31
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapCellLabelProvider.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164247, 164134
+ *     Matthew Hall - bug 302860
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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.CellLabelProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.jface.viewers.ViewerCell;
+
+/**
+ * A label provider based on one or more observable maps that track attributes
+ * that this label provider uses for display. The default behavior is to display
+ * the first attribute's value. Clients may customize by subclassing and
+ * overriding {@link #update(ViewerCell)}.
+ * 
+ * @since 1.3
+ * 
+ */
+public class ObservableMapCellLabelProvider extends CellLabelProvider {
+
+	/**
+	 * Observable maps typically mapping from viewer elements to label values.
+	 * Subclasses may use these maps to provide custom labels.
+	 * 
+	 * @since 1.4
+	 */
+	protected IObservableMap[] attributeMaps;
+
+	private IMapChangeListener mapChangeListener = new IMapChangeListener() {
+		public void handleMapChange(MapChangeEvent event) {
+			Set affectedElements = event.diff.getChangedKeys();
+			LabelProviderChangedEvent newEvent = new LabelProviderChangedEvent(
+					ObservableMapCellLabelProvider.this, affectedElements
+							.toArray());
+			fireLabelProviderChanged(newEvent);
+		}
+	};
+
+	/**
+	 * Creates a new label provider that tracks changes to one attribute.
+	 * 
+	 * @param attributeMap
+	 */
+	public ObservableMapCellLabelProvider(IObservableMap attributeMap) {
+		this(new IObservableMap[] { attributeMap });
+	}
+
+	/**
+	 * Creates a new label provider that tracks changes to more than one
+	 * attribute. This constructor should be used by subclasses that override
+	 * {@link #update(ViewerCell)} and make use of more than one attribute.
+	 * 
+	 * @param attributeMaps
+	 */
+	protected ObservableMapCellLabelProvider(IObservableMap[] attributeMaps) {
+		System.arraycopy(attributeMaps, 0,
+				this.attributeMaps = new IObservableMap[attributeMaps.length],
+				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();
+		this.attributeMaps = null;
+		this.mapChangeListener = null;
+	}
+
+	public void update(ViewerCell cell) {
+		Object element = cell.getElement();
+		Object value = attributeMaps[0].get(element);
+		cell.setText(value == null ? "" : value.toString()); //$NON-NLS-1$
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapLabelProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapLabelProvider.java
new file mode 100644
index 0000000..7e7066c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapLabelProvider.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164247, 164134
+ *     Matthew Hall - bug 302860
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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;
+
+/**
+ * A label provider based on one or more observable maps that track attributes
+ * that this label provider uses for display. Clients may customize by
+ * subclassing and overriding {@link #getColumnText(Object, int)},
+ * {@link #getColumnImage(Object, int)}, for tables or trees with columns, or by
+ * implementing additional mixin interfaces for colors, fonts etc.
+ * 
+ * @since 1.1
+ * 
+ */
+public class ObservableMapLabelProvider extends LabelProvider implements
+		ILabelProvider, ITableLabelProvider {
+
+	/**
+	 * Observable maps typically mapping from viewer elements to label values.
+	 * Subclasses may reference these maps to provide custom labels.
+	 * 
+	 * @since 1.4
+	 */
+	protected IObservableMap[] attributeMaps;
+
+	private IMapChangeListener mapChangeListener = new IMapChangeListener() {
+		public void handleMapChange(MapChangeEvent event) {
+			Set affectedElements = event.diff.getChangedKeys();
+			LabelProviderChangedEvent newEvent = new LabelProviderChangedEvent(
+					ObservableMapLabelProvider.this, affectedElements.toArray());
+			fireLabelProviderChanged(newEvent);
+		}
+	};
+
+	/**
+	 * @param attributeMap
+	 */
+	public ObservableMapLabelProvider(IObservableMap attributeMap) {
+		this(new IObservableMap[] { attributeMap });
+	}
+
+	/**
+	 * @param attributeMaps
+	 */
+	public ObservableMapLabelProvider(IObservableMap[] attributeMaps) {
+		System.arraycopy(attributeMaps, 0,
+				this.attributeMaps = new IObservableMap[attributeMaps.length],
+				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();
+		this.attributeMaps = null;
+		this.mapChangeListener = null;
+	}
+
+	public Image getImage(Object element) {
+		return getColumnImage(element, 0);
+	}
+
+	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 == null ? "" : result.toString(); //$NON-NLS-1$
+		}
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetContentProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetContentProvider.java
new file mode 100644
index 0000000..3240eb2
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetContentProvider.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 222991, 238296, 266038, 283351
+ *******************************************************************************/
+package org.eclipse.jface.databinding.viewers;
+
+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.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * @since 1.1
+ */
+public class ObservableSetContentProvider implements IStructuredContentProvider {
+	private ObservableCollectionContentProvider impl;
+
+	private static class Impl extends ObservableCollectionContentProvider
+			implements ISetChangeListener {
+		protected Impl(IViewerUpdater explicitViewerUpdater) {
+			super(explicitViewerUpdater);
+		}
+
+		protected void checkInput(Object input) {
+			Assert.isTrue(input instanceof IObservableSet,
+					"This content provider only works with input of type IObservableSet"); //$NON-NLS-1$
+		}
+
+		protected void addCollectionChangeListener(
+				IObservableCollection collection) {
+			((IObservableSet) collection).addSetChangeListener(this);
+		}
+
+		protected void removeCollectionChangeListener(
+				IObservableCollection collection) {
+			((IObservableSet) collection).removeSetChangeListener(this);
+		}
+
+		public void handleSetChange(SetChangeEvent event) {
+			if (isViewerDisposed())
+				return;
+
+			Set removals = event.diff.getRemovals();
+			Set additions = event.diff.getAdditions();
+
+			knownElements.addAll(additions);
+			if (realizedElements != null)
+				realizedElements.removeAll(removals);
+
+			viewerUpdater.remove(removals.toArray());
+			viewerUpdater.add(additions.toArray());
+
+			if (realizedElements != null)
+				realizedElements.addAll(additions);
+			knownElements.removeAll(removals);
+		}
+	}
+
+	/**
+	 * Constructs an ObservableSetContentProvider. Must be called from the
+	 * display thread.
+	 */
+	public ObservableSetContentProvider() {
+		this(null);
+	}
+
+	/**
+	 * Constructs an ObservableSetContentProvider with the given viewer updater.
+	 * Must be called from the display thread.
+	 * 
+	 * @param viewerUpdater
+	 *            the viewer updater to use when elements are added or removed
+	 *            from the input observable set.
+	 * @since 1.3
+	 */
+	public ObservableSetContentProvider(IViewerUpdater viewerUpdater) {
+		impl = new Impl(viewerUpdater);
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+		impl.inputChanged(viewer, oldInput, newInput);
+	}
+
+	public Object[] getElements(Object inputElement) {
+		return impl.getElements(inputElement);
+	}
+
+	/**
+	 * Disposes of this content provider. This is called by the viewer when a
+	 * content provider is replaced, or when the viewer itself is disposed.
+	 * <p>
+	 * The viewer should not be updated during this call, as it is in the
+	 * process of being disposed.
+	 * </p>
+	 * <p>
+	 * <em>Note:</em> Data binding content providers become unusable on
+	 * disposal.
+	 * </p>
+	 */
+	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();
+	}
+
+	/**
+	 * Returns the set of known elements which have been realized in the viewer.
+	 * Clients may track this set in order to perform custom actions on elements
+	 * while they are known to be present in the viewer.
+	 * 
+	 * @return the set of known elements which have been realized in the viewer.
+	 * @since 1.3
+	 */
+	public IObservableSet getRealizedElements() {
+		return impl.getRealizedElements();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.java
new file mode 100644
index 0000000..2ca876c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 - bugs 226765, 222991, 266038, 283351
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+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.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 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.
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ * @since 1.2
+ */
+public class ObservableSetTreeContentProvider implements ITreeContentProvider {
+	private final ObservableCollectionTreeContentProvider impl;
+
+	private static class Impl extends ObservableCollectionTreeContentProvider {
+		Impl(IObservableFactory setFactory,
+				TreeStructureAdvisor structureAdvisor) {
+			super(setFactory, structureAdvisor);
+		}
+
+		private class SetChangeListener implements ISetChangeListener {
+			final Object parentElement;
+
+			public SetChangeListener(Object parentElement) {
+				this.parentElement = parentElement;
+			}
+
+			public void handleSetChange(SetChangeEvent event) {
+				if (isViewerDisposed())
+					return;
+
+				Set localAdditions = event.diff.getAdditions();
+				Set localRemovals = event.diff.getRemovals();
+
+				Set knownElementAdditions = ViewerElementSet
+						.withComparer(comparer);
+				knownElementAdditions.addAll(localAdditions);
+				knownElementAdditions.removeAll(knownElements);
+
+				Set knownElementRemovals = findPendingRemovals(parentElement,
+						localRemovals);
+				knownElementRemovals.retainAll(knownElements);
+
+				knownElements.addAll(knownElementAdditions);
+				if (realizedElements != null) {
+					realizedElements.removeAll(knownElementRemovals);
+				}
+
+				for (Iterator iterator = localAdditions.iterator(); iterator
+						.hasNext();) {
+					Object child = iterator.next();
+					getOrCreateNode(child).addParent(parentElement);
+				}
+
+				viewerUpdater.add(parentElement, localAdditions.toArray());
+				viewerUpdater.remove(parentElement, localRemovals.toArray());
+
+				for (Iterator iterator = localRemovals.iterator(); iterator
+						.hasNext();) {
+					Object child = iterator.next();
+					TreeNode childNode = getExistingNode(child);
+					if (childNode != null)
+						childNode.removeParent(parentElement);
+				}
+
+				if (realizedElements != null) {
+					realizedElements.addAll(knownElementAdditions);
+				}
+				knownElements.removeAll(knownElementRemovals);
+			}
+		}
+
+		protected IObservablesListener createCollectionChangeListener(
+				Object parentElement) {
+			return new SetChangeListener(parentElement);
+		}
+
+		protected void addCollectionChangeListener(
+				IObservableCollection collection, IObservablesListener listener) {
+			IObservableSet set = (IObservableSet) collection;
+			ISetChangeListener setListener = (ISetChangeListener) listener;
+			set.addSetChangeListener(setListener);
+		}
+
+		protected void removeCollectionChangeListener(
+				IObservableCollection collection, IObservablesListener listener) {
+			IObservableSet set = (IObservableSet) collection;
+			ISetChangeListener setListener = (ISetChangeListener) listener;
+			set.removeSetChangeListener(setListener);
+		}
+	}
+
+	/**
+	 * Constructs an ObservableListTreeContentProvider using the given list
+	 * factory. Must be called from the display thread.
+	 * 
+	 * @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 ObservableSetTreeContentProvider(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 boolean 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);
+	}
+
+	/**
+	 * Disposes of this content provider. This is called by the viewer when a
+	 * content provider is replaced, or when the viewer itself is disposed.
+	 * <p>
+	 * The viewer should not be updated during this call, as it is in the
+	 * process of being disposed.
+	 * </p>
+	 * <p>
+	 * <em>Note:</em> Data binding content providers become unusable on
+	 * disposal.
+	 * </p>
+	 */
+	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();
+	}
+
+	/**
+	 * Returns the set of known elements which have been realized in the viewer.
+	 * Clients may track this set in order to perform custom actions on elements
+	 * while they are known to be present in the viewer.
+	 * 
+	 * @return the set of known elements which have been realized in the viewer.
+	 * @since 1.3
+	 */
+	public IObservableSet getRealizedElements() {
+		return impl.getRealizedElements();
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableValueEditingSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableValueEditingSupport.java
new file mode 100644
index 0000000..020fbec
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableValueEditingSupport.java
@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 234496
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+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 extends EditingSupport {
+	/**
+	 * Returns an ObservableValueEditingSupport instance which binds the given
+	 * cell editor property to the given element property.
+	 * 
+	 * @param viewer
+	 *            the column viewer
+	 * @param dbc
+	 *            the DataBindingContext used for binding between the cell
+	 *            editor and the viewer element.
+	 * @param cellEditor
+	 *            the cell editor
+	 * @param cellEditorProperty
+	 *            the cell editor property to be bound to the element.
+	 * @param elementProperty
+	 *            the element property to be bound to the cell editor.
+	 * @return an ObservableValueEditingSupport instance using the given
+	 *         arguments.
+	 * @since 1.3
+	 */
+	public static EditingSupport create(ColumnViewer viewer,
+			DataBindingContext dbc, final CellEditor cellEditor,
+			final IValueProperty cellEditorProperty,
+			final IValueProperty elementProperty) {
+		return new ObservableValueEditingSupport(viewer, dbc) {
+			protected IObservableValue doCreateCellEditorObservable(
+					CellEditor cellEditor) {
+				return cellEditorProperty.observe(cellEditor);
+			}
+
+			protected IObservableValue doCreateElementObservable(
+					Object element, ViewerCell cell) {
+				return elementProperty.observe(element);
+			}
+
+			protected CellEditor getCellEditor(Object element) {
+				return cellEditor;
+			}
+		};
+	}
+
+	/**
+	 * 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 ObservableValueEditingSupport(ColumnViewer viewer,
+			DataBindingContext dbc) {
+		super(viewer);
+
+		if (dbc == 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 boolean 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$
+
+		dirty = false;
+
+		Binding binding = createBinding(target, model);
+
+		target.addChangeListener(new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				dirty = true;
+			}
+		});
+
+		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);
+	}
+
+	boolean dirty = false;
+
+	/**
+	 * Updates the model from the target.
+	 */
+	final protected void saveCellEditorValue(CellEditor cellEditor,
+			ViewerCell cell) {
+		if (dirty) {
+			editingState.binding.updateTargetToModel();
+			dirty = false;
+		}
+	}
+
+	private class ColumnViewerEditorActivationListenerHelper extends
+			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;
+
+		EditingState(Binding binding, IObservableValue target,
+				IObservableValue model) {
+			this.binding = binding;
+			this.target = target;
+			this.model = model;
+		}
+
+		void dispose() {
+			binding.dispose();
+			target.dispose();
+			model.dispose();
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/TreeStructureAdvisor.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/TreeStructureAdvisor.java
new file mode 100644
index 0000000..ed7f72d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/TreeStructureAdvisor.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 265062
+ ******************************************************************************/
+package org.eclipse.jface.databinding.viewers;
+
+/**
+ * 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 the given element
+	 *         <strong>never</strong> has children, or <code>null</code> if the
+	 *         children collection should be consulted.
+	 */
+	public Boolean hasChildren(Object element) {
+		return null;
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerListProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerListProperty.java
new file mode 100644
index 0000000..216c404
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerListProperty.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Matthew Hall - bugs 263413, 264286
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.property.list.SimpleListProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.viewers.ViewerObservableListDecorator;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Abstract list property implementation for {@link Viewer} properties. This
+ * class implements some basic behavior that viewer properties are generally
+ * expected to have, namely:
+ * <ul>
+ * <li>Calling {@link #observe(Object)} should create the observable on the
+ * display realm of the viewer's control, rather than the current default realm
+ * <li>All <code>observe()</code> methods should return an
+ * {@link IViewerObservableList}
+ * </ul>
+ * 
+ * @since 1.3
+ */
+public abstract class ViewerListProperty extends SimpleListProperty implements
+		IViewerListProperty {
+	public IObservableList observe(Object source) {
+		if (source instanceof Viewer) {
+			return observe((Viewer) source);
+		}
+		return super.observe(source);
+	}
+
+	public IObservableList observe(Realm realm, Object source) {
+		IObservableList observable = super.observe(realm, source);
+		if (source instanceof Viewer)
+			observable = new ViewerObservableListDecorator(observable,
+					(Viewer) source);
+		return observable;
+	}
+
+	public IViewerObservableList observe(Viewer viewer) {
+		return (IViewerObservableList) observe(SWTObservables.getRealm(viewer
+				.getControl().getDisplay()), viewer);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerProperties.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerProperties.java
new file mode 100644
index 0000000..398a72d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerProperties.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 264286
+ *     Ovidio Mallo - bug 270494
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.jface.internal.databinding.viewers.SelectionProviderMultipleSelectionProperty;
+import org.eclipse.jface.internal.databinding.viewers.SelectionProviderSingleSelectionProperty;
+import org.eclipse.jface.internal.databinding.viewers.StructuredViewerFiltersProperty;
+import org.eclipse.jface.internal.databinding.viewers.ViewerCheckedElementsProperty;
+import org.eclipse.jface.internal.databinding.viewers.ViewerInputProperty;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * A factory for creating properties of JFace {@link Viewer viewers}.
+ * 
+ * @since 1.3
+ */
+public class ViewerProperties {
+	/**
+	 * Returns a set property for observing the checked elements of a
+	 * {@link CheckboxTableViewer}, {@link CheckboxTreeViewer} or
+	 * {@link ICheckable}.
+	 * 
+	 * @param elementType
+	 *            the element type of the returned property
+	 * 
+	 * @return a set property for observing the checked elements of a
+	 *         {@link CheckboxTableViewer}, {@link CheckboxTreeViewer} or
+	 *         {@link ICheckable}.
+	 */
+	public static IViewerSetProperty checkedElements(Object elementType) {
+		return new ViewerCheckedElementsProperty(elementType);
+	}
+
+	/**
+	 * Returns a value property for observing the input of a
+	 * {@link StructuredViewer}.
+	 * 
+	 * @return a value property for observing the input of a
+	 *         {@link StructuredViewer}.
+	 */
+	public static IViewerSetProperty filters() {
+		return new StructuredViewerFiltersProperty();
+	}
+
+	/**
+	 * Returns a value property for observing the input of a {@link Viewer}.
+	 * 
+	 * @return a value property for observing the input of a {@link Viewer}.
+	 */
+	public static IViewerValueProperty input() {
+		return new ViewerInputProperty();
+	}
+
+	/**
+	 * Returns a list property for observing the multiple selection of an
+	 * {@link ISelectionProvider}.
+	 * 
+	 * @return a list property for observing the multiple selection of an
+	 *         {@link ISelectionProvider}.
+	 */
+	public static IViewerListProperty multipleSelection() {
+		return new SelectionProviderMultipleSelectionProperty(false);
+	}
+
+	/**
+	 * Returns a list property for observing the multiple <i>post</i> selection
+	 * of an {@link IPostSelectionProvider}.
+	 * 
+	 * @return a list property for observing the multiple <i>post</i> selection
+	 *         of an {@link IPostSelectionProvider}.
+	 * 
+	 * @since 1.4
+	 */
+	public static IViewerListProperty multiplePostSelection() {
+		return new SelectionProviderMultipleSelectionProperty(true);
+	}
+
+	/**
+	 * Returns a value property for observing the single selection of a
+	 * {@link ISelectionProvider}.
+	 * 
+	 * @return a value property for observing the single selection of a
+	 *         {@link ISelectionProvider}.
+	 */
+	public static IViewerValueProperty singleSelection() {
+		return new SelectionProviderSingleSelectionProperty(false);
+	}
+
+	/**
+	 * Returns a value property for observing the single <i>post</i> selection
+	 * of a {@link IPostSelectionProvider}.
+	 * 
+	 * @return a value property for observing the single <i>post</i> selection
+	 *         of a {@link IPostSelectionProvider}.
+	 * 
+	 * @since 1.4
+	 */
+	public static IViewerValueProperty singlePostSelection() {
+		return new SelectionProviderSingleSelectionProperty(true);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerSetProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerSetProperty.java
new file mode 100644
index 0000000..c191c40
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerSetProperty.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 263413, 264286
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.viewers.ViewerObservableSetDecorator;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Abstract set property implementation for {@link Viewer} properties. This
+ * class implements some basic behavior that viewer properties are generally
+ * expected to have, namely:
+ * <ul>
+ * <li>Calling {@link #observe(Object)} should create the observable on the
+ * display realm of the viewer's control, rather than the current default realm
+ * <li>All <code>observe()</code> methods should return an
+ * {@link IViewerObservableSet}
+ * </ul>
+ * 
+ * @since 1.3
+ */
+public abstract class ViewerSetProperty extends SimpleSetProperty implements
+		IViewerSetProperty {
+	public IObservableSet observe(Object source) {
+		if (source instanceof Viewer) {
+			return observe((Viewer) source);
+		}
+		return super.observe(source);
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		IObservableSet observable = super.observe(realm, source);
+		if (source instanceof Viewer)
+			return new ViewerObservableSetDecorator(observable, (Viewer) source);
+		return observable;
+	}
+
+	public IViewerObservableSet observe(Viewer viewer) {
+		return (IViewerObservableSet) observe(SWTObservables.getRealm(viewer
+				.getControl().getDisplay()), viewer);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerSupport.java
new file mode 100644
index 0000000..0a9d2f3
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerSupport.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 260337)
+ *     Matthew Hall - bug 283428
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.property.Properties;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.viewers.AbstractTableViewer;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * Helper methods for binding observables to a {@link StructuredViewer} or
+ * {@link AbstractTableViewer}.
+ * 
+ * @since 1.3
+ */
+public class ViewerSupport {
+	/**
+	 * Binds the viewer to the specified input, using the specified label
+	 * property to generate labels.
+	 * 
+	 * @param viewer
+	 *            the viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param labelProperty
+	 *            the property to use for labels
+	 */
+	public static void bind(StructuredViewer viewer, IObservableList input,
+			IValueProperty labelProperty) {
+		bind(viewer, input, new IValueProperty[] { labelProperty });
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified label
+	 * properties to generate labels.
+	 * 
+	 * @param viewer
+	 *            the viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param labelProperties
+	 *            the respective properties to use for labels in each of the
+	 *            viewer's columns
+	 */
+	public static void bind(StructuredViewer viewer, IObservableList input,
+			IValueProperty[] labelProperties) {
+		ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+		if (viewer.getInput() != null)
+			viewer.setInput(null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setLabelProvider(new ObservableMapLabelProvider(Properties
+				.observeEach(contentProvider.getKnownElements(),
+						labelProperties)));
+		if (input != null)
+			viewer.setInput(input);
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified label
+	 * property to generate labels.
+	 * 
+	 * @param viewer
+	 *            the viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param labelProperty
+	 *            the property to use for labels
+	 */
+	public static void bind(StructuredViewer viewer, IObservableSet input,
+			IValueProperty labelProperty) {
+		bind(viewer, input, new IValueProperty[] { labelProperty });
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified label
+	 * properties to generate labels.
+	 * 
+	 * @param viewer
+	 *            the viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param labelProperties
+	 *            the respective properties to use for labels in each of the
+	 *            viewer's columns
+	 */
+	public static void bind(StructuredViewer viewer, IObservableSet input,
+			IValueProperty[] labelProperties) {
+		ObservableSetContentProvider contentProvider = new ObservableSetContentProvider();
+		if (viewer.getInput() != null)
+			viewer.setInput(null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setLabelProvider(new ObservableMapLabelProvider(Properties
+				.observeEach(contentProvider.getKnownElements(),
+						labelProperties)));
+		if (input != null)
+			viewer.setInput(input);
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified children
+	 * property to generate child nodes, and the specified label property to
+	 * generate labels.
+	 * 
+	 * @param viewer
+	 *            the tree viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param childrenProperty
+	 *            the property to use as the children of an element
+	 * @param labelProperty
+	 *            the property to use for labels
+	 */
+	public static void bind(AbstractTreeViewer viewer, Object input,
+			IListProperty childrenProperty, IValueProperty labelProperty) {
+		bind(viewer, input, childrenProperty,
+				new IValueProperty[] { labelProperty });
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified children
+	 * property to generate child nodes, and the specified label properties to
+	 * generate labels.
+	 * 
+	 * @param viewer
+	 *            the tree viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param childrenProperty
+	 *            the property to use as the children of an element
+	 * @param labelProperties
+	 *            the respective properties to use for labels in each of the
+	 *            viewer's columns
+	 */
+	public static void bind(AbstractTreeViewer viewer, Object input,
+			IListProperty childrenProperty, IValueProperty[] labelProperties) {
+		Realm realm = SWTObservables.getRealm(viewer.getControl().getDisplay());
+		ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(
+				childrenProperty.listFactory(realm), null);
+		if (viewer.getInput() != null)
+			viewer.setInput(null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setLabelProvider(new ObservableMapLabelProvider(Properties
+				.observeEach(contentProvider.getKnownElements(),
+						labelProperties)));
+		if (input != null)
+			viewer.setInput(input);
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified children
+	 * property to generate child nodes, and the specified label property to
+	 * generate labels.
+	 * 
+	 * @param viewer
+	 *            the tree viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param childrenProperty
+	 *            the property to use as the children of an element
+	 * @param labelProperty
+	 *            the property to use for labels
+	 */
+	public static void bind(AbstractTreeViewer viewer, Object input,
+			ISetProperty childrenProperty, IValueProperty labelProperty) {
+		bind(viewer, input, childrenProperty,
+				new IValueProperty[] { labelProperty });
+	}
+
+	/**
+	 * Binds the viewer to the specified input, using the specified children
+	 * property to generate child nodes, and the specified label properties to
+	 * generate labels.
+	 * 
+	 * @param viewer
+	 *            the tree viewer to set up
+	 * @param input
+	 *            the input to set on the viewer
+	 * @param childrenProperty
+	 *            the property to use as the children of an element
+	 * @param labelProperties
+	 *            the respective properties to use for labels in each of the
+	 *            viewer's columns
+	 */
+	public static void bind(AbstractTreeViewer viewer, Object input,
+			ISetProperty childrenProperty, IValueProperty[] labelProperties) {
+		Realm realm = SWTObservables.getRealm(viewer.getControl().getDisplay());
+		ObservableSetTreeContentProvider contentProvider = new ObservableSetTreeContentProvider(
+				childrenProperty.setFactory(realm), null);
+		if (viewer.getInput() != null)
+			viewer.setInput(null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setLabelProvider(new ObservableMapLabelProvider(Properties
+				.observeEach(contentProvider.getKnownElements(),
+						labelProperties)));
+		if (input != null)
+			viewer.setInput(input);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerValueProperty.java
new file mode 100644
index 0000000..7a800c7
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewerValueProperty.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 263413, 264286
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.viewers.ViewerObservableValueDecorator;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Abstract value property implementation for {@link Viewer} properties. This
+ * class implements some basic behavior that viewer properties are generally
+ * expected to have, namely:
+ * <ul>
+ * <li>Calling {@link #observe(Object)} should create the observable on the
+ * display realm of the viewer's control, rather than the current default realm
+ * <li>All <code>observe()</code> methods should return an
+ * {@link IViewerObservableValue}
+ * </ul>
+ * 
+ * @since 1.3
+ */
+public abstract class ViewerValueProperty extends SimpleValueProperty implements
+		IViewerValueProperty {
+	public IObservableValue observe(Object source) {
+		if (source instanceof Viewer) {
+			return observe((Viewer) source);
+		}
+		return super.observe(source);
+	}
+
+	public IObservableValue observe(Realm realm, Object source) {
+		IObservableValue observable = super.observe(realm, source);
+		if (source instanceof Viewer)
+			observable = new ViewerObservableValueDecorator(observable,
+					(Viewer) source);
+		return observable;
+	}
+
+	public IViewerObservableValue observe(Viewer viewer) {
+		return (IViewerObservableValue) observe(SWTObservables.getRealm(viewer
+				.getControl().getDisplay()), viewer);
+	}
+
+	public IViewerObservableValue observeDelayed(int delay, Viewer viewer) {
+		return ViewersObservables.observeDelayedValue(delay, observe(viewer));
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewersObservables.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewersObservables.java
new file mode 100644
index 0000000..0f36401
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewersObservables.java
@@ -0,0 +1,336 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 206839, 124684, 239302, 245647, 194734, 195222,
+ *                    264286
+ *     Ovidio Mallo - bug 270494
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.Observables;
+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.internal.databinding.viewers.ViewerObservableValueDecorator;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Factory methods for creating observables for JFace viewers
+ * 
+ * @since 1.1
+ */
+public class ViewersObservables {
+	private static void checkNull(Object obj) {
+		if (obj == null)
+			throw new IllegalArgumentException();
+	}
+
+	/**
+	 * 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 viewer control (whichever happens earlier). This
+	 * class helps to delay validation until the user stops changing the value
+	 * (e.g. until a user stops changing a viewer selection). 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
+	 *            the delay in milliseconds
+	 * @param observable
+	 *            the observable being delayed
+	 * @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.3
+	 */
+	public static IViewerObservableValue observeDelayedValue(int delay,
+			IViewerObservableValue observable) {
+		return new ViewerObservableValueDecorator(Observables
+				.observeDelayedValue(delay, observable), observable.getViewer());
+	}
+
+	/**
+	 * 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) {
+		checkNull(selectionProvider);
+		return ViewerProperties.singleSelection().observe(selectionProvider);
+	}
+
+	/**
+	 * Returns an observable value that tracks the current <i>post</i> selection
+	 * of the given post 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
+	 *            The selection provider on which to track the <i>post</i>
+	 *            selection.
+	 * @return the observable value tracking the (single) <i>post</i> selection
+	 *         of the given post selection provider
+	 * 
+	 * @since 1.4
+	 */
+	public static IObservableValue observeSinglePostSelection(
+			IPostSelectionProvider selectionProvider) {
+		checkNull(selectionProvider);
+		return ViewerProperties.singlePostSelection()
+				.observe(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) {
+		checkNull(selectionProvider);
+		return ViewerProperties.multipleSelection().observe(selectionProvider);
+	}
+
+	/**
+	 * Returns an observable list that tracks the current <i>post</i> selection
+	 * of the given post 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
+	 *            The selection provider on which to track the <i>post</i>
+	 *            selection.
+	 * @return the observable value tracking the (multi) <i>post</i> selection
+	 *         of the given post selection provider
+	 * 
+	 * @since 1.4
+	 */
+	public static IObservableList observeMultiPostSelection(
+			IPostSelectionProvider selectionProvider) {
+		checkNull(selectionProvider);
+		return ViewerProperties.multiplePostSelection().observe(
+				selectionProvider);
+	}
+
+	/**
+	 * 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) {
+		checkNull(viewer);
+		return ViewerProperties.singleSelection().observe(viewer);
+	}
+
+	/**
+	 * Returns an observable value that tracks the current <i>post</i> selection
+	 * of the given structured 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 on which to track the <i>post</i> selection.
+	 * @return the observable value tracking the (single) <i>post</i> selection
+	 *         of the given structured viewer
+	 * 
+	 * @since 1.4
+	 */
+	public static IViewerObservableValue observeSinglePostSelection(
+			StructuredViewer viewer) {
+		checkNull(viewer);
+		return ViewerProperties.singlePostSelection().observe(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) {
+		checkNull(viewer);
+		return ViewerProperties.multipleSelection().observe(viewer);
+	}
+
+	/**
+	 * Returns an observable list that tracks the current <i>post</i> selection
+	 * of the given structured 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
+	 *            The viewer on which to track the <i>post</i> selection.
+	 * @return the observable value tracking the (multi) <i>post</i> selection
+	 *         of the given structured viewer
+	 * 
+	 * @since 1.4
+	 */
+	public static IViewerObservableList observeMultiPostSelection(
+			StructuredViewer viewer) {
+		checkNull(viewer);
+		return ViewerProperties.multiplePostSelection().observe(viewer);
+	}
+
+	/**
+	 * 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) {
+		checkNull(viewer);
+		return ViewerProperties.input().observe(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) {
+		checkNull(checkable);
+		return ViewerProperties.checkedElements(elementType).observe(checkable);
+	}
+
+	/**
+	 * 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) {
+		checkNull(viewer);
+		return ViewerProperties.checkedElements(elementType).observe(viewer);
+	}
+
+	/**
+	 * 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) {
+		checkNull(viewer);
+		return ViewerProperties.checkedElements(elementType).observe(viewer);
+	}
+
+	/**
+	 * Returns an observable set that tracks the filters of the given viewer.
+	 * Note that the returned set will not track changes that are made using
+	 * direct API on StructuredViewer (by calling
+	 * {@link StructuredViewer#addFilter(org.eclipse.jface.viewers.ViewerFilter)
+	 * addFilter()},
+	 * {@link StructuredViewer#removeFilter(org.eclipse.jface.viewers.ViewerFilter)
+	 * removeFilter()}, or
+	 * {@link StructuredViewer#setFilters(org.eclipse.jface.viewers.ViewerFilter[])
+	 * setFilters()}) -- it is assumed that filters are only changed through the
+	 * returned set.
+	 * 
+	 * @param viewer
+	 *            viewer containing the filters to be tracked
+	 * @return an observable set that tracks the filters of the given viewer.
+	 * @since 1.3
+	 */
+	public static IViewerObservableSet observeFilters(StructuredViewer viewer) {
+		checkNull(viewer);
+		return ViewerProperties.filters().observe(viewer);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/package.html b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/package.html
new file mode 100644
index 0000000..2ebf748
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/package.html
@@ -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>
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/WizardPageSupport.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/WizardPageSupport.java
new file mode 100644
index 0000000..40fcde6
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/WizardPageSupport.java
@@ -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
+ *     Boris Bokowski - bug 218269
+ *     Matthew Hall - bug 218269, 240444, 239900
+ *     Ashley Cambrell - bug 199179 
+ *     Ovidio Mallo - bug 235195, 237856
+ *******************************************************************************/
+package org.eclipse.jface.databinding.wizard;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.dialog.DialogPageSupport;
+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.
+ * 
+ * <p>
+ * The completion state of the wizard page will only be set to <code>true</code>
+ * if <i>all</i> of the following conditions are met:
+ * <ul>
+ * <li>The validation result from the data binding context has none of the
+ * severities {@link IStatus#ERROR} or {@link IStatus#CANCEL}.</li>
+ * <li>None of the validation status observables of the data binding context is
+ * stale.</li>
+ * </ul>
+ * </p>
+ * 
+ * @noextend This class is not intended to be subclassed by clients.
+ *
+ * @since 1.1
+ */
+public class WizardPageSupport extends DialogPageSupport {
+	private WizardPageSupport(WizardPage wizardPage, DataBindingContext dbc) {
+		super(wizardPage, dbc);
+	}
+
+	/**
+	 * 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(boolean)} 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);
+	}
+
+	protected void handleStatusChanged() {
+		super.handleStatusChanged();
+		boolean pageComplete = true;
+		if (currentStatusStale) {
+			pageComplete = false;
+		} else if (currentStatus != null) {
+			pageComplete = !currentStatus.matches(IStatus.ERROR
+					| IStatus.CANCEL);
+		}
+		((WizardPage) getDialogPage()).setPageComplete(pageComplete);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/package.html b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/package.html
new file mode 100755
index 0000000..b094981
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/package.html
@@ -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>
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.java
new file mode 100644
index 0000000..8ccf7f5
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 251424
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.swt;
+
+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 implements Runnable, IChangeListener {
+		private Widget widget;
+		Object element;
+
+		private boolean dirty = true;
+
+		private IObservable[] dependencies = new IObservable[0];
+
+		UpdateRunnable(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 != null && !theComposite.isDisposed()
+					&& widget != null && !widget.isDisposed()) {
+				updateIfNecessary();
+			}
+		}
+
+		private void updateIfNecessary() {
+			if (dirty) {
+				dependencies = ObservableTracker.runAndMonitor(new 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 implements Runnable {
+		private boolean 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(
+					(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 implements DisposeListener,
+			IListChangeListener {
+
+		// DisposeListener implementation
+		public void widgetDisposed(DisposeEvent e) {
+			CompositeUpdater.this.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 CompositeUpdater(Composite toUpdate, IObservableList model) {
+		this.theComposite = toUpdate;
+		this.model = model;
+
+		model.addListChangeListener(privateInterface);
+		theComposite.addDisposeListener(privateInterface);
+		ObservableTracker.setIgnore(true);
+		try {
+			int index = 0;
+			for (Iterator it = CompositeUpdater.this.model.iterator(); it
+					.hasNext();) {
+				Object element = it.next();
+				createChild(element, index++);
+			}
+		} finally {
+			ObservableTracker.setIgnore(false);
+		}
+	}
+
+	/**
+	 * @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();
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/ControlUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/ControlUpdater.java
new file mode 100644
index 0000000..ca819b7
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/ControlUpdater.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.swt;
+
+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 implements PaintListener, 
+		DisposeListener, Runnable, IChangeListener {
+		
+		// PaintListener implementation
+		public void paintControl(PaintEvent e) {
+			updateIfNecessary();
+		}
+
+		// DisposeListener implementation
+		public void widgetDisposed(DisposeEvent e) {
+			ControlUpdater.this.dispose();
+		}
+		
+		// Runnable implementation. This method runs at most once per repaint whenever the
+		// value gets marked as dirty.
+		public void run() {
+			if (theControl != 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 Runnable() {
+		public void run() {
+			updateControl();
+		}
+	};
+	
+	private PrivateInterface privateInterface = new PrivateInterface();
+	private Control theControl;
+	private IObservable[] dependencies = new IObservable[0];
+	private boolean dirty = false;
+	
+	/**
+	 * Creates an updater for the given control.  
+	 * 
+	 * @param toUpdate control to update
+	 */
+	public ControlUpdater(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);
+		}
+	}
+	
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/MenuUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/MenuUpdater.java
new file mode 100644
index 0000000..a40060d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/MenuUpdater.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.swt;
+
+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 implements MenuListener, 
+		DisposeListener, Runnable, IChangeListener {
+
+		// DisposeListener implementation
+		public void widgetDisposed(DisposeEvent e) {
+			MenuUpdater.this.dispose();
+		}
+		
+		// Runnable implementation. This method runs at most once per repaint whenever the
+		// value gets marked as dirty.
+		public void run() {
+			if (theMenu != 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 Runnable() {
+		public void run() {
+			updateMenu();
+		}
+	};
+	
+	private PrivateInterface privateInterface = new PrivateInterface();
+	private Menu theMenu;
+	private IObservable[] dependencies = new IObservable[0];
+	private boolean dirty = false;
+	
+	/**
+	 * Creates an updator for the given menu.  
+	 * 
+	 * @param toUpdate menu to update
+	 */
+	public MenuUpdater(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);
+		}
+	}
+	
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/SWTUtil.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/SWTUtil.java
new file mode 100644
index 0000000..077c79c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/SWTUtil.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.swt;
+
+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 SWTUtil() {
+	}
+
+	/**
+	 * Runs the given runnable on the given display as soon as possible. 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(final Display d) {
+		WorkQueue result;
+		synchronized (mapDisplayOntoWorkQueue) {
+			// Look for existing queue
+			result = (WorkQueue) mapDisplayOntoWorkQueue.get(d);
+
+			if (result == null) {
+				// If none, create new queue
+				result = new WorkQueue(d);
+				final WorkQueue q = result;
+				mapDisplayOntoWorkQueue.put(d, result);
+				d.asyncExec(new Runnable() {
+					public void run() {
+						d.disposeExec(new Runnable() {
+							public void run() {
+								synchronized (mapDisplayOntoWorkQueue) {
+									q.cancelAll();
+									mapDisplayOntoWorkQueue.remove(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 = (int) (i1 * ratio + i2 * (1.0d - 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(final Exception t) {
+		SafeRunnable.run(new SafeRunnable() {
+			public void run() throws Exception {
+				throw t;
+			}
+
+			public void handleException(Throwable e) {
+				// IMPORTANT: Do not call the super implementation, since
+				// it opens a modal dialog, and may cause *syncExecs to run
+				// too early.
+			}
+		});
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/TableUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/TableUpdater.java
new file mode 100644
index 0000000..8570a3c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/TableUpdater.java
@@ -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
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.swt;
+
+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 implements Runnable, IChangeListener,
+			DisposeListener {
+		private TableItem item;
+
+		private boolean dirty = false;
+
+		private IObservable[] dependencies = new IObservable[0];
+
+		private final Object element;
+
+		UpdateRunnable(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 != null && !table.isDisposed() && item != 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(final int indexOfItem) {
+			if (dirty) {
+				dependencies = ObservableTracker.runAndMonitor(new Runnable() {
+					public void run() {
+						updateItem(indexOfItem, item, element);
+					}
+				}, 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 implements Listener, DisposeListener {
+
+		// Listener implementation
+		public void handleEvent(Event e) {
+			if (e.type == SWT.SetData) {
+				UpdateRunnable runnable = (UpdateRunnable) e.item.getData();
+				if (runnable == null) {
+					runnable = new UpdateRunnable((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) {
+			TableUpdater.this.dispose();
+		}
+
+	}
+
+	private PrivateInterface privateInterface = new PrivateInterface();
+
+	private Table table;
+
+	private IListChangeListener listChangeListener = new 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 TableUpdater(Table table, IObservableList list) {
+		this.table = table;
+		this.list = list;
+		Assert.isLegal((table.getStyle() & SWT.VIRTUAL) != 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);
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/WorkQueue.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/WorkQueue.java
new file mode 100644
index 0000000..07d7205
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/WorkQueue.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.swt;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * 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 boolean updateScheduled = false;
+
+	private LinkedList pendingWork = new LinkedList();
+
+	private Display d;
+
+	private Set pendingWorkSet = new HashSet();
+
+	private Runnable updateJob = new Runnable() {
+		public void run() {
+			doUpdate();
+			updateScheduled = false;
+		}
+	};
+
+	/**
+	 * @param targetDisplay
+	 */
+	public WorkQueue(Display targetDisplay) {
+		d = targetDisplay;
+	}
+
+	private void doUpdate() {
+		for (;;) {
+			Runnable next;
+			synchronized (pendingWork) {
+				if (pendingWork.isEmpty()) {
+					break;
+				}
+				next = (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.timerExec(0, updateJob);
+			}
+		}
+	}
+
+	/**
+	 * Cancels a previously-scheduled runnable. Has no effect if the given
+	 * runnable was not previously scheduled or has already executed.
+	 * 
+	 * @param toCancel
+	 *            runnable to cancel
+	 */
+	public void cancelExec(Runnable toCancel) {
+		synchronized (pendingWork) {
+			pendingWork.remove(toCancel);
+			pendingWorkSet.remove(toCancel);
+		}
+	}
+
+	/**
+	 * Cancels all pending work.
+	 */
+	public void cancelAll() {
+		synchronized (pendingWork) {
+			pendingWork.clear();
+			pendingWorkSet.clear();
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/ViewerLabelProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/ViewerLabelProvider.java
new file mode 100644
index 0000000..bfc1a56
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/ViewerLabelProvider.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.provisional.viewers;
+
+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 implements 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 = (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 boolean isLabelProperty(Object element, String property) {
+		return true;
+	}
+
+	public void removeListener(ILabelProviderListener listener) {
+		listeners.remove(listener);
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonImageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonImageProperty.java
new file mode 100644
index 0000000..dd8aae3
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonImageProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 213893)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ButtonImageProperty extends WidgetImageValueProperty {
+	Image doGetImageValue(Object source) {
+		return ((Button) source).getImage();
+	}
+
+	void doSetImageValue(Object source, Image value) {
+		((Button) source).setImage(value);
+	}
+
+	public String toString() {
+		return "Button.image <Image>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonSelectionProperty.java
new file mode 100644
index 0000000..cbd5811
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonSelectionProperty.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ButtonSelectionProperty extends WidgetBooleanValueProperty {
+	/**
+	 * 
+	 */
+	public ButtonSelectionProperty() {
+		super(SWT.Selection);
+	}
+
+	boolean doGetBooleanValue(Object source) {
+		return ((Button) source).getSelection();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((Button) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "Button.selection <Boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonTextProperty.java
new file mode 100644
index 0000000..4c54be4
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 213893)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Button;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ButtonTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Button) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Button) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Button.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboItemsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboItemsProperty.java
new file mode 100644
index 0000000..2a6de88
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboItemsProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 251611
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CComboItemsProperty extends ControlStringListProperty {
+	protected void doUpdateStringList(final Control control, ListDiff diff) {
+		diff.accept(new ListDiffVisitor() {
+			CCombo combo = (CCombo) control;
+
+			public void handleAdd(int index, Object element) {
+				combo.add((String) element, index);
+			}
+
+			public void handleRemove(int index, Object element) {
+				combo.remove(index);
+			}
+
+			// public void handleMove(int oldIndex, int newIndex, Object
+			// element) {
+			// int selectionIndex = combo.getSelectionIndex();
+			// Listener[] modifyListeners = combo.getListeners(SWT.Modify);
+			// if (selectionIndex == oldIndex) {
+			// for (int i = 0; i < modifyListeners.length; i++)
+			// combo.removeListener(SWT.Modify, modifyListeners[i]);
+			// }
+			//
+			// super.handleMove(oldIndex, newIndex, element);
+			//
+			// if (selectionIndex == oldIndex) {
+			// combo.select(newIndex);
+			// for (int i = 0; i < modifyListeners.length; i++)
+			// combo.addListener(SWT.Modify, modifyListeners[i]);
+			// }
+			// }
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				combo.setItem(index, (String) newElement);
+			}
+		});
+	}
+
+	public String[] doGetStringList(Control control) {
+		return ((CCombo) control).getItems();
+	}
+
+	public String toString() {
+		return "CCombo.items[] <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSelectionProperty.java
new file mode 100644
index 0000000..1bb4a11
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSelectionProperty.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CComboSelectionProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public CComboSelectionProperty() {
+		super(SWT.Modify);
+	}
+
+	String doGetStringValue(Object source) {
+		return ((CCombo) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		CCombo ccombo = (CCombo) source;
+		String items[] = ccombo.getItems();
+		int index = -1;
+		if (value == null) {
+			ccombo.select(-1);
+		} else if (items != null) {
+			for (int i = 0; i < items.length; i++) {
+				if (value.equals(items[i])) {
+					index = i;
+					break;
+				}
+			}
+			if (index == -1) {
+				ccombo.setText(value);
+			} else {
+				ccombo.select(index); // -1 will not "unselect"
+			}
+		}
+	}
+
+	public String toString() {
+		return "CCombo.selection <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSingleSelectionIndexProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSingleSelectionIndexProperty.java
new file mode 100644
index 0000000..168c94a
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSingleSelectionIndexProperty.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CComboSingleSelectionIndexProperty extends SingleSelectionIndexProperty {
+	/**
+	 * 
+	 */
+	public CComboSingleSelectionIndexProperty() {
+		super(new int[] { SWT.Selection, SWT.DefaultSelection });
+	}
+
+	int doGetIntValue(Object source) {
+		return ((CCombo) source).getSelectionIndex();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		if (value == -1)
+			((CCombo) source).deselectAll();
+		else
+			((CCombo) source).select(value);
+	}
+
+	public String toString() {
+		return "CCombo.selectionIndex <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboTextProperty.java
new file mode 100644
index 0000000..cc44515
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboTextProperty.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CComboTextProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public CComboTextProperty() {
+		super(SWT.Modify);
+	}
+
+	String doGetStringValue(Object source) {
+		return ((CCombo) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((CCombo) source).setText(value != null ? value : ""); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "CCombo.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelImageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelImageProperty.java
new file mode 100644
index 0000000..e72cc84
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelImageProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 213893)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CLabelImageProperty extends WidgetImageValueProperty {
+	Image doGetImageValue(Object source) {
+		return ((CLabel) source).getImage();
+	}
+
+	void doSetImageValue(Object source, Image value) {
+		((CLabel) source).setImage(value);
+	}
+
+	public String toString() {
+		return "CLabel.image <Image>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelTextProperty.java
new file mode 100644
index 0000000..5aeb166
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.custom.CLabel;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CLabelTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((CLabel) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((CLabel) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "CLabel.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CTabItemTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CTabItemTooltipTextProperty.java
new file mode 100644
index 0000000..790fda2
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CTabItemTooltipTextProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262946
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.custom.CTabItem;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CTabItemTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((CTabItem) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((CTabItem) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "CTabItem.toolTipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboItemsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboItemsProperty.java
new file mode 100644
index 0000000..12c8588
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboItemsProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 251611
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ComboItemsProperty extends ControlStringListProperty {
+	protected void doUpdateStringList(final Control control, ListDiff diff) {
+		diff.accept(new ListDiffVisitor() {
+			Combo combo = (Combo) control;
+
+			public void handleAdd(int index, Object element) {
+				combo.add((String) element, index);
+			}
+
+			public void handleRemove(int index, Object element) {
+				combo.remove(index);
+			}
+
+			// public void handleMove(int oldIndex, int newIndex, Object
+			// element) {
+			// int selectionIndex = combo.getSelectionIndex();
+			// Listener[] modifyListeners = combo.getListeners(SWT.Modify);
+			// if (selectionIndex == oldIndex) {
+			// for (int i = 0; i < modifyListeners.length; i++)
+			// combo.removeListener(SWT.Modify, modifyListeners[i]);
+			// }
+			//
+			// super.handleMove(oldIndex, newIndex, element);
+			//
+			// if (selectionIndex == oldIndex) {
+			// combo.select(newIndex);
+			// for (int i = 0; i < modifyListeners.length; i++)
+			// combo.addListener(SWT.Modify, modifyListeners[i]);
+			// }
+			// }
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				combo.setItem(index, (String) newElement);
+			}
+		});
+	}
+
+	public String[] doGetStringList(Control control) {
+		return ((Combo) control).getItems();
+	}
+
+	public String toString() {
+		return "Combo.items[] <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSelectionProperty.java
new file mode 100644
index 0000000..3aab542
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSelectionProperty.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ComboSelectionProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public ComboSelectionProperty() {
+		super(SWT.Modify);
+	}
+
+	String doGetStringValue(Object source) {
+		return ((Combo) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		Combo combo = (Combo) source;
+		String items[] = combo.getItems();
+		int index = -1;
+		if (items != null && value != null) {
+			for (int i = 0; i < items.length; i++) {
+				if (value.equals(items[i])) {
+					index = i;
+					break;
+				}
+			}
+			if (index == -1) {
+				combo.setText(value);
+			} else {
+				combo.select(index); // -1 will not "unselect"
+			}
+		}
+	}
+
+	public String toString() {
+		return "Combo.selection <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSingleSelectionIndexProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSingleSelectionIndexProperty.java
new file mode 100644
index 0000000..ab5bc16
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSingleSelectionIndexProperty.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ComboSingleSelectionIndexProperty extends
+		SingleSelectionIndexProperty {
+	/**
+	 * 
+	 */
+	public ComboSingleSelectionIndexProperty() {
+		super(new int[] { SWT.Selection, SWT.DefaultSelection });
+	}
+
+	int doGetIntValue(Object source) {
+		return ((Combo) source).getSelectionIndex();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		if (value == -1)
+			((Combo) source).deselectAll();
+		else
+			((Combo) source).select(value);
+	}
+
+	public String toString() {
+		return "Combo.selectionIndex <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboTextProperty.java
new file mode 100644
index 0000000..f35b9a8
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboTextProperty.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ComboTextProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public ComboTextProperty() {
+		super(SWT.Modify);
+	}
+
+	String doGetStringValue(Object source) {
+		return ((Combo) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Combo) source).setText(value != null ? value : ""); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Combo.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlBackgroundProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlBackgroundProperty.java
new file mode 100644
index 0000000..6af816d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlBackgroundProperty.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlBackgroundProperty extends WidgetValueProperty {
+	public Object getValueType() {
+		return Color.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Control) source).getBackground();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Control) source).setBackground((Color) value);
+	}
+
+	public String toString() {
+		return "Control.background <Color>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlBoundsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlBoundsProperty.java
new file mode 100644
index 0000000..95c290e
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlBoundsProperty.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Tom Schindl - initial API and implementation
+ *     Matthew Hall - bugs 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlBoundsProperty extends WidgetValueProperty {
+	/**
+	 * 
+	 */
+	public ControlBoundsProperty() {
+		super(new int[] { SWT.Resize, SWT.Move });
+	}
+
+	public Object getValueType() {
+		return Rectangle.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Control) source).getBounds();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Control) source).setBounds((Rectangle) value);
+	}
+
+	public String toString() {
+		return "Control.bounds <Rectangle>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlEnabledProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlEnabledProperty.java
new file mode 100644
index 0000000..1cd2c25
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlEnabledProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlEnabledProperty extends WidgetBooleanValueProperty {
+	public boolean doGetBooleanValue(Object source) {
+		return ((Control) source).getEnabled();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((Control) source).setEnabled(value);
+	}
+
+	public String toString() {
+		return "Control.enabled <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlFocusedProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlFocusedProperty.java
new file mode 100644
index 0000000..caa5f58
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlFocusedProperty.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Tom Schindl - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlFocusedProperty extends WidgetBooleanValueProperty {
+	/**
+	 * 
+	 */
+	public ControlFocusedProperty() {
+		super();
+	}
+
+	public boolean doGetBooleanValue(Object source) {
+		return ((Control) source).isFocusControl();
+	}
+
+	public void doSetBooleanValue(Object source, boolean value) {
+		if (value)
+			((Control) source).setFocus();
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		int[] events = { SWT.FocusIn, SWT.FocusOut };
+		return new ControlFocusListener(this, listener, events, null);
+	}
+
+	private class ControlFocusListener extends WidgetListener {
+		/**
+		 * @param property
+		 * @param listener
+		 * @param changeEvents
+		 * @param staleEvents
+		 */
+		private ControlFocusListener(IProperty property,
+				ISimplePropertyListener listener, int[] changeEvents,
+				int[] staleEvents) {
+			super(property, listener, changeEvents, staleEvents);
+		}
+
+		public void handleEvent(Event event) {
+			switch (event.type) {
+			case SWT.FocusIn:
+				fireChange(event.widget, Diffs.createValueDiff(Boolean.FALSE,
+						Boolean.TRUE));
+				break;
+			case SWT.FocusOut:
+				fireChange(event.widget, Diffs.createValueDiff(Boolean.TRUE,
+						Boolean.FALSE));
+				break;
+			}
+		}
+	}
+
+	public String toString() {
+		return "Control.focus <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlFontProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlFontProperty.java
new file mode 100644
index 0000000..dc6ea7f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlFontProperty.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlFontProperty extends WidgetValueProperty {
+	public Object getValueType() {
+		return Font.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Control) source).getFont();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Control) source).setFont((Font) value);
+	}
+
+	public String toString() {
+		return "Control.font <Font>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlForegroundProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlForegroundProperty.java
new file mode 100644
index 0000000..ba415ed
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlForegroundProperty.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlForegroundProperty extends WidgetValueProperty {
+	public Object getValueType() {
+		return Color.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Control) source).getForeground();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Control) source).setForeground((Color) value);
+	}
+
+	public String toString() {
+		return "Control.foreground <Color>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlLocationProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlLocationProperty.java
new file mode 100644
index 0000000..5178995
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlLocationProperty.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Tom Schindl - initial API and implementation
+ *     Matthew Hall - bug 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlLocationProperty extends WidgetValueProperty {
+	/**
+	 * 
+	 */
+	public ControlLocationProperty() {
+		super(SWT.Move);
+	}
+
+	public Object getValueType() {
+		return Point.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Control) source).getLocation();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Control) source).setLocation((Point) value);
+	}
+
+	public String toString() {
+		return "Control.location <Point>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlSizeProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlSizeProperty.java
new file mode 100644
index 0000000..4f4bbee
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlSizeProperty.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Tom Schindl - initial API and implementation
+ *     Matthew Hall - bug 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlSizeProperty extends WidgetValueProperty {
+	/**
+	 * 
+	 */
+	public ControlSizeProperty() {
+		super(SWT.Resize);
+	}
+
+	public Object getValueType() {
+		return Point.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Control) source).getSize();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Control) source).setSize((Point) value);
+	}
+
+	public String toString() {
+		return "Control.size <Point>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlStringListProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlStringListProperty.java
new file mode 100644
index 0000000..b06d8e4
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlStringListProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 251611, 263413, 265561
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.jface.databinding.swt.WidgetListProperty;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class ControlStringListProperty extends WidgetListProperty {
+	public Object getElementType() {
+		return String.class;
+	}
+
+	protected void doSetList(Object source, List list, ListDiff diff) {
+		doUpdateList(source, diff);
+	}
+
+	protected void doUpdateList(Object source, ListDiff diff) {
+		doUpdateStringList((Control) source, diff);
+	}
+
+	abstract void doUpdateStringList(Control control, ListDiff diff);
+
+	protected List doGetList(Object source) {
+		String[] list = doGetStringList((Control) source);
+		return Arrays.asList(list);
+	}
+
+	abstract String[] doGetStringList(Control control);
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlTooltipTextProperty.java
new file mode 100644
index 0000000..1c4a9c0
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlTooltipTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Control) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Control) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "Control.tooltipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlVisibleProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlVisibleProperty.java
new file mode 100644
index 0000000..055657d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlVisibleProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ControlVisibleProperty extends WidgetBooleanValueProperty {
+	boolean doGetBooleanValue(Object source) {
+		return ((Control) source).getVisible();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((Control) source).setVisible(value);
+	}
+
+	public String toString() {
+		return "Control.visible <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DateTimeSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DateTimeSelectionProperty.java
new file mode 100644
index 0000000..76ffe17
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DateTimeSelectionProperty.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 169876)
+ *     Matthew Hall - bug 271720
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DateTime;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DateTimeSelectionProperty extends WidgetValueProperty {
+	/**
+	 * 
+	 */
+	public DateTimeSelectionProperty() {
+		super(SWT.Selection);
+	}
+
+	public Object getValueType() {
+		return Date.class;
+	}
+
+	// One calendar per thread to preserve thread-safety
+	private static final ThreadLocal calendar = new ThreadLocal() {
+		protected Object initialValue() {
+			return Calendar.getInstance();
+		}
+	};
+
+	protected Object doGetValue(Object source) {
+		DateTime dateTime = (DateTime) source;
+
+		Calendar cal = (Calendar) calendar.get();
+		cal.clear();
+		if ((dateTime.getStyle() & SWT.TIME) != 0) {
+			cal.set(Calendar.HOUR_OF_DAY, dateTime.getHours());
+			cal.set(Calendar.MINUTE, dateTime.getMinutes());
+			cal.set(Calendar.SECOND, dateTime.getSeconds());
+		} else {
+			cal.set(Calendar.YEAR, dateTime.getYear());
+			cal.set(Calendar.MONTH, dateTime.getMonth());
+			cal.set(Calendar.DAY_OF_MONTH, dateTime.getDay());
+		}
+		return cal.getTime();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		DateTime dateTime = (DateTime) source;
+
+		if (value == null)
+			throw new IllegalArgumentException(
+					"Cannot set null selection on DateTime"); //$NON-NLS-1$
+
+		Calendar cal = (Calendar) calendar.get();
+		cal.setTime((Date) value);
+		if ((dateTime.getStyle() & SWT.TIME) != 0) {
+			dateTime.setTime(cal.get(Calendar.HOUR_OF_DAY), cal
+					.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
+		} else {
+			dateTime.setDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
+					cal.get(Calendar.DAY_OF_MONTH));
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ItemImageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ItemImageProperty.java
new file mode 100644
index 0000000..310ea68
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ItemImageProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 262320)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Item;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ItemImageProperty extends WidgetImageValueProperty {
+	Image doGetImageValue(Object source) {
+		return ((Item) source).getImage();
+	}
+
+	void doSetImageValue(Object source, Image value) {
+		((Item) source).setImage(value);
+	}
+
+	public String toString() {
+		return "Item.image <Image>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ItemTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ItemTextProperty.java
new file mode 100644
index 0000000..bfcf462
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ItemTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Item;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ItemTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Item) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Item) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Item.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelImageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelImageProperty.java
new file mode 100644
index 0000000..3722125
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelImageProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 213893)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class LabelImageProperty extends WidgetImageValueProperty {
+	Image doGetImageValue(Object source) {
+		return ((Label) source).getImage();
+	}
+
+	void doSetImageValue(Object source, Image value) {
+		((Label) source).setImage(value);
+	}
+
+	public String toString() {
+		return "Label.image <Image>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelTextProperty.java
new file mode 100644
index 0000000..f7baeee
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class LabelTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Label) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Label) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Label.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LinkTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LinkTextProperty.java
new file mode 100644
index 0000000..7a7141d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LinkTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Link;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class LinkTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Link) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Link) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Link.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListItemsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListItemsProperty.java
new file mode 100644
index 0000000..c5e3035
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListItemsProperty.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 251611
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.List;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ListItemsProperty extends ControlStringListProperty {
+	protected void doUpdateStringList(final Control control, ListDiff diff) {
+		diff.accept(new ListDiffVisitor() {
+			List list = (List) control;
+
+			public void handleAdd(int index, Object element) {
+				list.add((String) element, index);
+			}
+
+			public void handleRemove(int index, Object element) {
+				list.remove(index);
+			}
+
+			// public void handleMove(int oldIndex, int newIndex, Object
+			// element) {
+			// int selectionIndex = combo.getSelectionIndex();
+			// Listener[] modifyListeners = combo.getListeners(SWT.Modify);
+			// if (selectionIndex == oldIndex) {
+			// for (int i = 0; i < modifyListeners.length; i++)
+			// combo.removeListener(SWT.Modify, modifyListeners[i]);
+			// }
+			//
+			// super.handleMove(oldIndex, newIndex, element);
+			//
+			// if (selectionIndex == oldIndex) {
+			// combo.select(newIndex);
+			// for (int i = 0; i < modifyListeners.length; i++)
+			// combo.addListener(SWT.Modify, modifyListeners[i]);
+			// }
+			// }
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				list.setItem(index, (String) newElement);
+			}
+		});
+	}
+
+	public String[] doGetStringList(Control control) {
+		return ((List) control).getItems();
+	}
+
+	public String toString() {
+		return "List.items[] <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSelectionProperty.java
new file mode 100644
index 0000000..7648d6e
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSelectionProperty.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.List;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ListSelectionProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public ListSelectionProperty() {
+		super(SWT.Selection);
+	}
+
+	String doGetStringValue(Object source) {
+		List list = (List) source;
+		int index = list.getSelectionIndex();
+		if (index >= 0)
+			return list.getItem(index);
+		return null;
+	}
+
+	void doSetStringValue(Object source, String value) {
+		List list = (List) source;
+		String items[] = list.getItems();
+		int index = -1;
+		if (items != null && value != null) {
+			for (int i = 0; i < items.length; i++) {
+				if (value.equals(items[i])) {
+					index = i;
+					break;
+				}
+			}
+			list.select(index);
+		}
+	}
+
+	public String toString() {
+		return "List.selection <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSingleSelectionIndexProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSingleSelectionIndexProperty.java
new file mode 100644
index 0000000..33e9680
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSingleSelectionIndexProperty.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.List;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ListSingleSelectionIndexProperty extends
+		SingleSelectionIndexProperty {
+	/**
+	 * 
+	 */
+	public ListSingleSelectionIndexProperty() {
+		super(new int[] { SWT.Selection, SWT.DefaultSelection });
+	}
+
+	int doGetIntValue(Object source) {
+		return ((List) source).getSelectionIndex();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		if (value == -1)
+			((List) source).deselectAll();
+		else
+			((List) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "List.selectionIndex <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuEnabledProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuEnabledProperty.java
new file mode 100644
index 0000000..05e3c25
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuEnabledProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 280157)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Menu;
+
+/**
+ * 
+ */
+public class MenuEnabledProperty extends WidgetBooleanValueProperty {
+	public boolean doGetBooleanValue(Object source) {
+		return ((Menu) source).getEnabled();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((Menu) source).setEnabled(value);
+	}
+
+	public String toString() {
+		return "Menu.enabled <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuItemEnabledProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuItemEnabledProperty.java
new file mode 100644
index 0000000..9340e00
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuItemEnabledProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 280157)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.MenuItem;
+
+/**
+ * 
+ */
+public class MenuItemEnabledProperty extends WidgetBooleanValueProperty {
+	public boolean doGetBooleanValue(Object source) {
+		return ((MenuItem) source).getEnabled();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((MenuItem) source).setEnabled(value);
+	}
+
+	public String toString() {
+		return "MenuItem.enabled <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuItemSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuItemSelectionProperty.java
new file mode 100644
index 0000000..ae3182d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/MenuItemSelectionProperty.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.MenuItem;
+
+/**
+ * 
+ */
+public class MenuItemSelectionProperty extends WidgetBooleanValueProperty {
+	/**
+	 * 
+	 */
+	public MenuItemSelectionProperty() {
+		super(SWT.Selection);
+	}
+
+	boolean doGetBooleanValue(Object source) {
+		return ((MenuItem) source).getSelection();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((MenuItem) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "MenuItem.selection <Boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTDelayedObservableValueDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTDelayedObservableValueDecorator.java
new file mode 100644
index 0000000..2f7031b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTDelayedObservableValueDecorator.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 - bugs 212223, 208332, 245647, 281723
+ *  	Will Horn - bug 215297
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IVetoableValue;
+import org.eclipse.core.databinding.observable.value.ValueChangingEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * {@link IObservableValue} implementation that wraps an
+ * {@link IObservableValue} 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, if any (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 SWTDelayedObservableValueDecorator extends
+		SWTObservableValueDecorator {
+	private Control control;
+
+	/**
+	 * 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 decorated
+	 * @param widget
+	 * @throws IllegalArgumentException
+	 *             if <code>updateEventType</code> is an incorrect type.
+	 */
+	public SWTDelayedObservableValueDecorator(IObservableValue decorated,
+			Widget widget) {
+		super(decorated, widget);
+
+		if (widget instanceof Control) {
+			control = (Control) widget;
+			WidgetListenerUtil.asyncAddListener(control, SWT.FocusOut, this);
+		}
+	}
+
+	public void handleEvent(Event event) {
+		// When the control loses focus..
+		if (event.type == SWT.FocusOut && isStale())
+			getValue(); // short-circuit the delay
+
+		super.handleEvent(event);
+	}
+
+	public synchronized void dispose() {
+		if (control != null) {
+			WidgetListenerUtil.asyncRemoveListener(control, SWT.FocusOut, this);
+			control = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableListDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableListDecorator.java
new file mode 100644
index 0000000..dfc88e7
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableListDecorator.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Matthew Hall - bugs 190881, 264286, 281723
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.list.DecoratingObservableList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.jface.databinding.swt.ISWTObservableList;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SWTObservableListDecorator extends DecoratingObservableList
+		implements ISWTObservableList {
+	private Widget widget;
+
+	/**
+	 * @param decorated
+	 * @param widget
+	 */
+	public SWTObservableListDecorator(IObservableList decorated, Widget widget) {
+		super(decorated, true);
+		this.widget = widget;
+		WidgetListenerUtil.asyncAddListener(widget, SWT.Dispose,
+				disposeListener);
+	}
+
+	private Listener disposeListener = new Listener() {
+		public void handleEvent(Event event) {
+			SWTObservableListDecorator.this.dispose();
+		}
+	};
+
+	public synchronized void dispose() {
+		WidgetListenerUtil.asyncRemoveListener(widget, SWT.Dispose,
+				disposeListener);
+		this.widget = null;
+		super.dispose();
+	}
+
+	/**
+	 * @return Returns the widget.
+	 */
+	public Widget getWidget() {
+		return widget;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableValueDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableValueDecorator.java
new file mode 100644
index 0000000..22d9e52
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableValueDecorator.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 281723
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.DecoratingObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SWTObservableValueDecorator extends DecoratingObservableValue
+		implements ISWTObservableValue, Listener {
+	private Widget widget;
+
+	/**
+	 * @param decorated
+	 * @param widget
+	 */
+	public SWTObservableValueDecorator(IObservableValue decorated, Widget widget) {
+		super(decorated, true);
+		this.widget = widget;
+		WidgetListenerUtil.asyncAddListener(widget, SWT.Dispose, this);
+	}
+
+	public void handleEvent(Event event) {
+		if (event.type == SWT.Dispose)
+			dispose();
+	}
+
+	public Widget getWidget() {
+		return widget;
+	}
+
+	public synchronized void dispose() {
+		if (widget != null) {
+			WidgetListenerUtil.asyncRemoveListener(widget, SWT.Dispose, this);
+			widget = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTVetoableValueDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTVetoableValueDecorator.java
new file mode 100644
index 0000000..212c234
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTVetoableValueDecorator.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 256543, 190881, 263691, 281723
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.value.DecoratingVetoableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SWTVetoableValueDecorator extends DecoratingVetoableValue
+		implements ISWTObservableValue {
+	private Widget widget;
+	private WidgetStringValueProperty property;
+
+	private Listener verifyListener = new Listener() {
+		public void handleEvent(Event event) {
+			String currentText = (String) property.getValue(widget);
+			String newText = currentText.substring(0, event.start) + event.text
+					+ currentText.substring(event.end);
+			if (!fireValueChanging(Diffs.createValueDiff(currentText, newText))) {
+				event.doit = false;
+			}
+		}
+	};
+
+	private Listener disposeListener = new Listener() {
+		public void handleEvent(Event event) {
+			SWTVetoableValueDecorator.this.dispose();
+		}
+	};
+
+	/**
+	 * @param widget
+	 * @param property
+	 * @param decorated
+	 */
+	public SWTVetoableValueDecorator(Widget widget,
+			WidgetStringValueProperty property, IObservableValue decorated) {
+		super(decorated, true);
+		this.property = property;
+		this.widget = widget;
+		Assert
+				.isTrue(decorated.getValueType().equals(String.class),
+						"SWTVetoableValueDecorator can only decorate observable values of String value type"); //$NON-NLS-1$
+		WidgetListenerUtil.asyncAddListener(widget, SWT.Dispose,
+				disposeListener);
+	}
+
+	protected void firstListenerAdded() {
+		super.firstListenerAdded();
+		WidgetListenerUtil.asyncAddListener(widget, SWT.Verify, verifyListener);
+	}
+
+	protected void lastListenerRemoved() {
+		WidgetListenerUtil.asyncRemoveListener(widget, SWT.Verify,
+				verifyListener);
+		super.lastListenerRemoved();
+	}
+
+	public synchronized void dispose() {
+		WidgetListenerUtil.asyncRemoveListener(widget, SWT.Verify,
+				verifyListener);
+		WidgetListenerUtil.asyncRemoveListener(widget, SWT.Dispose,
+				disposeListener);
+		this.widget = null;
+		super.dispose();
+	}
+
+	public Widget getWidget() {
+		return widget;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleMaximumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleMaximumProperty.java
new file mode 100644
index 0000000..885f3b6
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleMaximumProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Scale;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ScaleMaximumProperty extends WidgetIntValueProperty {
+	int doGetIntValue(Object source) {
+		return ((Scale) source).getMaximum();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Scale) source).setMaximum(value);
+	}
+
+	public String toString() {
+		return "Scale.maximum <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleMinimumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleMinimumProperty.java
new file mode 100644
index 0000000..23513d8
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleMinimumProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Scale;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ScaleMinimumProperty extends WidgetIntValueProperty {
+	int doGetIntValue(Object source) {
+		return ((Scale) source).getMinimum();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Scale) source).setMinimum(value);
+	}
+
+	public String toString() {
+		return "Scale.minimum <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleSelectionProperty.java
new file mode 100644
index 0000000..00591b5
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleSelectionProperty.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Scale;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ScaleSelectionProperty extends WidgetIntValueProperty {
+	/**
+	 * 
+	 */
+	public ScaleSelectionProperty() {
+		super(SWT.Selection);
+	}
+
+	int doGetIntValue(Object source) {
+		return ((Scale) source).getSelection();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Scale) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "Scale.selection <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScrollBarEnabledProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScrollBarEnabledProperty.java
new file mode 100644
index 0000000..241c5c9
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScrollBarEnabledProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 280157)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.ScrollBar;
+
+/**
+ * 
+ */
+public class ScrollBarEnabledProperty extends WidgetBooleanValueProperty {
+	public boolean doGetBooleanValue(Object source) {
+		return ((ScrollBar) source).getEnabled();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((ScrollBar) source).setEnabled(value);
+	}
+
+	public String toString() {
+		return "ScrollBar.enabled <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ShellTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ShellTextProperty.java
new file mode 100644
index 0000000..46d8aa6
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ShellTextProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ShellTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Shell) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Shell) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Shell.text <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SingleSelectionIndexProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SingleSelectionIndexProperty.java
new file mode 100644
index 0000000..742f930
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SingleSelectionIndexProperty.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 288642)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class SingleSelectionIndexProperty extends
+		WidgetIntValueProperty {
+	/**
+	 * @param events
+	 */
+	public SingleSelectionIndexProperty(int[] events) {
+		super(events);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		super.doSetValue(source, value == null ? new Integer(-1) : value);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderMaximumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderMaximumProperty.java
new file mode 100644
index 0000000..fa5ce5a
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderMaximumProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 299123)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Slider;
+
+/**
+ * 
+ */
+public class SliderMaximumProperty extends WidgetIntValueProperty {
+	int doGetIntValue(Object source) {
+		return ((Slider) source).getMaximum();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Slider) source).setMaximum(value);
+	}
+
+	public String toString() {
+		return "Slider.maximum <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderMinimumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderMinimumProperty.java
new file mode 100644
index 0000000..6f36a36
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderMinimumProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 299123)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Slider;
+
+/**
+ * 
+ */
+public class SliderMinimumProperty extends WidgetIntValueProperty {
+	int doGetIntValue(Object source) {
+		return ((Slider) source).getMinimum();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Slider) source).setMinimum(value);
+	}
+
+	public String toString() {
+		return "Slider.minimum <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderSelectionProperty.java
new file mode 100644
index 0000000..99757ce
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SliderSelectionProperty.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 299123)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Slider;
+
+/**
+ * 
+ */
+public class SliderSelectionProperty extends WidgetIntValueProperty {
+	/**
+	 * 
+	 */
+	public SliderSelectionProperty() {
+		super(SWT.Selection);
+	}
+
+	int doGetIntValue(Object source) {
+		return ((Slider) source).getSelection();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Slider) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "Slider.selection <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerMaximumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerMaximumProperty.java
new file mode 100644
index 0000000..2e01bf2
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerMaximumProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SpinnerMaximumProperty extends WidgetIntValueProperty {
+	int doGetIntValue(Object source) {
+		return ((Spinner) source).getMaximum();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Spinner) source).setMaximum(value);
+	}
+
+	public String toString() {
+		return "Spinner.maximum <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerMinimumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerMinimumProperty.java
new file mode 100644
index 0000000..e5b05cb
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerMinimumProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SpinnerMinimumProperty extends WidgetIntValueProperty {
+	int doGetIntValue(Object source) {
+		return ((Spinner) source).getMinimum();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Spinner) source).setMinimum(value);
+	}
+
+	public String toString() {
+		return "Spinner.minimum <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerSelectionProperty.java
new file mode 100644
index 0000000..26ecfe0
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerSelectionProperty.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SpinnerSelectionProperty extends WidgetIntValueProperty {
+	/**
+	 * 
+	 */
+	public SpinnerSelectionProperty() {
+		super(SWT.Modify);
+	}
+
+	int doGetIntValue(Object source) {
+		return ((Spinner) source).getSelection();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		((Spinner) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "Spinner.selection <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/StyledTextTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/StyledTextTextProperty.java
new file mode 100644
index 0000000..a306359
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/StyledTextTextProperty.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 256543, 262287
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class StyledTextTextProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public StyledTextTextProperty() {
+		this(null);
+	}
+
+	/**
+	 * @param events
+	 */
+	public StyledTextTextProperty(int[] events) {
+		super(checkEvents(events), staleEvents(events));
+	}
+
+	private static int[] checkEvents(int[] events) {
+		if (events != null)
+			for (int i = 0; i < events.length; i++)
+				checkEvent(events[i]);
+		return events;
+	}
+
+	private static void checkEvent(int event) {
+		if (event != SWT.None && event != SWT.Modify && event != SWT.FocusOut
+				&& event != SWT.DefaultSelection)
+			throw new IllegalArgumentException("UpdateEventType [" //$NON-NLS-1$
+					+ event + "] is not supported."); //$NON-NLS-1$
+	}
+
+	private static int[] staleEvents(int[] changeEvents) {
+		if (changeEvents != null)
+			for (int i = 0; i < changeEvents.length; i++)
+				if (changeEvents[i] == SWT.Modify)
+					return null;
+		return new int[] { SWT.Modify };
+	}
+
+	String doGetStringValue(Object source) {
+		return ((StyledText) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((StyledText) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "StyledText.text <String>"; //$NON-NLS-1$
+	}
+
+	protected ISWTObservableValue wrapObservable(IObservableValue observable,
+			Widget widget) {
+		return new SWTVetoableValueDecorator(widget, this, observable);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TabItemTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TabItemTooltipTextProperty.java
new file mode 100644
index 0000000..d773fd9
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TabItemTooltipTextProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262946
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.TabItem;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TabItemTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((TabItem) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((TabItem) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "TabItem.toolTipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableColumnTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableColumnTooltipTextProperty.java
new file mode 100644
index 0000000..bf88296
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableColumnTooltipTextProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262946
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TableColumnTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((TableColumn) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((TableColumn) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "TableColumn.toolTipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableSingleSelectionIndexProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableSingleSelectionIndexProperty.java
new file mode 100644
index 0000000..1715338
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableSingleSelectionIndexProperty.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Table;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TableSingleSelectionIndexProperty extends
+		SingleSelectionIndexProperty {
+	/**
+	 * 
+	 */
+	public TableSingleSelectionIndexProperty() {
+		super(new int[] { SWT.Selection, SWT.DefaultSelection });
+	}
+
+	int doGetIntValue(Object source) {
+		return ((Table) source).getSelectionIndex();
+	}
+
+	void doSetIntValue(Object source, int value) {
+		if (value == -1)
+			((Table) source).deselectAll();
+		else
+			((Table) source).setSelection(value);
+	}
+
+	public String toString() {
+		return "Table.selectionIndex <int>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextEditableProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextEditableProperty.java
new file mode 100644
index 0000000..a806510
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextEditableProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TextEditableProperty extends WidgetBooleanValueProperty {
+	boolean doGetBooleanValue(Object source) {
+		return ((Text) source).getEditable();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((Text) source).setEditable(value);
+	}
+
+	public String toString() {
+		return "Text.editable <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextMessageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextMessageProperty.java
new file mode 100644
index 0000000..042f696
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextMessageProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 266563)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TextMessageProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((Text) source).getMessage();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Text) source).setMessage(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Text.message<String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextTextProperty.java
new file mode 100644
index 0000000..ad7f1ad
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextTextProperty.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 256543, 262287
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TextTextProperty extends WidgetStringValueProperty {
+	/**
+	 * 
+	 */
+	public TextTextProperty() {
+		this(null);
+	}
+
+	/**
+	 * @param events
+	 */
+	public TextTextProperty(int[] events) {
+		super(checkEvents(events), staleEvents(events));
+	}
+
+	private static int[] checkEvents(int[] events) {
+		if (events != null)
+			for (int i = 0; i < events.length; i++)
+				checkEvent(events[i]);
+		return events;
+	}
+
+	private static void checkEvent(int event) {
+		if (event != SWT.None && event != SWT.Modify && event != SWT.FocusOut
+				&& event != SWT.DefaultSelection)
+			throw new IllegalArgumentException("UpdateEventType [" //$NON-NLS-1$
+					+ event + "] is not supported."); //$NON-NLS-1$
+	}
+
+	private static int[] staleEvents(int[] changeEvents) {
+		if (changeEvents != null)
+			for (int i = 0; i < changeEvents.length; i++)
+				if (changeEvents[i] == SWT.Modify)
+					return null;
+		return new int[] { SWT.Modify };
+	}
+
+	String doGetStringValue(Object source) {
+		return ((Text) source).getText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((Text) source).setText(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "Text.text <String>"; //$NON-NLS-1$
+	}
+
+	protected ISWTObservableValue wrapObservable(IObservableValue observable,
+			Widget widget) {
+		return new SWTVetoableValueDecorator(widget, this, observable);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolItemEnabledProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolItemEnabledProperty.java
new file mode 100644
index 0000000..9a38502
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolItemEnabledProperty.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 280157)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.ToolItem;
+
+/**
+ * 
+ */
+public class ToolItemEnabledProperty extends WidgetBooleanValueProperty {
+	public boolean doGetBooleanValue(Object source) {
+		return ((ToolItem) source).getEnabled();
+	}
+
+	void doSetBooleanValue(Object source, boolean value) {
+		((ToolItem) source).setEnabled(value);
+	}
+
+	public String toString() {
+		return "ToolItem.enabled <boolean>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolItemTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolItemTooltipTextProperty.java
new file mode 100644
index 0000000..7b19632
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolItemTooltipTextProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262946
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.ToolItem;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ToolItemTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((ToolItem) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((ToolItem) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "ToolItem.toolTipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolTipMessageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolTipMessageProperty.java
new file mode 100644
index 0000000..88dd0ef
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ToolTipMessageProperty.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 266563)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.ToolTip;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ToolTipMessageProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((ToolTip) source).getMessage();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((ToolTip) source).setMessage(value == null ? "" : value); //$NON-NLS-1$
+	}
+
+	public String toString() {
+		return "ToolTip.message<String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TrayItemTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TrayItemTooltipTextProperty.java
new file mode 100644
index 0000000..25c3f89
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TrayItemTooltipTextProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262946
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.TrayItem;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TrayItemTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((TrayItem) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((TrayItem) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "TrayItem.toolTipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TreeColumnTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TreeColumnTooltipTextProperty.java
new file mode 100644
index 0000000..18ab0d1
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TreeColumnTooltipTextProperty.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 262946
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class TreeColumnTooltipTextProperty extends WidgetStringValueProperty {
+	String doGetStringValue(Object source) {
+		return ((TreeColumn) source).getToolTipText();
+	}
+
+	void doSetStringValue(Object source, String value) {
+		((TreeColumn) source).setToolTipText(value);
+	}
+
+	public String toString() {
+		return "TreeColumn.toolTipText <String>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetBooleanValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetBooleanValueProperty.java
new file mode 100644
index 0000000..1036fb2
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetBooleanValueProperty.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class WidgetBooleanValueProperty extends WidgetValueProperty {
+	WidgetBooleanValueProperty() {
+		super();
+	}
+
+	WidgetBooleanValueProperty(int event) {
+		super(event);
+	}
+
+	WidgetBooleanValueProperty(int[] events) {
+		super(events);
+	}
+
+	public Object getValueType() {
+		return Boolean.TYPE;
+	}
+
+	protected Object doGetValue(Object source) {
+		return doGetBooleanValue(source) ? Boolean.TRUE : Boolean.FALSE;
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		if (value == null)
+			value = Boolean.FALSE;
+		doSetBooleanValue(source, ((Boolean) value).booleanValue());
+	}
+
+	abstract boolean doGetBooleanValue(Object source);
+
+	abstract void doSetBooleanValue(Object source, boolean value);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetDelegatingListProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetDelegatingListProperty.java
new file mode 100644
index 0000000..7024c63
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetDelegatingListProperty.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.list.DelegatingListProperty;
+import org.eclipse.jface.databinding.swt.ISWTObservableList;
+import org.eclipse.jface.databinding.swt.IWidgetListProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Widget;
+
+abstract class WidgetDelegatingListProperty extends DelegatingListProperty
+		implements IWidgetListProperty {
+	RuntimeException notSupported(Object source) {
+		return new IllegalArgumentException(
+				"Widget [" + source.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$
+	}
+
+	public WidgetDelegatingListProperty(Object elementType) {
+		super(elementType);
+	}
+
+	public ISWTObservableList observe(Widget widget) {
+		return (ISWTObservableList) observe(SWTObservables.getRealm(widget
+				.getDisplay()), widget);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetDelegatingValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetDelegatingValueProperty.java
new file mode 100644
index 0000000..1b7e82c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetDelegatingValueProperty.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.DelegatingValueProperty;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.IWidgetValueProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Widget;
+
+abstract class WidgetDelegatingValueProperty extends DelegatingValueProperty
+		implements IWidgetValueProperty {
+	RuntimeException notSupported(Object source) {
+		return new IllegalArgumentException(
+				"Widget [" + source.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$
+	}
+
+	public WidgetDelegatingValueProperty() {
+	}
+
+	public WidgetDelegatingValueProperty(Object valueType) {
+		super(valueType);
+	}
+
+	public ISWTObservableValue observe(Widget widget) {
+		return (ISWTObservableValue) observe(SWTObservables.getRealm(widget
+				.getDisplay()), widget);
+	}
+
+	public ISWTObservableValue observeDelayed(int delay, Widget widget) {
+		return SWTObservables.observeDelayedValue(delay, observe(widget));
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetEditableProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetEditableProperty.java
new file mode 100644
index 0000000..60ebd82
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetEditableProperty.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetEditableProperty extends WidgetDelegatingValueProperty {
+	IValueProperty text;
+
+	/**
+	 * 
+	 */
+	public WidgetEditableProperty() {
+		super(Boolean.TYPE);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Text) {
+			if (text == null)
+				text = new TextEditableProperty();
+			return text;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetEnabledProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetEnabledProperty.java
new file mode 100644
index 0000000..d49112f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetEnabledProperty.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+/**
+ * 
+ */
+public class WidgetEnabledProperty extends WidgetDelegatingValueProperty {
+	IValueProperty control;
+	IValueProperty menu;
+	IValueProperty menuItem;
+	IValueProperty scrollBar;
+	IValueProperty toolItem;
+
+	/**
+	 * 
+	 */
+	public WidgetEnabledProperty() {
+		super(Boolean.TYPE);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Control) {
+			if (control == null)
+				control = new ControlEnabledProperty();
+			return control;
+		}
+		if (source instanceof Menu) {
+			if (menu == null)
+				menu = new MenuEnabledProperty();
+			return menu;
+		}
+		if (source instanceof MenuItem) {
+			if (menuItem == null)
+				menuItem = new MenuItemEnabledProperty();
+			return menuItem;
+		}
+		if (source instanceof ScrollBar) {
+			if (scrollBar == null)
+				scrollBar = new ScrollBarEnabledProperty();
+			return scrollBar;
+		}
+		if (source instanceof ToolItem) {
+			if (toolItem == null)
+				toolItem = new ToolItemEnabledProperty();
+			return toolItem;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetImageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetImageProperty.java
new file mode 100644
index 0000000..2197b08
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetImageProperty.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetImageProperty extends WidgetDelegatingValueProperty {
+	private IValueProperty button;
+	private IValueProperty cLabel;
+	private IValueProperty item;
+	private IValueProperty label;
+
+	/**
+	 * 
+	 */
+	public WidgetImageProperty() {
+		super(Image.class);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Button) {
+			if (button == null)
+				button = new ButtonImageProperty();
+			return button;
+		}
+		if (source instanceof CLabel) {
+			if (cLabel == null)
+				cLabel = new CLabelImageProperty();
+			return cLabel;
+		}
+		if (source instanceof Item) {
+			if (item == null)
+				item = new ItemImageProperty();
+			return item;
+		}
+		if (source instanceof Label) {
+			if (label == null)
+				label = new LabelImageProperty();
+			return label;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetImageValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetImageValueProperty.java
new file mode 100644
index 0000000..07e0e3f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetImageValueProperty.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 213893)
+ *     Matthew Hall - bug 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class WidgetImageValueProperty extends WidgetValueProperty {
+	public Object getValueType() {
+		return Image.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return doGetImageValue(source);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		doSetImageValue(source, (Image) value);
+	}
+
+	abstract Image doGetImageValue(Object source);
+
+	abstract void doSetImageValue(Object source, Image value);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetIntValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetIntValueProperty.java
new file mode 100644
index 0000000..6134d84
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetIntValueProperty.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class WidgetIntValueProperty extends WidgetValueProperty {
+	WidgetIntValueProperty() {
+		super();
+	}
+
+	WidgetIntValueProperty(int event) {
+		super(event);
+	}
+
+	WidgetIntValueProperty(int[] events) {
+		super(events);
+	}
+
+	public Object getValueType() {
+		return Integer.TYPE;
+	}
+
+	protected Object doGetValue(Object source) {
+		return new Integer(doGetIntValue(source));
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		doSetIntValue(source, ((Integer) value).intValue());
+	}
+
+	abstract int doGetIntValue(Object source);
+
+	abstract void doSetIntValue(Object source, int intValue);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetItemsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetItemsProperty.java
new file mode 100644
index 0000000..96567be
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetItemsProperty.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.List;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetItemsProperty extends WidgetDelegatingListProperty {
+	private IListProperty cCombo;
+	private IListProperty combo;
+	private IListProperty list;
+
+	/**
+	 * 
+	 */
+	public WidgetItemsProperty() {
+		super(String.class);
+	}
+
+	protected IListProperty doGetDelegate(Object source) {
+		if (source instanceof CCombo) {
+			if (cCombo == null)
+				cCombo = new CComboItemsProperty();
+			return cCombo;
+		}
+		if (source instanceof Combo) {
+			if (combo == null)
+				combo = new ComboItemsProperty();
+			return combo;
+		}
+		if (source instanceof List) {
+			if (list == null)
+				list = new ListItemsProperty();
+			return list;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetListener.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetListener.java
new file mode 100644
index 0000000..6fb036a
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetListener.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2008-2009 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
+ *         (from WidgetValueProperty.java)
+ *     Matthew Hall - bug 294810
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.3
+ */
+public class WidgetListener extends NativePropertyListener implements Listener {
+	private final int[] changeEvents;
+	private final int[] staleEvents;
+
+	/**
+	 * @param property
+	 * @param listener
+	 * @param changeEvents
+	 * @param staleEvents
+	 */
+	public WidgetListener(IProperty property, ISimplePropertyListener listener,
+			int[] changeEvents, int[] staleEvents) {
+		super(property, listener);
+		this.changeEvents = changeEvents;
+		this.staleEvents = staleEvents;
+	}
+
+	public void handleEvent(Event event) {
+		if (staleEvents != null)
+			for (int i = 0; i < staleEvents.length; i++)
+				if (event.type == staleEvents[i]) {
+					fireStale(event.widget);
+					break;
+				}
+
+		if (changeEvents != null)
+			for (int i = 0; i < changeEvents.length; i++)
+				if (event.type == changeEvents[i]) {
+					fireChange(event.widget, null);
+					break;
+				}
+	}
+
+	protected void doAddTo(Object source) {
+		Widget widget = (Widget) source;
+		if (changeEvents != null) {
+			for (int i = 0; i < changeEvents.length; i++) {
+				int event = changeEvents[i];
+				if (event != SWT.None) {
+					WidgetListenerUtil.asyncAddListener(widget, event, this);
+				}
+			}
+		}
+		if (staleEvents != null) {
+			for (int i = 0; i < staleEvents.length; i++) {
+				int event = staleEvents[i];
+				if (event != SWT.None) {
+					WidgetListenerUtil.asyncAddListener(widget, event, this);
+				}
+			}
+		}
+	}
+
+	protected void doRemoveFrom(Object source) {
+		Widget widget = (Widget) source;
+		if (!widget.isDisposed()) {
+			if (changeEvents != null) {
+				for (int i = 0; i < changeEvents.length; i++) {
+					int event = changeEvents[i];
+					if (event != SWT.None)
+						WidgetListenerUtil.asyncRemoveListener(widget, event,
+								this);
+				}
+			}
+			if (staleEvents != null) {
+				for (int i = 0; i < staleEvents.length; i++) {
+					int event = staleEvents[i];
+					if (event != SWT.None) {
+						WidgetListenerUtil.asyncRemoveListener(widget, event,
+								this);
+					}
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetListenerUtil.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetListenerUtil.java
new file mode 100644
index 0000000..0314565
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetListenerUtil.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 281723)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.4
+ * 
+ */
+public class WidgetListenerUtil {
+	/**
+	 * @param widget
+	 * @param event
+	 * @param listener
+	 */
+	public static void asyncAddListener(final Widget widget, final int event,
+			final Listener listener) {
+		if (widget == null)
+			return;
+		if (widget.isDisposed())
+			return;
+
+		Display display = widget.getDisplay();
+		if (display == Display.getCurrent()) {
+			widget.addListener(event, listener);
+		} else {
+			SWTObservables.getRealm(display).exec(new Runnable() {
+				public void run() {
+					if (!widget.isDisposed())
+						widget.addListener(event, listener);
+				}
+			});
+		}
+	}
+
+	/**
+	 * @param widget
+	 * @param event
+	 * @param listener
+	 */
+	public static void asyncRemoveListener(final Widget widget,
+			final int event, final Listener listener) {
+		if (widget == null)
+			return;
+		if (widget.isDisposed())
+			return;
+
+		Display display = widget.getDisplay();
+		if (display == Display.getCurrent()) {
+			widget.removeListener(event, listener);
+		} else {
+			SWTObservables.getRealm(display).exec(new Runnable() {
+				public void run() {
+					if (!widget.isDisposed())
+						widget.removeListener(event, listener);
+				}
+			});
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMaximumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMaximumProperty.java
new file mode 100644
index 0000000..ee1d67e
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMaximumProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetMaximumProperty extends WidgetDelegatingValueProperty {
+	private IValueProperty scale;
+	private IValueProperty slider;
+	private IValueProperty spinner;
+
+	/**
+	 * 
+	 */
+	public WidgetMaximumProperty() {
+		super(Integer.TYPE);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Scale) {
+			if (scale == null)
+				scale = new ScaleMaximumProperty();
+			return scale;
+		}
+		if (source instanceof Slider) {
+			if (slider == null) {
+				slider = new SliderMaximumProperty();
+			}
+			return slider;
+		}
+		if (source instanceof Spinner) {
+			if (spinner == null)
+				spinner = new SpinnerMaximumProperty();
+			return spinner;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMessageProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMessageProperty.java
new file mode 100644
index 0000000..1352a61
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMessageProperty.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 266563)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolTip;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetMessageProperty extends WidgetDelegatingValueProperty {
+	private IValueProperty text;
+	private IValueProperty toolTip;
+
+	/**
+	 * 
+	 */
+	public WidgetMessageProperty() {
+		super(String.class);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Text) {
+			if (text == null)
+				text = new TextMessageProperty();
+			return text;
+		}
+		if (source instanceof ToolTip) {
+			if (toolTip == null)
+				toolTip = new ToolTipMessageProperty();
+			return toolTip;
+		}
+		throw notSupported(source);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMinimumProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMinimumProperty.java
new file mode 100644
index 0000000..efa8539
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetMinimumProperty.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetMinimumProperty extends WidgetDelegatingValueProperty {
+	private IValueProperty scale;
+	private IValueProperty slider;
+	private IValueProperty spinner;
+
+	/**
+	 * 
+	 */
+	public WidgetMinimumProperty() {
+		super(Integer.TYPE);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Scale) {
+			if (scale == null)
+				scale = new ScaleMinimumProperty();
+			return scale;
+		}
+		if (source instanceof Slider) {
+			if (slider == null) {
+				slider = new SliderMinimumProperty();
+			}
+			return slider;
+		}
+		if (source instanceof Spinner) {
+			if (spinner == null)
+				spinner = new SpinnerMinimumProperty();
+			return spinner;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetSelectionProperty.java
new file mode 100644
index 0000000..7a5ca0d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetSelectionProperty.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *     Matthew Hall - bug 169876
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class WidgetSelectionProperty extends
+		WidgetDelegatingValueProperty {
+	private IValueProperty button;
+	private IValueProperty cCombo;
+	private IValueProperty combo;
+	private IValueProperty dateTime;
+	private IValueProperty list;
+	private IValueProperty menuItem;
+	private IValueProperty scale;
+	private IValueProperty slider;
+	private IValueProperty spinner;
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Button) {
+			if (button == null)
+				button = new ButtonSelectionProperty();
+			return button;
+		}
+		if (source instanceof CCombo) {
+			if (cCombo == null)
+				cCombo = new CComboSelectionProperty();
+			return cCombo;
+		}
+		if (source instanceof Combo) {
+			if (combo == null)
+				combo = new ComboSelectionProperty();
+			return combo;
+		}
+		if (source instanceof DateTime) {
+			if (dateTime == null)
+				dateTime = new DateTimeSelectionProperty();
+			return dateTime;
+		}
+		if (source instanceof List) {
+			if (list == null)
+				list = new ListSelectionProperty();
+			return list;
+		}
+		if (source instanceof MenuItem) {
+			if (menuItem == null)
+				menuItem = new MenuItemSelectionProperty();
+			return menuItem;
+		}
+		if (source instanceof Scale) {
+			if (scale == null)
+				scale = new ScaleSelectionProperty();
+			return scale;
+		}
+		if (source instanceof Slider) {
+			if (slider == null)
+				slider = new SliderSelectionProperty();
+			return slider;
+		}
+		if (source instanceof Spinner) {
+			if (spinner == null)
+				spinner = new SpinnerSelectionProperty();
+			return spinner;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetSingleSelectionIndexProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetSingleSelectionIndexProperty.java
new file mode 100644
index 0000000..22f32e5
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetSingleSelectionIndexProperty.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Table;
+
+/**
+ * @since 3.3
+ * 
+ */
+public final class WidgetSingleSelectionIndexProperty extends
+		WidgetDelegatingValueProperty {
+	private IValueProperty cCombo;
+	private IValueProperty combo;
+	private IValueProperty list;
+	private IValueProperty table;
+
+	/**
+	 * 
+	 */
+	public WidgetSingleSelectionIndexProperty() {
+		super(Integer.TYPE);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof CCombo) {
+			if (cCombo == null)
+				cCombo = new CComboSingleSelectionIndexProperty();
+			return cCombo;
+		}
+		if (source instanceof Combo) {
+			if (combo == null)
+				combo = new ComboSingleSelectionIndexProperty();
+			return combo;
+		}
+		if (source instanceof List) {
+			if (list == null)
+				list = new ListSingleSelectionIndexProperty();
+			return list;
+		}
+		if (source instanceof Table) {
+			if (table == null)
+				table = new TableSingleSelectionIndexProperty();
+			return table;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetStringValueProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetStringValueProperty.java
new file mode 100644
index 0000000..241256f
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetStringValueProperty.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bugs 195222, 256543, 263413, 262287
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.swt.WidgetValueProperty;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class WidgetStringValueProperty extends WidgetValueProperty {
+	WidgetStringValueProperty() {
+		super();
+	}
+
+	WidgetStringValueProperty(int event) {
+		super(event);
+	}
+
+	WidgetStringValueProperty(int[] events) {
+		super(events);
+	}
+
+	WidgetStringValueProperty(int[] events, int[] staleEvents) {
+		super(events, staleEvents);
+	}
+
+	public Object getValueType() {
+		return String.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return doGetStringValue(source);
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		doSetStringValue(source, (String) value);
+	}
+
+	abstract String doGetStringValue(Object source);
+
+	abstract void doSetStringValue(Object source, String value);
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTextProperty.java
new file mode 100644
index 0000000..51d0189
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTextProperty.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetTextProperty extends WidgetDelegatingValueProperty {
+	private IValueProperty button;
+	private IValueProperty cCombo;
+	private IValueProperty cLabel;
+	private IValueProperty combo;
+	private IValueProperty item;
+	private IValueProperty label;
+	private IValueProperty link;
+	private IValueProperty shell;
+	private IValueProperty styledText;
+	private IValueProperty text;
+
+	/**
+	 * 
+	 */
+	public WidgetTextProperty() {
+		super(String.class);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof Button) {
+			if (button == null)
+				button = new ButtonTextProperty();
+			return button;
+		}
+		if (source instanceof CCombo) {
+			if (cCombo == null)
+				cCombo = new CComboTextProperty();
+			return cCombo;
+		}
+		if (source instanceof CLabel) {
+			if (cLabel == null)
+				cLabel = new CLabelTextProperty();
+			return cLabel;
+		}
+		if (source instanceof Combo) {
+			if (combo == null)
+				combo = new ComboTextProperty();
+			return combo;
+		}
+		if (source instanceof Item) {
+			if (item == null)
+				item = new ItemTextProperty();
+			return item;
+		}
+		if (source instanceof Label) {
+			if (label == null)
+				label = new LabelTextProperty();
+			return label;
+		}
+		if (source instanceof Link) {
+			if (link == null)
+				link = new LinkTextProperty();
+			return link;
+		}
+		if (source instanceof Shell) {
+			if (shell == null)
+				shell = new ShellTextProperty();
+			return shell;
+		}
+		if (source instanceof StyledText) {
+			if (styledText == null)
+				styledText = new StyledTextTextProperty();
+			return styledText;
+		}
+		if (source instanceof Text) {
+			if (text == null)
+				text = new TextTextProperty();
+			return text;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTextWithEventsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTextWithEventsProperty.java
new file mode 100644
index 0000000..0dd91df
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTextWithEventsProperty.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetTextWithEventsProperty extends WidgetDelegatingValueProperty {
+	private final int[] events;
+
+	private IValueProperty styledText;
+	private IValueProperty text;
+
+	/**
+	 * @param events
+	 */
+	public WidgetTextWithEventsProperty(int[] events) {
+		super(String.class);
+		this.events = checkEvents(events);
+	}
+
+	private static int[] checkEvents(int[] events) {
+		for (int i = 0; i < events.length; i++)
+			checkEvent(events[i]);
+		return events;
+	}
+
+	private static void checkEvent(int event) {
+		if (event != SWT.None && event != SWT.Modify && event != SWT.FocusOut
+				&& event != SWT.DefaultSelection)
+			throw new IllegalArgumentException("UpdateEventType [" //$NON-NLS-1$
+					+ event + "] is not supported."); //$NON-NLS-1$
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof StyledText) {
+			if (styledText == null)
+				styledText = new StyledTextTextProperty(events);
+			return styledText;
+		}
+		if (source instanceof Text) {
+			if (text == null)
+				text = new TextTextProperty(events);
+			return text;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTooltipTextProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTooltipTextProperty.java
new file mode 100644
index 0000000..6ebb7e0
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/WidgetTooltipTextProperty.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.swt;
+
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.TrayItem;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class WidgetTooltipTextProperty extends WidgetDelegatingValueProperty {
+	private IValueProperty cTabItem;
+	private IValueProperty control;
+	private IValueProperty tabItem;
+	private IValueProperty tableColumn;
+	private IValueProperty toolItem;
+	private IValueProperty trayItem;
+	private IValueProperty treeColumn;
+
+	/**
+	 * 
+	 */
+	public WidgetTooltipTextProperty() {
+		super(String.class);
+	}
+
+	protected IValueProperty doGetDelegate(Object source) {
+		if (source instanceof CTabItem) {
+			if (cTabItem == null)
+				cTabItem = new CTabItemTooltipTextProperty();
+			return cTabItem;
+		}
+		if (source instanceof Control) {
+			if (control == null)
+				control = new ControlTooltipTextProperty();
+			return control;
+		}
+		if (source instanceof TabItem) {
+			if (tabItem == null)
+				tabItem = new TabItemTooltipTextProperty();
+			return tabItem;
+		}
+		if (source instanceof TableColumn) {
+			if (tableColumn == null)
+				tableColumn = new TableColumnTooltipTextProperty();
+			return tableColumn;
+		}
+		if (source instanceof ToolItem) {
+			if (toolItem == null)
+				toolItem = new ToolItemTooltipTextProperty();
+			return toolItem;
+		}
+		if (source instanceof TrayItem) {
+			if (trayItem == null)
+				trayItem = new TrayItemTooltipTextProperty();
+			return trayItem;
+		}
+		if (source instanceof TreeColumn) {
+			if (treeColumn == null)
+				treeColumn = new TreeColumnTooltipTextProperty();
+			return treeColumn;
+		}
+		throw notSupported(source);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/util/JFaceProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/util/JFaceProperty.java
new file mode 100644
index 0000000..3a16be3
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/util/JFaceProperty.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 278314
+ *******************************************************************************/
+package org.eclipse.jface.internal.databinding.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+/**
+ * Class that supports the use of {@link IObservableValue} with objects that
+ * follow standard bean method naming conventions but notify an
+ * {@link IPropertyChangeListener} when the property changes.
+ */
+public class JFaceProperty extends SimpleValueProperty {
+
+	private Class returnType;
+	private Method setterMethod;
+	private Method getterMethod;
+	private final String property;
+	private Method removePropertyListenerMethod;
+	private Method addPropertyListenerMethod;
+
+	private static String getSetterName(String fieldName) {
+		return "set" + toMethodSuffix(fieldName); //$NON-NLS-1$
+	}
+
+	private static String getGetterName(String fieldName) {
+		return "get" + toMethodSuffix(fieldName); //$NON-NLS-1$
+	}
+
+	private static String getBooleanGetterName(String fieldName) {
+		return "is" + toMethodSuffix(fieldName); //$NON-NLS-1$
+	}
+
+	private static String toMethodSuffix(String fieldName) {
+		if (Character.isLowerCase(fieldName.charAt(0))) {
+			return Character.toUpperCase(fieldName.charAt(0))
+					+ fieldName.substring(1);
+		}
+		return fieldName;
+	}
+
+	class Listener extends NativePropertyListener implements
+			IPropertyChangeListener {
+		public Listener(ISimplePropertyListener listener) {
+			super(JFaceProperty.this, listener);
+		}
+
+		public void propertyChange(PropertyChangeEvent event) {
+			if (event.getProperty().equals(JFaceProperty.this.property)) {
+				fireChange(event.getSource(), null);
+			}
+		}
+
+		protected void doAddTo(Object model) {
+			try {
+				addPropertyListenerMethod.invoke(model, new Object[] { this });
+			} catch (Exception e) {
+				throw new IllegalStateException(e.getMessage());
+			}
+		}
+
+		protected void doRemoveFrom(Object model) {
+			try {
+				removePropertyListenerMethod.invoke(model,
+						new Object[] { this });
+			} catch (Exception e) {
+				throw new IllegalStateException(e.getMessage());
+			}
+		}
+	}
+
+	/**
+	 * @param fieldName
+	 * @param property
+	 * @param clazz
+	 */
+	public JFaceProperty(String fieldName, String property, Class clazz) {
+		this.property = property;
+		// Create all the necessary method ahead of time to ensure they are
+		// available
+		try {
+			try {
+				String getterName = getGetterName(fieldName);
+				getterMethod = clazz.getMethod(getterName, new Class[] {});
+			} catch (NoSuchMethodException e) {
+				String getterName = getBooleanGetterName(fieldName);
+				getterMethod = clazz.getMethod(getterName, new Class[] {});
+			}
+			returnType = getterMethod.getReturnType();
+			setterMethod = clazz.getMethod(getSetterName(fieldName),
+					new Class[] { returnType });
+			addPropertyListenerMethod = clazz
+					.getMethod(
+							"addPropertyChangeListener", new Class[] { IPropertyChangeListener.class }); //$NON-NLS-1$
+			removePropertyListenerMethod = clazz
+					.getMethod(
+							"removePropertyChangeListener", new Class[] { IPropertyChangeListener.class }); //$NON-NLS-1$
+		} catch (SecurityException e) {
+			throw new IllegalArgumentException();
+		} catch (NoSuchMethodException e) {
+			throw new IllegalArgumentException();
+		}
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return new Listener(listener);
+	}
+
+	protected Object doGetValue(Object model) {
+		try {
+			return getterMethod.invoke(model, new Object[] {});
+		} catch (InvocationTargetException e) {
+			throw new IllegalStateException(e.getMessage());
+		} catch (IllegalAccessException e) {
+			throw new IllegalStateException(e.getMessage());
+		}
+	}
+
+	protected void doSetValue(Object model, Object value) {
+		try {
+			setterMethod.invoke(model, new Object[] { value });
+		} catch (IllegalAccessException e) {
+			throw new IllegalStateException(e.getMessage());
+		} catch (InvocationTargetException e) {
+			throw new IllegalStateException(e.getMessage());
+		}
+	}
+
+	public Object getValueType() {
+		return returnType;
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CellEditorControlProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CellEditorControlProperty.java
new file mode 100644
index 0000000..a56611b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CellEditorControlProperty.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 234496)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.value.SimpleValueProperty;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CellEditorControlProperty extends SimpleValueProperty {
+	public Object getValueType() {
+		return Control.class;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((CellEditor) source).getControl();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		throw new UnsupportedOperationException();
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	public String toString() {
+		return super.toString();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsObservableSet.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsObservableSet.java
new file mode 100644
index 0000000..09a29fa
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsObservableSet.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bug 259380, 283204
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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.runtime.Assert;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.IElementComparer;
+
+/**
+ * 
+ * @since 1.2
+ */
+public class CheckableCheckedElementsObservableSet extends
+		AbstractObservableSet {
+	private ICheckable checkable;
+	private Set wrappedSet;
+	private Object elementType;
+	private IElementComparer elementComparer;
+	private ICheckStateListener listener;
+
+	/**
+	 * Constructs a new instance of the given realm, and checkable,
+	 * 
+	 * @param realm
+	 *            the observable's realm
+	 * @param wrappedSet
+	 *            the set being wrapped
+	 * @param elementType
+	 *            type of elements in the set
+	 * @param elementComparer
+	 *            element comparer
+	 * @param checkable
+	 *            the ICheckable to track
+	 */
+	public CheckableCheckedElementsObservableSet(Realm realm,
+			final Set wrappedSet, Object elementType,
+			IElementComparer elementComparer, ICheckable checkable) {
+		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;
+		this.elementComparer = elementComparer;
+
+		listener = new ICheckStateListener() {
+			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 ViewerElementSet.withComparer(elementComparer);
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	public boolean add(Object o) {
+		getterCalled();
+		boolean added = wrappedSet.add(o);
+		if (added) {
+			checkable.setChecked(o, true);
+			fireSetChange(Diffs.createSetDiff(Collections.singleton(o),
+					Collections.EMPTY_SET));
+		}
+		return added;
+	}
+
+	public boolean remove(Object o) {
+		getterCalled();
+		boolean removed = wrappedSet.remove(o);
+		if (removed) {
+			checkable.setChecked(o, false);
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET,
+					Collections.singleton(o)));
+		}
+		return removed;
+	}
+
+	public boolean 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);
+			}
+		}
+		boolean changed = !additions.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET));
+		return changed;
+	}
+
+	public boolean 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);
+			}
+		}
+		boolean changed = !removals.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+		return changed;
+	}
+
+	public boolean 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);
+			}
+		}
+		boolean changed = !removals.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+		return changed;
+	}
+
+	public void clear() {
+		getterCalled();
+		Set removals = createDiffSet();
+		removals.addAll(wrappedSet);
+		removeAll(removals);
+	}
+
+	public Iterator iterator() {
+		getterCalled();
+		final Iterator wrappedIterator = wrappedSet.iterator();
+		return new Iterator() {
+			private Object last = null;
+
+			public boolean 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 != null) {
+			checkable.removeCheckStateListener(listener);
+			checkable = null;
+			listener = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsProperty.java
new file mode 100644
index 0000000..dedb0c1
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsProperty.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 259380)
+ *     Matthew Hall - bug 283204
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.set.SetProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CheckableCheckedElementsProperty extends SetProperty {
+	private final Object elementType;
+
+	/**
+	 * @param elementType
+	 */
+	public CheckableCheckedElementsProperty(Object elementType) {
+		this.elementType = elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected Set doGetSet(Object source) {
+		throw new UnsupportedOperationException(
+				"Cannot query the checked elements on an ICheckable"); //$NON-NLS-1$
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		throw new UnsupportedOperationException(
+				"Cannot batch replace the checked elements on an ICheckable.  " + //$NON-NLS-1$
+						"Use updateSet(SetDiff) instead"); //$NON-NLS-1$
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		ICheckable checkable = (ICheckable) source;
+		for (Iterator it = diff.getAdditions().iterator(); it.hasNext();)
+			checkable.setChecked(it.next(), true);
+		for (Iterator it = diff.getRemovals().iterator(); it.hasNext();)
+			checkable.setChecked(it.next(), false);
+	}
+
+	public IObservableSet observe(Object source) {
+		if (source instanceof Viewer) {
+			return observe(SWTObservables.getRealm(((Viewer) source)
+					.getControl().getDisplay()), source);
+		}
+		return super.observe(source);
+	}
+
+	public IObservableSet observe(Realm realm, Object source) {
+		IElementComparer comparer = null;
+		if (source instanceof StructuredViewer)
+			comparer = ((StructuredViewer) source).getComparer();
+		Set wrappedSet = ViewerElementSet.withComparer(comparer);
+		IObservableSet observable = new CheckableCheckedElementsObservableSet(
+				realm, wrappedSet, elementType, comparer, (ICheckable) source);
+		if (source instanceof Viewer)
+			observable = new ViewerObservableSetDecorator(observable,
+					(Viewer) source);
+		return observable;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTableViewerCheckedElementsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTableViewerCheckedElementsProperty.java
new file mode 100644
index 0000000..786fac2
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTableViewerCheckedElementsProperty.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 259380
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CheckboxTableViewerCheckedElementsProperty extends
+		CheckboxViewerCheckedElementsProperty {
+	/**
+	 * @param elementType
+	 */
+	public CheckboxTableViewerCheckedElementsProperty(Object elementType) {
+		super(elementType);
+	}
+
+	protected Set doGetSet(Object source) {
+		CheckboxTableViewer viewer = (CheckboxTableViewer) source;
+		Set set = createElementSet(viewer);
+		set.addAll(Arrays.asList(viewer.getCheckedElements()));
+		return set;
+	}
+
+	protected void doSetSet(Object source, Set set, SetDiff diff) {
+		doSetSet(source, set);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		CheckboxTableViewer viewer = (CheckboxTableViewer) source;
+		viewer.setCheckedElements(set.toArray());
+	}
+
+	public String toString() {
+		String s = "CheckboxTableViewer.checkedElements{}"; //$NON-NLS-1$
+		if (getElementType() != null)
+			s += " <" + getElementType() + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTableViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTableViewerUpdater.java
new file mode 100644
index 0000000..55554db
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTableViewerUpdater.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 226292)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CheckboxTableViewerUpdater extends TableViewerUpdater {
+	private final CheckboxTableViewer checkboxViewer;
+
+	CheckboxTableViewerUpdater(CheckboxTableViewer viewer) {
+		super(viewer);
+		checkboxViewer = viewer;
+	}
+
+	public void move(Object element, int oldPosition, int newPosition) {
+		if (isElementOrderPreserved()) {
+			boolean wasChecked = checkboxViewer.getChecked(element);
+			boolean wasGrayed = checkboxViewer.getGrayed(element);
+			super.move(element, oldPosition, newPosition);
+			checkboxViewer.setChecked(element, wasChecked);
+			checkboxViewer.setGrayed(element, wasGrayed);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTreeViewerCheckedElementsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTreeViewerCheckedElementsProperty.java
new file mode 100644
index 0000000..c54268c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTreeViewerCheckedElementsProperty.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 259380
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CheckboxTreeViewerCheckedElementsProperty extends
+		CheckboxViewerCheckedElementsProperty {
+	/**
+	 * @param elementType
+	 */
+	public CheckboxTreeViewerCheckedElementsProperty(Object elementType) {
+		super(elementType);
+	}
+
+	protected Set doGetSet(Object source) {
+		CheckboxTreeViewer viewer = (CheckboxTreeViewer) source;
+		Set set = createElementSet(viewer);
+		set.addAll(Arrays.asList(viewer.getCheckedElements()));
+		return set;
+	}
+
+	protected void doSetSet(Object source, Set set, SetDiff diff) {
+		doSetSet(source, set);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		CheckboxTreeViewer viewer = (CheckboxTreeViewer) source;
+		viewer.setCheckedElements(set.toArray());
+	}
+
+	public String toString() {
+		String s = "CheckboxTreeViewer.checkedElements{}"; //$NON-NLS-1$
+		if (getElementType() != null)
+			s += " <" + getElementType() + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTreeViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTreeViewerUpdater.java
new file mode 100644
index 0000000..2ec867c
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxTreeViewerUpdater.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 226292)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class CheckboxTreeViewerUpdater extends TreeViewerUpdater {
+	private final CheckboxTreeViewer checkboxViewer;
+
+	CheckboxTreeViewerUpdater(CheckboxTreeViewer viewer) {
+		super(viewer);
+		checkboxViewer = viewer;
+	}
+
+	public void move(Object parent, Object element, int oldPosition,
+			int newPosition) {
+		if (isElementOrderPreserved()) {
+			boolean wasChecked = checkboxViewer.getChecked(element);
+			boolean wasGrayed = checkboxViewer.getGrayed(element);
+			super.move(parent, element, oldPosition, newPosition);
+			checkboxViewer.setChecked(element, wasChecked);
+			checkboxViewer.setGrayed(element, wasGrayed);
+		}
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxViewerCheckedElementsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxViewerCheckedElementsProperty.java
new file mode 100644
index 0000000..afb1c3e
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxViewerCheckedElementsProperty.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 259380, 263413, 265561
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+import org.eclipse.jface.databinding.viewers.ViewerSetProperty;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ICheckable;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public abstract class CheckboxViewerCheckedElementsProperty extends
+		ViewerSetProperty {
+	private final Object elementType;
+
+	/**
+	 * @param elementType
+	 */
+	public CheckboxViewerCheckedElementsProperty(Object elementType) {
+		this.elementType = elementType;
+	}
+
+	public Object getElementType() {
+		return elementType;
+	}
+
+	protected final Set createElementSet(StructuredViewer viewer) {
+		return ViewerElementSet.withComparer(viewer.getComparer());
+	}
+
+	protected void doUpdateSet(Object source, SetDiff diff) {
+		ICheckable checkable = (ICheckable) source;
+		for (Iterator it = diff.getAdditions().iterator(); it.hasNext();)
+			checkable.setChecked(it.next(), true);
+		for (Iterator it = diff.getRemovals().iterator(); it.hasNext();)
+			checkable.setChecked(it.next(), false);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return new CheckStateListener(this, listener);
+	}
+
+	private class CheckStateListener extends NativePropertyListener implements
+			ICheckStateListener {
+		private CheckStateListener(IProperty property,
+				ISimplePropertyListener listener) {
+			super(property, listener);
+		}
+
+		public void checkStateChanged(CheckStateChangedEvent event) {
+			Object element = event.getElement();
+			boolean checked = event.getChecked();
+			Set elementSet = createElementSet((StructuredViewer) event
+					.getCheckable());
+			elementSet.add(element);
+			Set additions = checked ? elementSet : Collections.EMPTY_SET;
+			Set removals = checked ? Collections.EMPTY_SET : elementSet;
+			SetDiff diff = Diffs.createSetDiff(additions, removals);
+			fireChange(event.getSource(), diff);
+		}
+
+		public void doAddTo(Object source) {
+			((ICheckable) source).addCheckStateListener(this);
+		}
+
+		public void doRemoveFrom(Object source) {
+			((ICheckable) source).removeCheckStateListener(this);
+		}
+	}
+
+	public String toString() {
+		String s = "ICheckable.checkedElements{}"; //$NON-NLS-1$
+		if (elementType != null)
+			s += " <" + elementType + ">"; //$NON-NLS-1$//$NON-NLS-2$
+		return s;
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ListViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ListViewerUpdater.java
new file mode 100644
index 0000000..015669d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ListViewerUpdater.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.jface.viewers.AbstractListViewer;
+
+/**
+ * NON-API - A {@link ViewerUpdater} that updates {@link AbstractListViewer}
+ * instances.
+ * 
+ * @since 1.2
+ */
+class ListViewerUpdater extends ViewerUpdater {
+	private AbstractListViewer viewer;
+
+	ListViewerUpdater(AbstractListViewer viewer) {
+		super(viewer);
+		this.viewer = viewer;
+	}
+
+	public void insert(Object element, int position) {
+		viewer.insert(element, position);
+	}
+
+	public void remove(Object element, int position) {
+		viewer.remove(element);
+	}
+
+	public void add(Object[] elements) {
+		viewer.add(elements);
+	}
+
+	public void remove(Object[] elements) {
+		viewer.remove(elements);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionContentProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionContentProvider.java
new file mode 100644
index 0000000..a98b17b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionContentProvider.java
@@ -0,0 +1,273 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 - bugs 226765, 222991, 238296, 263956, 226292, 265051,
+ *                    266038
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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.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.IViewerUpdater;
+import org.eclipse.jface.viewers.AbstractListViewer;
+import org.eclipse.jface.viewers.AbstractTableViewer;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+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 implements
+		IStructuredContentProvider {
+	private Display display;
+
+	private IObservableValue viewerObservable;
+
+	/**
+	 * Element comparer used by the viewer (may be null).
+	 */
+	protected IElementComparer comparer;
+
+	private IObservableFactory elementSetFactory;
+
+	private final IViewerUpdater explicitViewerUpdater;
+
+	/**
+	 * Interface for sending updates to the viewer.
+	 */
+	protected IViewerUpdater 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;
+
+	/**
+	 * Observable set of known elements which have been realized in the viewer.
+	 * Subclasses must add new elements to this set <b>after</b> adding them to
+	 * the viewer, and must remove old elements from this set <b>before</b>
+	 * removing them from the viewer.
+	 */
+	protected IObservableSet realizedElements;
+	private IObservableSet unmodifiableRealizedElements;
+
+	private IObservableCollection observableCollection;
+
+	/**
+	 * Constructs an ObservableCollectionContentProvider
+	 * 
+	 * @param explicitViewerUpdater
+	 */
+	protected ObservableCollectionContentProvider(
+			IViewerUpdater explicitViewerUpdater) {
+		this.explicitViewerUpdater = explicitViewerUpdater;
+
+		display = Display.getDefault();
+		viewerObservable = new WritableValue(SWTObservables.getRealm(display));
+		viewerUpdater = null;
+
+		elementSetFactory = new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				IElementComparer comparer = null;
+				if (target instanceof StructuredViewer)
+					comparer = ((StructuredViewer) target).getComparer();
+				return ObservableViewerElementSet.withComparer(SWTObservables
+						.getRealm(display), null, comparer);
+			}
+		};
+		knownElements = MasterDetailObservables.detailSet(viewerObservable,
+				elementSetFactory, null);
+		unmodifiableKnownElements = Observables
+				.unmodifiableObservableSet(knownElements);
+
+		observableCollection = null;
+	}
+
+	public Object[] getElements(Object inputElement) {
+		if (observableCollection == null)
+			return new Object[0];
+
+		knownElements.addAll(observableCollection);
+		if (realizedElements != null) {
+			if (!realizedElements.equals(knownElements)) {
+				asyncUpdateRealizedElements();
+			}
+		}
+
+		return observableCollection.toArray();
+	}
+
+	private void asyncUpdateRealizedElements() {
+		if (realizedElements == null)
+			return;
+		display.asyncExec(new Runnable() {
+			public void run() {
+				if (realizedElements != null) {
+					realizedElements.addAll(knownElements);
+				}
+			}
+		});
+	}
+
+	public void dispose() {
+		if (observableCollection != null)
+			removeCollectionChangeListener(observableCollection);
+
+		if (viewerObservable != null) {
+			viewerObservable.dispose();
+			viewerObservable = null;
+		}
+		viewerUpdater = null;
+		knownElements = null;
+		unmodifiableKnownElements = null;
+		realizedElements = null;
+		unmodifiableRealizedElements = null;
+		display = 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 (viewer instanceof StructuredViewer)
+			return ((StructuredViewer) viewer).getComparer();
+		return null;
+	}
+
+	IViewerUpdater createViewerUpdater(Viewer viewer) {
+		if (explicitViewerUpdater != null)
+			return explicitViewerUpdater;
+		if (viewer instanceof AbstractListViewer)
+			return new ListViewerUpdater((AbstractListViewer) viewer);
+		if (viewer instanceof CheckboxTableViewer)
+			return new CheckboxTableViewerUpdater((CheckboxTableViewer) viewer);
+		if (viewer instanceof AbstractTableViewer)
+			return new TableViewerUpdater((AbstractTableViewer) viewer);
+		throw new IllegalArgumentException(
+				"This content provider only works with AbstractTableViewer or AbstractListViewer"); //$NON-NLS-1$
+	}
+
+	void setInput(Object input) {
+		if (observableCollection != null) {
+			removeCollectionChangeListener(observableCollection);
+			observableCollection = null;
+		}
+
+		knownElements.clear();
+		if (realizedElements != null)
+			realizedElements.clear();
+
+		if (input != null) {
+			checkInput(input);
+			Assert.isTrue(input instanceof IObservableCollection,
+					"Input must be an IObservableCollection"); //$NON-NLS-1$
+			observableCollection = (IObservableCollection) input;
+			addCollectionChangeListener(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 boolean isViewerDisposed() {
+		Viewer viewer = (Viewer) viewerObservable.getValue();
+		return viewer == null || viewer.getControl() == 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;
+	}
+
+	/**
+	 * Returns the set of known elements which have been realized in the viewer.
+	 * Clients may track this set in order to perform custom actions on elements
+	 * while they are known to be present in the viewer.
+	 * 
+	 * @return the set of known elements which have been realized in the viewer.
+	 * @since 1.3
+	 */
+	public IObservableSet getRealizedElements() {
+		if (realizedElements == null) {
+			realizedElements = MasterDetailObservables.detailSet(
+					viewerObservable, elementSetFactory, null);
+			unmodifiableRealizedElements = Observables
+					.unmodifiableObservableSet(realizedElements);
+			asyncUpdateRealizedElements();
+		}
+		return unmodifiableRealizedElements;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.java
new file mode 100644
index 0000000..aea0b53
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.java
@@ -0,0 +1,513 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 - bugs 226765, 239015, 222991, 263693, 263956, 226292,
+ *                    266038
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Collection;
+import java.util.Collections;
+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.CheckboxTreeViewer;
+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 implements
+		ITreeContentProvider {
+	private Realm realm;
+
+	private Display display;
+
+	private IObservableValue viewerObservable;
+
+	/**
+	 * Element comparer used by the viewer (may be null).
+	 */
+	protected IElementComparer comparer;
+
+	private IObservableFactory elementSetFactory;
+
+	/**
+	 * Interfaces for sending updates to the viewer.
+	 */
+	protected TreeViewerUpdater 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;
+
+	/**
+	 * Observable set of known elements which have been realized in the viewer.
+	 * Subclasses must add new elements to this set <b>after</b> adding them to
+	 * the viewer, and must remove old elements from this set <b>before</b>
+	 * removing them from the viewer.
+	 */
+	protected IObservableSet realizedElements;
+	private IObservableSet unmodifiableRealizedElements;
+
+	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 ObservableCollectionTreeContentProvider(
+			IObservableFactory collectionFactory,
+			TreeStructureAdvisor structureAdvisor) {
+		this.structureAdvisor = structureAdvisor;
+		display = Display.getDefault();
+		realm = SWTObservables.getRealm(display);
+		viewerObservable = new WritableValue(realm);
+		viewerUpdater = null;
+
+		elementSetFactory = new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return ObservableViewerElementSet.withComparer(realm, null,
+						getElementComparer((Viewer) target));
+			}
+		};
+		knownElements = MasterDetailObservables.detailSet(viewerObservable,
+				elementSetFactory, 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 != 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);
+
+		knownElements.clear();
+		if (realizedElements != null)
+			realizedElements.clear();
+	}
+
+	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 (viewer instanceof StructuredViewer)
+			return ((StructuredViewer) viewer).getComparer();
+		return null;
+	}
+
+	private static TreeViewerUpdater createViewerUpdater(Viewer viewer) {
+		if (viewer instanceof CheckboxTreeViewer)
+			return new CheckboxTreeViewerUpdater((CheckboxTreeViewer) viewer);
+		if (viewer instanceof AbstractTreeViewer)
+			return new TreeViewerUpdater((AbstractTreeViewer) viewer);
+		throw new IllegalArgumentException(
+				"This content provider only works with AbstractTreeViewer"); //$NON-NLS-1$
+	}
+
+	public Object getParent(Object element) {
+		if (structureAdvisor != null) {
+			Object parentFromAdvisor = structureAdvisor.getParent(element);
+			if (parentFromAdvisor != null) {
+				return parentFromAdvisor;
+			}
+		}
+		TreeNode node = getExistingNode(element);
+		if (node != null)
+			return node.getParent();
+		return null;
+	}
+
+	public Object[] getElements(Object input) {
+		return getChildren(input, true);
+	}
+
+	public Object[] getChildren(Object element) {
+		return getChildren(element, false);
+	}
+
+	private Object[] getChildren(Object element, boolean input) {
+		TreeNode node = getOrCreateNode(element, input);
+		Object[] children = node.getChildren().toArray();
+		for (int i = 0; i < children.length; i++)
+			getOrCreateNode(children[i], false).addParent(element);
+		knownElements.addAll(node.getChildren());
+		asyncUpdateRealizedElements();
+		return children;
+	}
+
+	boolean asyncUpdatePending;
+	Runnable asyncUpdateRunnable;
+
+	private void asyncUpdateRealizedElements() {
+		if (realizedElements == null)
+			return;
+		if (asyncUpdatePending)
+			return;
+		if (!realizedElements.equals(knownElements)) {
+			if (asyncUpdateRunnable == null) {
+				asyncUpdateRunnable = new Runnable() {
+					public void run() {
+						asyncUpdatePending = false;
+						if (realizedElements != null) {
+							realizedElements.addAll(knownElements);
+						}
+					}
+				};
+			}
+			asyncUpdatePending = true;
+			display.asyncExec(asyncUpdateRunnable);
+		}
+	}
+
+	public boolean hasChildren(Object element) {
+		if (structureAdvisor != null) {
+			Boolean hasChildren = structureAdvisor.hasChildren(element);
+			if (hasChildren != null) {
+				return hasChildren.booleanValue();
+			}
+		}
+		return getOrCreateNode(element, false).hasChildren();
+	}
+
+	protected TreeNode getOrCreateNode(Object element) {
+		return getOrCreateNode(element, false);
+	}
+
+	private TreeNode getOrCreateNode(Object element, boolean input) {
+		TreeNode node = getExistingNode(element);
+		if (node == null) {
+			node = new TreeNode(element);
+			elementNodes.put(element, node);
+		}
+		// In case the input element is also a visible node in the tree.
+		if (!input)
+			knownElements.add(element);
+		return node;
+	}
+
+	protected TreeNode getExistingNode(Object element) {
+		TreeNode node = (TreeNode) elementNodes.get(element);
+		return node;
+	}
+
+	protected boolean isViewerDisposed() {
+		Viewer viewer = (Viewer) viewerObservable.getValue();
+		return viewer == null || viewer.getControl() == null
+				|| viewer.getControl().isDisposed();
+	}
+
+	public void dispose() {
+		if (elementNodes != 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 != null) {
+			viewerObservable.dispose();
+			viewerObservable = null;
+		}
+		viewerUpdater = null;
+		comparer = null;
+		knownElements = null;
+		unmodifiableKnownElements = null;
+		collectionFactory = null;
+		asyncUpdateRunnable = 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 the set of known elements which have been realized in the viewer.
+	 * Clients may track this set in order to perform custom actions on elements
+	 * while they are known to be present in the viewer.
+	 * 
+	 * @return the set of known elements which have been realized in the viewer.
+	 * @since 1.3
+	 */
+	public IObservableSet getRealizedElements() {
+		if (realizedElements == null) {
+			realizedElements = MasterDetailObservables.detailSet(
+					viewerObservable, elementSetFactory, null);
+			unmodifiableRealizedElements = Observables
+					.unmodifiableObservableSet(realizedElements);
+			asyncUpdateRealizedElements();
+		}
+		return unmodifiableRealizedElements;
+	}
+
+	/**
+	 * Returns the set of all elements that would be removed from the known
+	 * elements set if the given elements were removed as children of the given
+	 * parent element.
+	 * 
+	 * @param parent
+	 *            the parent element of the elements being removed
+	 * @param elementsToBeRemoved
+	 *            the elements being removed
+	 * @return the set of all elements that would be removed from the known
+	 *         elements set
+	 */
+	protected Set findPendingRemovals(Object parent,
+			Collection elementsToBeRemoved) {
+		Set result = ViewerElementSet.withComparer(comparer);
+		Set parents = ViewerElementSet.withComparer(comparer);
+		parents.add(parent);
+		accumulatePendingRemovals(result, parents, elementsToBeRemoved);
+		return result;
+	}
+
+	private void accumulatePendingRemovals(Set removals, Set parents,
+			Collection elementsToRemove) {
+		for (Iterator it = elementsToRemove.iterator(); it.hasNext();) {
+			Object element = it.next();
+			TreeNode node = getExistingNode(element);
+			if (node != null) {
+				if (parents.containsAll(node.getParents())) {
+					removals.add(element);
+					parents.add(element);
+					Collection children = node.getChildren();
+					accumulatePendingRemovals(removals, parents, children);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 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 boolean equal(Object left, Object right) {
+		if (comparer == null)
+			return Util.equals(left, right);
+		return comparer.equals(left, right);
+	}
+
+	protected final class TreeNode {
+		private Object element;
+
+		private Object parent;
+		private Set parentSet;
+
+		private IObservableCollection children;
+
+		private IObservablesListener listener;
+
+		TreeNode(Object element) {
+			Assert.isNotNull(element, "element cannot be null"); //$NON-NLS-1$
+			this.element = element;
+		}
+
+		Object getElement() {
+			return element;
+		}
+
+		public void addParent(Object newParent) {
+			if (parent == null) {
+				parent = newParent;
+			} else if (!equal(parent, newParent)) {
+				if (parentSet == null) {
+					parentSet = ViewerElementSet.withComparer(comparer);
+					parentSet.add(parent);
+				}
+				parentSet.add(newParent);
+			}
+		}
+
+		public void removeParent(Object oldParent) {
+			if (parentSet != null) {
+				parentSet.remove(oldParent);
+				if (parentSet.isEmpty())
+					parentSet = null;
+			}
+
+			if (equal(parent, oldParent)) {
+				if (parentSet == null) {
+					parent = null;
+				} else {
+					parent = parentSet.iterator().next();
+				}
+			}
+
+			if (parent == null) {
+				dispose();
+			}
+		}
+
+		private Object getParent() {
+			return parent;
+		}
+
+		public Set getParents() {
+			if (parentSet != null)
+				return parentSet;
+			if (parent != null)
+				return Collections.singleton(parent);
+			return Collections.EMPTY_SET;
+		}
+
+		private void initChildren() {
+			if (children == null) {
+				children = (IObservableCollection) collectionFactory
+						.createObservable(element);
+				if (children == 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);
+				}
+			}
+		}
+
+		boolean hasChildren() {
+			initChildren();
+			return !children.isEmpty();
+		}
+
+		public Collection getChildren() {
+			initChildren();
+			return children;
+		}
+
+		private void dispose() {
+			if (element != null) {
+				elementNodes.remove(element);
+			}
+			if (children != null && !children.isDisposed()) {
+				for (Iterator iterator = children.iterator(); iterator
+						.hasNext();) {
+					TreeNode child = getExistingNode(iterator.next());
+					if (child != null)
+						child.removeParent(element);
+				}
+				if (listener != null)
+					removeCollectionChangeListener(children, listener);
+				children.dispose();
+				children = null;
+			}
+			element = null;
+			parent = null;
+			if (parentSet != null) {
+				parentSet.clear();
+				parentSet = null;
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.java
new file mode 100644
index 0000000..2f8b197
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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 extends 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 ObservableViewerElementSet(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 Iterator() {
+			Object last;
+
+			public boolean 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 boolean add(Object o) {
+		getterCalled();
+		boolean changed = wrappedSet.add(o);
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.singleton(o),
+					Collections.EMPTY_SET));
+		return changed;
+	}
+
+	public boolean 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);
+		}
+		boolean changed = !additions.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET));
+		return changed;
+	}
+
+	public boolean remove(Object o) {
+		getterCalled();
+		boolean changed = wrappedSet.remove(o);
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET,
+					Collections.singleton(o)));
+		return changed;
+	}
+
+	public boolean 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);
+		}
+		boolean changed = !removals.isEmpty();
+		if (changed)
+			fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+		return changed;
+	}
+
+	public boolean 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);
+		}
+		boolean 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 == null)
+			return new WritableSet(realm, Collections.EMPTY_SET, elementType);
+		return new ObservableViewerElementSet(realm, elementType, comparer);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionChangedListener.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionChangedListener.java
new file mode 100644
index 0000000..3c2d21b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionChangedListener.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 265561)
+ *     Ovidio Mallo - bug 270494
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+class SelectionChangedListener extends NativePropertyListener implements
+		ISelectionChangedListener {
+
+	private final boolean isPostSelection;
+
+	SelectionChangedListener(IProperty property,
+			ISimplePropertyListener listener, boolean isPostSelection) {
+		super(property, listener);
+		this.isPostSelection = isPostSelection;
+	}
+
+	public void selectionChanged(SelectionChangedEvent event) {
+		fireChange(event.getSource(), null);
+	}
+
+	public void doAddTo(Object source) {
+		if (isPostSelection) {
+			((IPostSelectionProvider) source)
+					.addPostSelectionChangedListener(this);
+		} else {
+			((ISelectionProvider) source).addSelectionChangedListener(this);
+		}
+	}
+
+	public void doRemoveFrom(Object source) {
+		if (isPostSelection) {
+			((IPostSelectionProvider) source)
+					.removePostSelectionChangedListener(this);
+		} else {
+			((ISelectionProvider) source).removeSelectionChangedListener(this);
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionProperty.java
new file mode 100644
index 0000000..88cbb41
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionProperty.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 263413, 265561
+ *     Ovidio Mallo - bug 270494
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.jface.databinding.viewers.ViewerListProperty;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SelectionProviderMultipleSelectionProperty extends
+		ViewerListProperty {
+
+	private final boolean isPostSelection;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param isPostSelection
+	 *            Whether the post selection or the normal selection is to be
+	 *            observed.
+	 */
+	public SelectionProviderMultipleSelectionProperty(boolean isPostSelection) {
+		this.isPostSelection = isPostSelection;
+	}
+
+	public Object getElementType() {
+		return Object.class;
+	}
+
+	protected List doGetList(Object source) {
+		ISelection selection = ((ISelectionProvider) source).getSelection();
+		if (selection instanceof IStructuredSelection) {
+			return ((IStructuredSelection) selection).toList();
+		}
+		return Collections.EMPTY_LIST;
+	}
+
+	protected void doSetList(Object source, List list, ListDiff diff) {
+		doSetList(source, list);
+	}
+
+	protected void doSetList(Object source, List list) {
+		((ISelectionProvider) source)
+				.setSelection(new StructuredSelection(list));
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return new SelectionChangedListener(this, listener, isPostSelection);
+	}
+
+	public String toString() {
+		return isPostSelection ? "IPostSelectionProvider.postSelection[]" //$NON-NLS-1$
+				: "ISelectionProvider.selection[]"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionProperty.java
new file mode 100644
index 0000000..758fa2d
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionProperty.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bugs 195222, 263413, 265561, 271080
+ *     Ovidio Mallo - bug 270494
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.jface.databinding.viewers.ViewerValueProperty;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class SelectionProviderSingleSelectionProperty extends
+		ViewerValueProperty {
+
+	private final boolean isPostSelection;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param isPostSelection
+	 *            Whether the post selection or the normal selection is to be
+	 *            observed.
+	 */
+	public SelectionProviderSingleSelectionProperty(boolean isPostSelection) {
+		this.isPostSelection = isPostSelection;
+	}
+
+	public Object getValueType() {
+		return null;
+	}
+
+	protected Object doGetValue(Object source) {
+		ISelection selection = ((ISelectionProvider) source).getSelection();
+		if (selection instanceof IStructuredSelection) {
+			return ((IStructuredSelection) selection).getFirstElement();
+		}
+		return null;
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		IStructuredSelection selection = value == null ? StructuredSelection.EMPTY
+				: new StructuredSelection(value);
+		if (source instanceof Viewer) {
+			((Viewer) source).setSelection(selection, true);
+		} else {
+			((ISelectionProvider) source).setSelection(selection);
+		}
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return new SelectionChangedListener(this, listener, isPostSelection);
+	}
+
+	public String toString() {
+		return isPostSelection ? "IPostSelectionProvider.postSelection" //$NON-NLS-1$
+				: "ISelectionProvider.selection"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/StructuredViewerFiltersProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/StructuredViewerFiltersProperty.java
new file mode 100644
index 0000000..731ea43
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/StructuredViewerFiltersProperty.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 194734)
+ *     Matthew Hall - bug 195222, 263413, 265561
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.jface.databinding.viewers.ViewerSetProperty;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class StructuredViewerFiltersProperty extends ViewerSetProperty {
+	public Object getElementType() {
+		return ViewerFilter.class;
+	}
+
+	protected Set doGetSet(Object source) {
+		return new HashSet(Arrays.asList(((StructuredViewer) source)
+				.getFilters()));
+	}
+
+	public void doSetSet(Object source, Set set, SetDiff diff) {
+		doSetSet(source, set);
+	}
+
+	protected void doSetSet(Object source, Set set) {
+		StructuredViewer viewer = (StructuredViewer) source;
+		viewer.getControl().setRedraw(false);
+		try {
+			viewer.setFilters((ViewerFilter[]) set.toArray(new ViewerFilter[set
+					.size()]));
+		} finally {
+			viewer.getControl().setRedraw(true);
+		}
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	public String toString() {
+		return "StructuredViewer.filters{} <ViewerFilter>"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.java
new file mode 100644
index 0000000..25a8a10
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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 - bugs 226765, 230296
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.jface.viewers.AbstractTableViewer;
+
+/**
+ * NON-API - A {@link ViewerUpdater} that updates {@link AbstractTableViewer}
+ * instances.
+ * 
+ * @since 1.2
+ */
+class TableViewerUpdater extends ViewerUpdater {
+	private AbstractTableViewer viewer;
+
+	TableViewerUpdater(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 (isElementOrderPreserved())
+			viewer.replace(newElement, position);
+		else {
+			super.replace(oldElement, newElement, position);
+		}
+	}
+
+	public void add(Object[] elements) {
+		viewer.add(elements);
+	}
+
+	public void remove(Object[] elements) {
+		viewer.remove(elements);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.java
new file mode 100644
index 0000000..5052d94
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 - bugs 226765, 230296, 226292, 312926
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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 TreeViewerUpdater(AbstractTreeViewer viewer) {
+		this.viewer = viewer;
+		if (viewer instanceof TreeViewer)
+			treeViewer = (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 != null && isElementOrderPreserved()) {
+			treeViewer.replace(parent, position, newElement);
+			treeViewer.refresh(newElement);
+		} else {
+			remove(parent, oldElement, position);
+			insert(parent, newElement, position);
+		}
+	}
+
+	boolean isElementOrderPreserved() {
+		return viewer.getComparator() == null
+				&& viewer.getFilters().length == 0;
+	}
+
+	/**
+	 * 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 (isElementOrderPreserved()) {
+			ITreeSelection selection = (ITreeSelection) viewer.getSelection();
+
+			remove(parent, element, oldPosition);
+			insert(parent, element, newPosition);
+
+			// If the moved element is selected (or is an ancestor of a selected
+			// element), restore the selection.
+			if (selectionContains(selection, parent, element))
+				viewer.setSelection(selection);
+		}
+	}
+
+	private boolean selectionContains(ITreeSelection selection, Object parent,
+			Object element) {
+		if (!selection.isEmpty()) {
+			IElementComparer comparer = viewer.getComparer();
+			TreePath[] paths = selection.getPaths();
+			for (int i = 0; i < paths.length; i++) {
+				TreePath path = paths[i];
+				for (int j = 0; j < path.getSegmentCount() - 1; j++) {
+					Object pathParent = path.getSegment(j);
+					Object pathElement = path.getSegment(j + 1);
+					if (eq(comparer, parent, pathParent)
+							&& eq(comparer, element, pathElement)) {
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	private boolean eq(IElementComparer comparer, Object o1, Object o2) {
+		return comparer == null ? Util.equals(o1, o2) : comparer.equals(o1, o2);
+	}
+
+	/**
+	 * 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 != null && viewer.getComparator() == null
+				&& viewer.getFilters().length == 0) {
+			// Only TreeViewer has a remove-by-index method.  
+			treeViewer.remove(parent, position);
+		} else {
+			viewer.remove(parent, new Object[] { element });
+		}
+	}
+
+	/**
+	 * Add the elements into the viewer as children of the specified parent
+	 * element.
+	 * 
+	 * @param parent
+	 *            the parent of the element being inserted
+	 * @param elements
+	 *            the elements to insert
+	 */
+	public void add(Object parent, Object[] elements) {
+		viewer.add(parent, elements);
+	}
+
+	/**
+	 * Remove the elements from the viewer wherever they appear as children of
+	 * the specified parent element.
+	 * 
+	 * @param parent
+	 *            the parent of the elements being removed
+	 * @param elements
+	 *            the elements to remove
+	 */
+	public void remove(Object parent, Object[] elements) {
+		viewer.remove(parent, elements);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerCheckedElementsProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerCheckedElementsProperty.java
new file mode 100644
index 0000000..956e9c0
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerCheckedElementsProperty.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 264286)
+ *******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.property.set.DelegatingSetProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.IViewerObservableSet;
+import org.eclipse.jface.databinding.viewers.IViewerSetProperty;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ViewerCheckedElementsProperty extends DelegatingSetProperty
+		implements IViewerSetProperty {
+	ISetProperty checkable;
+	ISetProperty checkboxTableViewer;
+	ISetProperty checkboxTreeViewer;
+
+	/**
+	 * @param elementType
+	 */
+	public ViewerCheckedElementsProperty(Object elementType) {
+		super(elementType);
+		checkable = new CheckableCheckedElementsProperty(elementType);
+		checkboxTableViewer = new CheckboxTableViewerCheckedElementsProperty(
+				elementType);
+		checkboxTreeViewer = new CheckboxTreeViewerCheckedElementsProperty(
+				elementType);
+	}
+
+	protected ISetProperty doGetDelegate(Object source) {
+		if (source instanceof CheckboxTableViewer)
+			return checkboxTableViewer;
+		if (source instanceof CheckboxTreeViewer)
+			return checkboxTreeViewer;
+		return checkable;
+	}
+
+	public IViewerObservableSet observe(Viewer viewer) {
+		return (IViewerObservableSet) observe(SWTObservables.getRealm(viewer
+				.getControl().getDisplay()), viewer);
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.java
new file mode 100644
index 0000000..6a8a03b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.java
@@ -0,0 +1,432 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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 implements 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 ViewerElementMap(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 ViewerElementMap(Map map, IElementComparer comparer) {
+		this(comparer);
+		Assert.isNotNull(map);
+		putAll(map);
+	}
+
+	public void clear() {
+		wrappedMap.clear();
+	}
+
+	public boolean containsKey(Object key) {
+		return wrappedMap.containsKey(new ViewerElementWrapper(key, comparer));
+	}
+
+	public boolean containsValue(Object value) {
+		return wrappedMap.containsValue(value);
+	}
+
+	public Set entrySet() {
+		final Set wrappedEntrySet = wrappedMap.entrySet();
+		return new Set() {
+			public boolean add(Object o) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean addAll(Collection c) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void clear() {
+				wrappedEntrySet.clear();
+			}
+
+			public boolean contains(Object o) {
+				for (Iterator iterator = iterator(); iterator.hasNext();)
+					if (iterator.next().equals(o))
+						return true;
+				return false;
+			}
+
+			public boolean containsAll(Collection c) {
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					if (!contains(iterator.next()))
+						return false;
+				return true;
+			}
+
+			public boolean isEmpty() {
+				return wrappedEntrySet.isEmpty();
+			}
+
+			public Iterator iterator() {
+				final Iterator wrappedIterator = wrappedEntrySet.iterator();
+				return new Iterator() {
+					public boolean hasNext() {
+						return wrappedIterator.hasNext();
+					}
+
+					public Object next() {
+						final Map.Entry wrappedEntry = (Map.Entry) wrappedIterator
+								.next();
+						return new Map.Entry() {
+							public Object getKey() {
+								return ((ViewerElementWrapper) wrappedEntry.getKey())
+										.unwrap();
+							}
+
+							public Object getValue() {
+								return wrappedEntry.getValue();
+							}
+
+							public Object setValue(Object value) {
+								return wrappedEntry.setValue(value);
+							}
+
+							public boolean equals(Object obj) {
+								if (obj == this)
+									return true;
+								if (obj == null || !(obj instanceof Map.Entry))
+									return false;
+								Map.Entry that = (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 boolean remove(Object o) {
+				final Map.Entry unwrappedEntry = (Map.Entry) o;
+				final ViewerElementWrapper wrappedKey = new ViewerElementWrapper(
+						unwrappedEntry.getKey(), comparer);
+				Map.Entry wrappedEntry = new Map.Entry() {
+					public Object getKey() {
+						return wrappedKey;
+					}
+
+					public Object getValue() {
+						return unwrappedEntry.getValue();
+					}
+
+					public Object setValue(Object value) {
+						throw new UnsupportedOperationException();
+					}
+
+					public boolean equals(Object obj) {
+						if (obj == this)
+							return true;
+						if (obj == null || !(obj instanceof Map.Entry))
+							return false;
+						Map.Entry that = (Map.Entry) obj;
+						return Util.equals(wrappedKey, that.getKey())
+								&& Util
+										.equals(this.getValue(), that
+												.getValue());
+					}
+
+					public int hashCode() {
+						return wrappedKey.hashCode()
+								^ (getValue() == null ? 0 : getValue()
+										.hashCode());
+					}
+				};
+				return wrappedEntrySet.remove(wrappedEntry);
+			}
+
+			public boolean removeAll(Collection c) {
+				boolean changed = false;
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					changed |= remove(iterator.next());
+				return changed;
+			}
+
+			public boolean retainAll(Collection c) {
+				boolean 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 = (Object[]) Array.newInstance(a.getClass()
+							.getComponentType(), size);
+				}
+				int i = 0;
+				for (Iterator iterator = iterator(); iterator.hasNext();) {
+					a[i] = iterator.next();
+					i++;
+				}
+				return a;
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this)
+					return true;
+				if (obj == null || !(obj instanceof Set))
+					return false;
+				Set that = (Set) obj;
+				return this.size() == that.size() && containsAll(that);
+			}
+
+			public int hashCode() {
+				return wrappedEntrySet.hashCode();
+			}
+		};
+	}
+
+	public Object get(Object key) {
+		return wrappedMap.get(new ViewerElementWrapper(key, comparer));
+	}
+
+	public boolean isEmpty() {
+		return wrappedMap.isEmpty();
+	}
+
+	public Set keySet() {
+		final Set wrappedKeySet = wrappedMap.keySet();
+		return new Set() {
+			public boolean add(Object o) {
+				throw new UnsupportedOperationException();
+			}
+
+			public boolean addAll(Collection c) {
+				throw new UnsupportedOperationException();
+			}
+
+			public void clear() {
+				wrappedKeySet.clear();
+			}
+
+			public boolean contains(Object o) {
+				return wrappedKeySet.contains(new ViewerElementWrapper(o, comparer));
+			}
+
+			public boolean containsAll(Collection c) {
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					if (!wrappedKeySet.contains(new ViewerElementWrapper(iterator.next(), comparer)))
+						return false;
+				return true;
+			}
+
+			public boolean isEmpty() {
+				return wrappedKeySet.isEmpty();
+			}
+
+			public Iterator iterator() {
+				final Iterator wrappedIterator = wrappedKeySet.iterator();
+				return new Iterator() {
+					public boolean hasNext() {
+						return wrappedIterator.hasNext();
+					}
+
+					public Object next() {
+						return ((ViewerElementWrapper) wrappedIterator.next()).unwrap();
+					}
+
+					public void remove() {
+						wrappedIterator.remove();
+					}
+				};
+			}
+
+			public boolean remove(Object o) {
+				return wrappedKeySet.remove(new ViewerElementWrapper(o, comparer));
+			}
+
+			public boolean removeAll(Collection c) {
+				boolean changed = false;
+				for (Iterator iterator = c.iterator(); iterator.hasNext();)
+					changed |= wrappedKeySet
+							.remove(new ViewerElementWrapper(iterator.next(), comparer));
+				return changed;
+			}
+
+			public boolean retainAll(Collection c) {
+				boolean 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 = (ViewerElementWrapper[]) wrappedKeySet
+						.toArray(new ViewerElementWrapper[size]);
+				Object[] result = a;
+				if (a.length < size) {
+					result = (Object[]) Array.newInstance(a.getClass()
+							.getComponentType(), size);
+				}
+				for (int i = 0; i < size; i++)
+					result[i] = wrappedArray[i].unwrap();
+				return result;
+			}
+
+			public boolean equals(Object obj) {
+				if (obj == this)
+					return true;
+				if (obj == null || !(obj instanceof Set))
+					return false;
+				Set that = (Set) obj;
+				return this.size() == 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 = (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 boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (obj == null || !(obj instanceof Map))
+			return false;
+		Map that = (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 == null)
+			return new HashMap();
+		return new ViewerElementMap(comparer);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.java
new file mode 100644
index 0000000..b9a0b1e
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.java
@@ -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 124684
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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 implements 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 ViewerElementSet(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 ViewerElementSet(Collection collection, IElementComparer comparer) {
+		this(comparer);
+		addAll(collection);
+	}
+
+	public boolean add(Object o) {
+		return wrappedSet.add(new ViewerElementWrapper(o, comparer));
+	}
+
+	public boolean addAll(Collection c) {
+		boolean 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 boolean contains(Object o) {
+		return wrappedSet.contains(new ViewerElementWrapper(o, comparer));
+	}
+
+	public boolean containsAll(Collection c) {
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			if (!wrappedSet.contains(new ViewerElementWrapper(iterator.next(),
+					comparer)))
+				return false;
+		return true;
+	}
+
+	public boolean isEmpty() {
+		return wrappedSet.isEmpty();
+	}
+
+	public Iterator iterator() {
+		final Iterator wrappedIterator = wrappedSet.iterator();
+		return new Iterator() {
+			public boolean hasNext() {
+				return wrappedIterator.hasNext();
+			}
+
+			public Object next() {
+				return ((ViewerElementWrapper) wrappedIterator.next()).unwrap();
+			}
+
+			public void remove() {
+				wrappedIterator.remove();
+			}
+		};
+	}
+
+	public boolean remove(Object o) {
+		return wrappedSet.remove(new ViewerElementWrapper(o, comparer));
+	}
+
+	public boolean removeAll(Collection c) {
+		boolean changed = false;
+		for (Iterator iterator = c.iterator(); iterator.hasNext();)
+			changed |= remove(iterator.next());
+		return changed;
+	}
+
+	public boolean 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.
+		boolean 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 = (ViewerElementWrapper[]) wrappedSet
+				.toArray(new ViewerElementWrapper[size]);
+		Object[] result = a;
+		if (a.length < size) {
+			result = (Object[]) Array.newInstance(a.getClass()
+					.getComponentType(), size);
+		}
+		for (int i = 0; i < size; i++)
+			result[i] = wrappedArray[i].unwrap();
+		return result;
+	}
+
+	public boolean equals(Object obj) {
+		if (obj == this)
+			return true;
+		if (!(obj instanceof Set))
+			return false;
+		Set that = (Set) obj;
+		return size() == that.size() && containsAll(that);
+	}
+
+	public int hashCode() {
+		int hash = 0;
+		for (Iterator iterator = iterator(); iterator.hasNext();) {
+			Object element = iterator.next();
+			hash += element == 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 == null)
+			return new HashSet();
+		return new ViewerElementSet(comparer);
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.java
new file mode 100644
index 0000000..c2645ae
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.java
@@ -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)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+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 ViewerElementWrapper(Object element, IElementComparer comparer) {
+		if (comparer == null)
+			throw new NullPointerException();
+		this.element = element;
+		this.comparer = comparer;
+	}
+
+	public boolean equals(Object obj) {
+		if (!(obj instanceof ViewerElementWrapper)) {
+			return false;
+		}
+		return comparer.equals(element, ((ViewerElementWrapper) obj).element);
+	}
+
+	public int hashCode() {
+		return comparer.hashCode(element);
+	}
+
+	Object unwrap() {
+		return element;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputProperty.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputProperty.java
new file mode 100644
index 0000000..55baadf
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputProperty.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ *     Matthew Hall - bug 195222, 263413
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.jface.databinding.viewers.ViewerValueProperty;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ViewerInputProperty extends ViewerValueProperty {
+	public Object getValueType() {
+		return null;
+	}
+
+	protected Object doGetValue(Object source) {
+		return ((Viewer) source).getInput();
+	}
+
+	protected void doSetValue(Object source, Object value) {
+		((Viewer) source).setInput(value);
+	}
+
+	public INativePropertyListener adaptListener(
+			ISimplePropertyListener listener) {
+		return null;
+	}
+
+	protected void doAddListener(Object source, INativePropertyListener listener) {
+	}
+
+	protected void doRemoveListener(Object source,
+			INativePropertyListener listener) {
+	}
+
+	public String toString() {
+		return "Viewer.input"; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableListDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableListDecorator.java
new file mode 100644
index 0000000..91463b6
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableListDecorator.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.list.DecoratingObservableList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.jface.databinding.viewers.IViewerObservableList;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ViewerObservableListDecorator extends DecoratingObservableList
+		implements IViewerObservableList {
+	private final Viewer viewer;
+
+	/**
+	 * @param decorated
+	 * @param viewer
+	 */
+	public ViewerObservableListDecorator(IObservableList decorated,
+			Viewer viewer) {
+		super(decorated, true);
+		this.viewer = viewer;
+	}
+
+	public Viewer getViewer() {
+		return viewer;
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableSetDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableSetDecorator.java
new file mode 100644
index 0000000..3062b0b
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableSetDecorator.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 194734)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.set.DecoratingObservableSet;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.viewers.IViewerObservableSet;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ViewerObservableSetDecorator extends DecoratingObservableSet
+		implements IViewerObservableSet {
+	private final Viewer viewer;
+
+	/**
+	 * @param decorated
+	 * @param viewer
+	 */
+	public ViewerObservableSetDecorator(IObservableSet decorated, Viewer viewer) {
+		super(decorated, true);
+		this.viewer = viewer;
+	}
+
+	public Viewer getViewer() {
+		return viewer;
+	}
+
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableValueDecorator.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableValueDecorator.java
new file mode 100644
index 0000000..f14c351
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerObservableValueDecorator.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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 245647)
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.value.DecoratingObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ViewerObservableValueDecorator extends DecoratingObservableValue
+		implements IViewerObservableValue, Listener {
+	private Viewer viewer;
+
+	/**
+	 * @param decorated
+	 * @param viewer
+	 */
+	public ViewerObservableValueDecorator(IObservableValue decorated,
+			Viewer viewer) {
+		super(decorated, true);
+		this.viewer = viewer;
+		viewer.getControl().addListener(SWT.Dispose, this);
+	}
+
+	public void handleEvent(Event event) {
+		if (event.type == SWT.Dispose)
+			dispose();
+	}
+
+	public Viewer getViewer() {
+		return viewer;
+	}
+
+	public synchronized void dispose() {
+		if (viewer != null) {
+			Control control = viewer.getControl();
+			if (control != null && !control.isDisposed()) {
+				control.removeListener(SWT.Dispose, this);
+			}
+			viewer = null;
+		}
+		super.dispose();
+	}
+}
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.java
new file mode 100644
index 0000000..2bf9609
--- /dev/null
+++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bug 230296, 238296
+ ******************************************************************************/
+
+package org.eclipse.jface.internal.databinding.viewers;
+
+import java.util.Iterator;
+
+import org.eclipse.jface.databinding.viewers.IViewerUpdater;
+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 implements IViewerUpdater {
+	private final StructuredViewer viewer;
+
+	/**
+	 * Constructs a ViewerUpdater for updating the specified viewer.
+	 * 
+	 * @param viewer
+	 *            the viewer which will be updated through this instance.
+	 */
+	protected ViewerUpdater(StructuredViewer viewer) {
+		this.viewer = viewer;
+	}
+
+	public abstract void insert(Object element, int position);
+
+	public abstract void remove(Object element, int position);
+
+	public void replace(Object oldElement, Object newElement, int position) {
+		remove(oldElement, position);
+		insert(newElement, position);
+	}
+
+	public void move(Object element, int oldPosition, int newPosition) {
+		if (isElementOrderPreserved()) {
+			IStructuredSelection selection = (IStructuredSelection) viewer
+					.getSelection();
+
+			remove(element, oldPosition);
+			insert(element, newPosition);
+
+			// Preserve selection
+			if (selectionContains(selection, element)) {
+				viewer.setSelection(selection);
+			}
+		}
+	}
+
+	boolean isElementOrderPreserved() {
+		return viewer.getComparator() == null
+				&& viewer.getFilters().length == 0;
+	}
+
+	private boolean selectionContains(IStructuredSelection selection,
+			Object element) {
+		if (!selection.isEmpty()) {
+			IElementComparer comparer = viewer.getComparer();
+			for (Iterator iter = selection.iterator(); iter.hasNext();) {
+				Object selectionElement = iter.next();
+				if (comparer == null ? Util.equals(element, selectionElement)
+						: comparer.equals(element, selectionElement)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	public abstract void add(Object[] elements);
+
+	public abstract void remove(Object[] elements);
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/.classpath b/examples/org.eclipse.jface.examples.databinding/.classpath
new file mode 100644
index 0000000..304e861
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/examples/org.eclipse.jface.examples.databinding/.cvsignore b/examples/org.eclipse.jface.examples.databinding/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/examples/org.eclipse.jface.examples.databinding/.project b/examples/org.eclipse.jface.examples.databinding/.project
new file mode 100644
index 0000000..0ccc228
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/.project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jface.examples.databinding</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.jem.beaninfo.BeanInfoNature</nature>
+	</natures>
+</projectDescription>
diff --git a/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.core.prefs b/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..4dbb4aa
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,361 @@
+#Fri May 22 11:26:57 MDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=ignore
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.ui.prefs b/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..44c2ab6
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:06:02 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.2\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.pde.prefs b/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..122c936
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:55:50 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/examples/org.eclipse.jface.examples.databinding/META-INF/MANIFEST.MF b/examples/org.eclipse.jface.examples.databinding/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9e4b98d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jface.examples.databinding
+Bundle-Version: 1.0.200.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.jface,
+ org.eclipse.core.databinding,
+ org.eclipse.core.runtime,
+ org.eclipse.core.databinding.beans,
+ org.eclipse.jface.databinding,
+ org.eclipse.core.databinding.property
+Export-Package: org.eclipse.jface.examples.databinding;x-internal:=false,
+ org.eclipse.jface.examples.databinding.mask;x-internal:=false,
+ org.eclipse.jface.examples.databinding.mask.internal;x-internal:=true,
+ org.eclipse.jface.examples.databinding.model;x-internal:=false,
+ org.eclipse.jface.examples.databinding.radioGroup;x-internal:=false
+Import-Package: com.ibm.icu.text
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/examples/org.eclipse.jface.examples.databinding/about.html b/examples/org.eclipse.jface.examples.databinding/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/examples/org.eclipse.jface.examples.databinding/build.properties b/examples/org.eclipse.jface.examples.databinding/build.properties
new file mode 100644
index 0000000..dbd7a49
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/build.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2003, 2009 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+output.databindingexamples.jar = bin/
+src.includes = about.html
+source.. = src/
+javacWarnings..=-raw,unchecked
diff --git a/examples/org.eclipse.jface.examples.databinding/plugin.properties b/examples/org.eclipse.jface.examples.databinding/plugin.properties
new file mode 100644
index 0000000..759e715
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2000, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+pluginName = JFace Data Binding Examples
+providerName = Eclipse.org
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ModelObject.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ModelObject.java
new file mode 100644
index 0000000..3e2acf3
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ModelObject.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ModelObject {
+	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+			this);
+	private String id;
+
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		propertyChangeSupport.addPropertyChangeListener(listener);
+	}
+
+	public void addPropertyChangeListener(String propertyName,
+			PropertyChangeListener listener) {
+		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
+	}
+
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		propertyChangeSupport.removePropertyChangeListener(listener);
+	}
+
+	public void removePropertyChangeListener(String propertyName,
+			PropertyChangeListener listener) {
+		propertyChangeSupport.removePropertyChangeListener(propertyName,
+				listener);
+	}
+
+	protected void firePropertyChange(String propertyName, Object oldValue,
+			Object newValue) {
+		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+				newValue);
+	}
+	
+	protected void firePropertyChange(String propertyName, int oldValue,
+			int newValue) {
+		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+				newValue);
+	}
+	
+	protected void firePropertyChange(String propertyName, boolean oldValue,
+			boolean newValue) {
+		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+				newValue);
+	}
+
+	public void setId(String string) {
+		Object oldValue = id;
+		id = string;
+		firePropertyChange("id", oldValue, id);
+	}
+
+	protected Object[] append(Object[] array, Object object) {
+		List newList = new ArrayList(Arrays.asList(array));
+		newList.add(object);
+		return newList.toArray((Object[]) Array.newInstance(array.getClass()
+				.getComponentType(), newList.size()));
+	}
+
+	protected Object[] remove(Object[] array, Object object) {
+		List newList = new ArrayList(Arrays.asList(array));
+		newList.remove(object);
+		return newList.toArray((Object[]) Array.newInstance(array.getClass()
+				.getComponentType(), newList.size()));
+	}
+	
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/AsynchronousTestSet.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/AsynchronousTestSet.java
new file mode 100644
index 0000000..9669115
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/AsynchronousTestSet.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.set.ObservableSet;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Test set that simulates asynchronously computed elements. The elements of the
+ * set are randomly generated Integers. Whenever the "recompute" method is
+ * called, the set will spin off a job that sleeps for a period of time and then
+ * randomly adds and removes elements from the set.
+ * 
+ * <p>
+ * This simulates a set that wraps a database query or network communication.
+ * These would follow the same pattern (report the set as "stale", perform some
+ * slow operation, then make changes to the set).
+ * </p>
+ * 
+ * @since 1.0
+ */
+public class AsynchronousTestSet extends ObservableSet {
+
+	private static Random randomNumberGenerator = new Random();
+
+	private Display display;
+
+	private boolean stale = false;
+
+	/**
+	 * Average number of elements to add or remove
+	 */
+	private static final int AVERAGE_DELTA = 4;
+
+	/**
+	 * Average "computation" time -- time taken to do the simulated work (ms)
+	 */
+	private static final int AVERAGE_BUSY_TIME = 1000;
+
+	/**
+	 * List of all undisposed AsynchronousTestSet instances. Used for the
+	 * recomputeAll method.
+	 */
+	private static List allSets = new ArrayList();
+
+	public AsynchronousTestSet() {
+		super(new HashSet(), Object.class);
+		display = Display.getCurrent();
+		if (display == null) {
+			throw new IllegalStateException(
+					"This object can only be created in the UI thread"); //$NON-NLS-1$
+		}
+		recompute();
+	}
+
+	protected void firstListenerAdded() {
+		super.firstListenerAdded();
+		allSets.add(this);
+	}
+
+	protected void lastListenerRemoved() {
+		allSets.remove(this);
+		super.lastListenerRemoved();
+	}
+
+	public static void recomputeAll() {
+		for (Iterator iter = allSets.iterator(); iter.hasNext();) {
+			AsynchronousTestSet next = (AsynchronousTestSet) iter.next();
+
+			next.recompute();
+		}
+	}
+
+	public void remove(Collection toRemove) {
+		HashSet removed = new HashSet();
+		removed.addAll(toRemove);
+		removed.retainAll(wrappedSet);
+
+		wrappedSet.removeAll(removed);
+		fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removed));
+	}
+
+	public boolean isStale() {
+		return stale;
+	}
+
+	public void recompute() {
+		if (!isStale()) {
+			setStale(true);
+			final int sleepTime = (int) (randomNumberGenerator.nextDouble() * (AVERAGE_BUSY_TIME * 2));
+			Thread newThread = new Thread(new Runnable() {
+				public void run() {
+
+					// Simulate work by sleeping
+					try {
+						Thread.sleep(sleepTime);
+					} catch (InterruptedException e) {
+					}
+
+					// Add and remove some elements -- important: fire all
+					// events in the UI thread
+					display.asyncExec(new Runnable() {
+						public void run() {
+							final HashSet toAdd = new HashSet();
+							final HashSet toRemove = new HashSet();
+
+							// Compute elements to add and remove (basically
+							// just fills the toAdd
+							// and toRemove sets with random elements)
+							int delta = (randomNumberGenerator
+									.nextInt(AVERAGE_DELTA * 4) - AVERAGE_DELTA * 2);
+							int extraAdds = randomNumberGenerator
+									.nextInt(AVERAGE_DELTA);
+							int addCount = delta + extraAdds;
+							int removeCount = -delta + extraAdds;
+
+							if (addCount > 0) {
+								for (int i = 0; i < addCount; i++) {
+									toAdd.add(new Integer(randomNumberGenerator
+											.nextInt(20)));
+								}
+							}
+
+							if (removeCount > 0) {
+								Iterator oldElements = wrappedSet.iterator();
+								for (int i = 0; i < removeCount
+										&& oldElements.hasNext(); i++) {
+									toRemove.add(oldElements.next());
+								}
+							}
+
+							toAdd.removeAll(wrappedSet);
+							wrappedSet.addAll(toAdd);
+							wrappedSet.removeAll(toRemove);
+
+							setStale(false);
+							fireSetChange(Diffs.createSetDiff(toAdd, toRemove));
+						}
+					});
+				}
+			});
+
+			newThread.start();
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/LabelProviderTest.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/LabelProviderTest.java
new file mode 100644
index 0000000..736f7b5
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/LabelProviderTest.java
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * 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 116920
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+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.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ListeningLabelProvider;
+import org.eclipse.jface.databinding.viewers.ObservableSetContentProvider;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.LayoutConstants;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests UpdatableTreeContentProvider and DirtyIndicationLabelProvider. Creates
+ * a tree containing three randomly-generated sets of integers, and one node
+ * that contains the union of the other sets.
+ * 
+ * @since 1.0
+ */
+public class LabelProviderTest {
+
+	private Shell shell;
+
+	private ListViewer list;
+
+	private WritableSet setOfRenamables;
+
+	private Button addButton;
+
+	private Button removeButton;
+
+	private Button renameButton;
+
+	private SelectionListener buttonSelectionListener = new SelectionAdapter() {
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+		 */
+		public void widgetSelected(SelectionEvent e) {
+			Button pressed = (Button) e.widget;
+			if (pressed == addButton) {
+				setOfRenamables.add(new RenamableItem());
+			} else if (pressed == removeButton) {
+				setOfRenamables.remove(getCurrentSelection());
+			} else if (pressed == renameButton) {
+				rename(getCurrentSelection());
+			}
+
+			super.widgetSelected(e);
+		}
+	};
+
+	private IObservableValue selectedRenamable;
+
+	/**
+	 * 
+	 */
+	public LabelProviderTest() {
+
+		// Create shell
+		shell = new Shell(Display.getCurrent());
+		{ // Initialize shell
+			setOfRenamables = new WritableSet();
+
+			list = new ListViewer(shell);
+			ObservableSetContentProvider contentProvider = new ObservableSetContentProvider();
+			list.setContentProvider(contentProvider);
+			list.setLabelProvider(new ListeningLabelProvider(contentProvider
+					.getKnownElements()) {
+				RenamableItem.Listener listener = new RenamableItem.Listener() {
+					public void handleChanged(RenamableItem item) {
+						fireChangeEvent(Collections.singleton(item));
+					}
+				};
+
+				/*
+				 * (non-Javadoc)
+				 * 
+				 * @see org.eclipse.jface.databinding.viewers.ViewerLabelProvider#updateLabel(org.eclipse.jface.viewers.ViewerLabel,
+				 *      java.lang.Object)
+				 */
+				public void updateLabel(ViewerLabel label, Object element) {
+					if (element instanceof RenamableItem) {
+						RenamableItem item = (RenamableItem) element;
+
+						label.setText(item.getName());
+					}
+				}
+
+				protected void addListenerTo(Object next) {
+					RenamableItem item = (RenamableItem) next;
+
+					item.addListener(listener);
+				}
+
+				protected void removeListenerFrom(Object next) {
+					RenamableItem item = (RenamableItem) next;
+
+					item.removeListener(listener);
+				}
+			});
+			list.setInput(setOfRenamables);
+
+			selectedRenamable = ViewersObservables.observeSingleSelection(list);
+
+			Composite buttonBar = new Composite(shell, SWT.NONE);
+			{ // Initialize buttonBar
+				addButton = new Button(buttonBar, SWT.PUSH);
+				addButton.setText("Add"); //$NON-NLS-1$
+				addButton.addSelectionListener(buttonSelectionListener);
+				removeButton = new Button(buttonBar, SWT.PUSH);
+				removeButton.addSelectionListener(buttonSelectionListener);
+				removeButton.setText("Remove"); //$NON-NLS-1$
+				renameButton = new Button(buttonBar, SWT.PUSH);
+				renameButton.addSelectionListener(buttonSelectionListener);
+				renameButton.setText("Rename"); //$NON-NLS-1$
+
+				selectedRenamable
+						.addValueChangeListener(new IValueChangeListener() {
+							public void handleValueChange(ValueChangeEvent event) {
+								boolean shouldEnable = selectedRenamable
+										.getValue() != null;
+								removeButton.setEnabled(shouldEnable);
+								renameButton.setEnabled(shouldEnable);
+							}
+						});
+				removeButton.setEnabled(false);
+				renameButton.setEnabled(false);
+
+				GridLayoutFactory.fillDefaults().generateLayout(buttonBar);
+			}
+
+		}
+		GridLayoutFactory.fillDefaults().numColumns(2).margins(
+				LayoutConstants.getMargins()).generateLayout(shell);
+	}
+
+	/**
+	 * @param currentSelection
+	 */
+	protected void rename(final RenamableItem currentSelection) {
+		InputDialog inputDialog = new InputDialog(
+				shell,
+				"Edit name", "Enter the new item name", currentSelection.getName(), null); //$NON-NLS-1$ //$NON-NLS-2$
+		if (Window.OK == inputDialog.open()) {
+			currentSelection.setName(inputDialog.getValue());
+		}
+	}
+
+	/**
+	 * @return
+	 */
+	protected RenamableItem getCurrentSelection() {
+		return (RenamableItem) selectedRenamable.getValue();
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+
+			public void run() {
+				LabelProviderTest test = new LabelProviderTest();
+				Shell s = test.getShell();
+				s.pack();
+				s.setVisible(true);
+
+				while (!s.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	private Shell getShell() {
+		return shell;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/LabelProviderTest2.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/LabelProviderTest2.java
new file mode 100644
index 0000000..3089982
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/LabelProviderTest2.java
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * 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 116920
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+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.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ListeningLabelProvider;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.LayoutConstants;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests UpdatableTreeContentProvider and DirtyIndicationLabelProvider. Creates
+ * a tree containing three randomly-generated sets of integers, and one node
+ * that contains the union of the other sets.
+ * 
+ * @since 1.0
+ */
+public class LabelProviderTest2 {
+
+	private Shell shell;
+
+	private ListViewer list;
+
+	private WritableList listOfRenamables;
+
+	private Button addButton;
+
+	private Button removeButton;
+
+	private Button renameButton;
+
+	private SelectionListener buttonSelectionListener = new SelectionAdapter() {
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+		 */
+		public void widgetSelected(SelectionEvent e) {
+			Button pressed = (Button) e.widget;
+			if (pressed == addButton) {
+				listOfRenamables.add(new RenamableItem());
+			} else if (pressed == removeButton) {
+				listOfRenamables.remove(getCurrentSelection());
+			} else if (pressed == renameButton) {
+				rename(getCurrentSelection());
+			}
+
+			super.widgetSelected(e);
+		}
+	};
+
+	private IObservableValue selectedRenamable;
+
+	/**
+	 * 
+	 */
+	public LabelProviderTest2() {
+
+		// Create shell
+		shell = new Shell(Display.getCurrent());
+		{ // Initialize shell
+			listOfRenamables = new WritableList();
+
+			list = new ListViewer(shell);
+			ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+			list.setContentProvider(contentProvider);
+			list.setLabelProvider(new ListeningLabelProvider(contentProvider
+					.getKnownElements()) {
+				RenamableItem.Listener listener = new RenamableItem.Listener() {
+					public void handleChanged(RenamableItem item) {
+						fireChangeEvent(Collections.singleton(item));
+					}
+				};
+
+				/*
+				 * (non-Javadoc)
+				 * 
+				 * @see org.eclipse.jface.databinding.viewers.ViewerLabelProvider#updateLabel(org.eclipse.jface.viewers.ViewerLabel,
+				 *      java.lang.Object)
+				 */
+				public void updateLabel(ViewerLabel label, Object element) {
+					if (element instanceof RenamableItem) {
+						RenamableItem item = (RenamableItem) element;
+
+						label.setText(item.getName());
+					}
+				}
+
+				protected void addListenerTo(Object next) {
+					RenamableItem item = (RenamableItem) next;
+
+					item.addListener(listener);
+				}
+
+				protected void removeListenerFrom(Object next) {
+					RenamableItem item = (RenamableItem) next;
+
+					item.removeListener(listener);
+				}
+			});
+			list.setInput(listOfRenamables);
+
+			selectedRenamable = ViewersObservables.observeSingleSelection(list);
+
+			Composite buttonBar = new Composite(shell, SWT.NONE);
+			{ // Initialize buttonBar
+				addButton = new Button(buttonBar, SWT.PUSH);
+				addButton.setText("Add"); //$NON-NLS-1$
+				addButton.addSelectionListener(buttonSelectionListener);
+				removeButton = new Button(buttonBar, SWT.PUSH);
+				removeButton.addSelectionListener(buttonSelectionListener);
+				removeButton.setText("Remove"); //$NON-NLS-1$
+				renameButton = new Button(buttonBar, SWT.PUSH);
+				renameButton.addSelectionListener(buttonSelectionListener);
+				renameButton.setText("Rename"); //$NON-NLS-1$
+
+				selectedRenamable
+						.addValueChangeListener(new IValueChangeListener() {
+							public void handleValueChange(ValueChangeEvent event) {
+								boolean shouldEnable = selectedRenamable
+										.getValue() != null;
+								removeButton.setEnabled(shouldEnable);
+								renameButton.setEnabled(shouldEnable);
+							}
+						});
+				removeButton.setEnabled(false);
+				renameButton.setEnabled(false);
+
+				GridLayoutFactory.fillDefaults().generateLayout(buttonBar);
+			}
+
+		}
+		GridLayoutFactory.fillDefaults().numColumns(2).margins(
+				LayoutConstants.getMargins()).generateLayout(shell);
+	}
+
+	/**
+	 * @param currentSelection
+	 */
+	protected void rename(final RenamableItem currentSelection) {
+		InputDialog inputDialog = new InputDialog(
+				shell,
+				"Edit name", "Enter the new item name", currentSelection.getName(), null); //$NON-NLS-1$ //$NON-NLS-2$
+		if (Window.OK == inputDialog.open()) {
+			currentSelection.setName(inputDialog.getValue());
+		}
+	}
+
+	/**
+	 * @return
+	 */
+	protected RenamableItem getCurrentSelection() {
+		return (RenamableItem) selectedRenamable.getValue();
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		final Display display = Display.getDefault();
+        Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				LabelProviderTest2 test = new LabelProviderTest2();
+				Shell s = test.getShell();
+				s.pack();
+				s.setVisible(true);
+				
+				while (!s.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	private Shell getShell() {
+		return shell;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/RenamableItem.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/RenamableItem.java
new file mode 100644
index 0000000..970b061
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/RenamableItem.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import java.util.ArrayList;
+
+/**
+ * @since 1.0
+ *
+ */
+public class RenamableItem {
+	
+	public static interface Listener {
+		public void handleChanged(RenamableItem item);
+	}
+	
+	private String name;
+	private ArrayList listeners = new ArrayList();
+
+	public RenamableItem() {
+		name = "RenamableItem"; //$NON-NLS-1$
+	}
+	
+	public void addListener(Listener listener) {
+		listeners.add(listener);
+	}
+	
+	public void removeListener(Listener toRemove) {
+		listeners.remove(toRemove);
+	}
+	
+	public void setName(String newName) {
+		this.name = newName;
+		
+		Listener[] l = (Listener[]) listeners.toArray(new Listener[listeners.size()]);
+		for (int i = 0; i < l.length; i++) {
+			l[i].handleChanged(this);
+		}
+	}
+	
+	public String getName() {
+		return name;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/SimpleNode.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/SimpleNode.java
new file mode 100644
index 0000000..7d34d94
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/SimpleNode.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+
+
+/**
+ * This object will be given randomly-generated children
+ *  
+ * @since 1.0
+ */
+public class SimpleNode {
+	private String nodeName;
+	private IObservableSet children;
+
+	public SimpleNode(String nodeName, IObservableSet children) {
+		super();
+		this.nodeName = nodeName;
+		this.children = children;
+	}
+
+	public String getNodeName() {
+		return nodeName;
+	}
+
+	public IObservableSet getChildren() {
+		return children;
+	}
+	
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/SomeMathFunction.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/SomeMathFunction.java
new file mode 100644
index 0000000..88ec454
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/SomeMathFunction.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+
+/**
+ * Simple function that performs one of three operations on Doubles:
+ * <ul>
+ * <li>Multiply by two</li>
+ * <li>Round to nearest integer</li>
+ * <li>Do nothing</li>
+ * </ul>
+ * 
+ * @since 1.0
+ */
+public class SomeMathFunction extends ComputedObservableMap {
+
+	/**
+	 * 
+	 */
+	public static final int OP_IDENTITY = 0;
+
+	/**
+	 * 
+	 */
+	public static final int OP_MULTIPLY = 1;
+
+	/**
+	 * 
+	 */
+	public static final int OP_ROUND = 2;
+
+	private int op = OP_ROUND;
+
+	/**
+	 * @param domain
+	 */
+	public SomeMathFunction(IObservableSet domain) {
+		super(domain);
+		init();
+	}
+
+	/**
+	 * @param operation
+	 */
+	public void setOperation(final int operation) {
+		final int oldOp = this.op;
+		this.op = operation;
+
+		// Fire a change event. Changing the operation is going to affect every
+		// answer returned by
+		// this function, so include every element in the function domain in the
+		// event.
+		// If this was a change that would only affect a subset of elements, we
+		// would include
+		// the subset of affected elements rather than using
+		// domain.toCollection()
+		fireMapChange(new MapDiff() {
+
+			public Set getAddedKeys() {
+				return Collections.EMPTY_SET;
+			}
+
+			public Set getChangedKeys() {
+				return keySet();
+			}
+
+			public Object getNewValue(Object key) {
+				return doComputeResult(key, operation);
+			}
+
+			public Object getOldValue(Object key) {
+				return doComputeResult(key, oldOp);
+			}
+
+			public Set getRemovedKeys() {
+				return Collections.EMPTY_SET;
+			}
+		});
+	}
+
+	private Object doComputeResult(Object element, int op) {
+		switch (op) {
+		case OP_IDENTITY:
+			return element;
+		case OP_MULTIPLY:
+			return new Double((((Double) element).doubleValue() * 2.0));
+		case OP_ROUND:
+			return new Double(Math.floor((((Double) element).doubleValue())));
+		}
+		return element;
+	}
+
+	protected Object doGet(Object key) {
+		return doComputeResult(key, this.op);
+	}
+
+	protected Object doPut(Object key, Object value) {
+		throw new UnsupportedOperationException();
+	}
+
+	protected void hookListener(Object addedKey) {
+		// ignore, no need to listen to immutable Double objects
+	}
+
+	protected void unhookListener(Object removedKey) {
+		// ignore, no need to listen to immutable Double objects
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/StructuredContentProviderTest.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/StructuredContentProviderTest.java
new file mode 100644
index 0000000..20efc8e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/StructuredContentProviderTest.java
@@ -0,0 +1,396 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import java.util.Iterator;
+import java.util.Random;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.MappedSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+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.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableSetContentProvider;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater;
+import org.eclipse.jface.internal.databinding.provisional.viewers.ViewerLabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests UpdatableSetContentProvider, ComputableValue, ControlUpdator,
+ * UpdatableFunction, and ConvertingSet.
+ * 
+ * <p>
+ * This test displays a dialog with user-editable list of Doubles. It allows the
+ * user to select a math function to apply to the set, and displays the result
+ * in a new list. A line of text along the bottom of the dialog displays the sum
+ * of the elements from the transformed set. Although this dialog is rather
+ * silly, it is a good example of a dialog where a lot of things can change from
+ * many directions.
+ * </p>
+ * 
+ * <p>
+ * An UpdatableSetContentProvider is used to supply the contents each
+ * ListViewer. ControlUpdators
+ * 
+ * </p>
+ * 
+ * @since 1.0
+ */
+public class StructuredContentProviderTest {
+
+	private static Realm realm;
+
+	/**
+	 * Top-level shell for the dialog
+	 */
+	private Shell shell;
+
+	/**
+	 * Random number stream. Used for the "add" button.
+	 */
+	protected Random random = new Random();
+
+	// Data model ////////////////////////////////////////////////////////
+
+	/**
+	 * inputSet stores a set of Doubles. The user is allowed to add and remove
+	 * Doubles from this set.
+	 */
+	private WritableSet inputSet;
+
+	/**
+	 * currentFunction is an Integer, set to one of the SomeMathFunction.OP_*
+	 * constants. It identifies which function will be applied to inputSet.
+	 */
+	private WritableValue currentFunction;
+
+	/**
+	 * mathFunction is the transformation. It can multiply by 2, round down to
+	 * the nearest integer, or do nothing (identity)
+	 */
+	private SomeMathFunction mathFunction;
+
+	/**
+	 * Set of Doubles. Holds the result of applying mathFunction to the
+	 * inputSet.
+	 */
+	private MappedSet outputSet;
+
+	/**
+	 * A Double. Stores the sum of the Doubles in outputSet
+	 */
+	private IObservableValue sumOfOutputSet;
+
+	/**
+	 * Creates the test dialog as a top-level shell.
+	 */
+	public StructuredContentProviderTest() {
+
+		// Initialize the data model
+		createDataModel();
+
+		shell = new Shell(Display.getCurrent(), SWT.SHELL_TRIM);
+		{ // Initialize shell
+			final Label someDoubles = new Label(shell, SWT.NONE);
+			someDoubles.setText("A list of random Doubles"); //$NON-NLS-1$
+			someDoubles.setLayoutData(new GridData(
+					GridData.HORIZONTAL_ALIGN_FILL
+							| GridData.VERTICAL_ALIGN_FILL));
+
+			Control addRemoveComposite = createInputControl(shell, inputSet);
+
+			GridData addRemoveData = new GridData(GridData.FILL_BOTH);
+			addRemoveData.minimumHeight = 1;
+			addRemoveData.minimumWidth = 1;
+
+			addRemoveComposite.setLayoutData(addRemoveData);
+
+			Group operation = new Group(shell, SWT.NONE);
+			{ // Initialize operation group
+				operation.setText("Select transformation"); //$NON-NLS-1$
+
+				createRadioButton(operation, currentFunction, "f(x) = x", //$NON-NLS-1$
+						new Integer(SomeMathFunction.OP_IDENTITY));
+				createRadioButton(operation, currentFunction, "f(x) = 2 * x", //$NON-NLS-1$
+						new Integer(SomeMathFunction.OP_MULTIPLY));
+				createRadioButton(operation, currentFunction,
+						"f(x) = floor(x)", new Integer( //$NON-NLS-1$
+								SomeMathFunction.OP_ROUND));
+
+				GridLayout layout = new GridLayout();
+				layout.numColumns = 1;
+				operation.setLayout(layout);
+			}
+			operation.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+					| GridData.VERTICAL_ALIGN_FILL));
+
+			Control outputControl = createOutputComposite(shell);
+			GridData outputData = new GridData(GridData.FILL_BOTH);
+			outputData.minimumHeight = 1;
+			outputData.minimumWidth = 1;
+			outputData.widthHint = 300;
+			outputData.heightHint = 150;
+
+			outputControl.setLayoutData(outputData);
+
+			final Label sumLabel = new Label(shell, SWT.NONE);
+			new ControlUpdater(sumLabel) {
+				protected void updateControl() {
+					double sum = ((Double) sumOfOutputSet.getValue())
+							.doubleValue();
+					int size = outputSet.size();
+
+					sumLabel.setText("The sum of the above " + size //$NON-NLS-1$
+							+ " doubles is " + sum); //$NON-NLS-1$
+				}
+			};
+			sumLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+					| GridData.VERTICAL_ALIGN_FILL));
+
+			GridLayout layout = new GridLayout();
+			layout.numColumns = 1;
+			shell.setLayout(layout);
+		}
+
+	}
+
+	/**
+	 * Create the updatables for this dialog
+	 */
+	private void createDataModel() {
+		// Initialize data model. We will create a user-editable set of Doubles.
+		// The user can run
+		// a transformation on this set and view the result in a list viewer.
+
+		// inputSet will be a writable set of doubles. The user will add and
+		// remove entries from this set
+		// through the UI.
+		inputSet = new WritableSet(realm);
+
+		// currentFunction holds the ID currently selected function to apply to
+		// elements in the inputSet.
+		// We will allow the user to change the current function through a set
+		// of radio buttons
+		currentFunction = new WritableValue(realm, new Integer(
+				SomeMathFunction.OP_MULTIPLY), null);
+
+		// mathFunction implements the selected function
+		mathFunction = new SomeMathFunction(inputSet);
+		currentFunction.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				mathFunction
+						.setOperation(((Integer) currentFunction.getValue())
+								.intValue());
+			}
+		});
+		mathFunction.setOperation(((Integer) currentFunction.getValue())
+				.intValue());
+
+		// outputSet holds the result. It displays the result of applying the
+		// currently-selected
+		// function on all the elements in the input set.
+		outputSet = new MappedSet(inputSet, mathFunction);
+
+		// sumOfOutputSet stores the current sum of the the Doubles in the
+		// output set
+		sumOfOutputSet = new ComputedValue(realm) {
+			protected Object calculate() {
+				double sum = 0.0;
+				for (Iterator iter = outputSet.iterator(); iter.hasNext();) {
+					Double next = (Double) iter.next();
+
+					sum += next.doubleValue();
+				}
+				return new Double(sum);
+			}
+		};
+	}
+
+	/**
+	 * Creates a radio button in the given parent composite. When selected, the
+	 * button will change the given SettableValue to the given value.
+	 * 
+	 * @param parent
+	 *            parent composite
+	 * @param model
+	 *            SettableValue that will hold the value of the
+	 *            currently-selected radio button
+	 * @param string
+	 *            text to appear in the radio button
+	 * @param value
+	 *            value of this radio button (SettableValue will hold this value
+	 *            when the radio button is selected)
+	 */
+	private void createRadioButton(Composite parent, final WritableValue model,
+			String string, final Object value) {
+		final Button button = new Button(parent, SWT.RADIO);
+		button.setText(string);
+		button.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				model.setValue(value);
+				super.widgetSelected(e);
+			}
+		});
+		new ControlUpdater(button) {
+			protected void updateControl() {
+				button.setSelection(model.getValue().equals(value));
+			}
+		};
+		button.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+				| GridData.VERTICAL_ALIGN_FILL));
+	}
+
+	private Control createOutputComposite(Composite parent) {
+		ListViewer listOfInts = new ListViewer(parent, SWT.BORDER
+				| SWT.V_SCROLL | SWT.H_SCROLL);
+
+		listOfInts.setContentProvider(new ObservableSetContentProvider());
+		listOfInts.setLabelProvider(new ViewerLabelProvider());
+		listOfInts.setInput(outputSet);
+		return listOfInts.getControl();
+	}
+
+	/**
+	 * Creates and returns a control that will allow the user to add and remove
+	 * Doubles from the given input set.
+	 * 
+	 * @param parent
+	 *            parent control
+	 * @param inputSet
+	 *            input set
+	 * @return a newly created SWT control that displays Doubles from the input
+	 *         set and allows the user to add and remove entries
+	 */
+	private Control createInputControl(Composite parent,
+			final WritableSet inputSet) {
+		Composite addRemoveComposite = new Composite(parent, SWT.NONE);
+		{ // Initialize addRemoveComposite
+			ListViewer listOfInts = new ListViewer(addRemoveComposite,
+					SWT.BORDER);
+
+			listOfInts.setContentProvider(new ObservableSetContentProvider());
+			listOfInts.setLabelProvider(new ViewerLabelProvider());
+			listOfInts.setInput(inputSet);
+
+			final IObservableValue selectedInt = ViewersObservables.observeSingleSelection(listOfInts);
+
+			GridData listData = new GridData(GridData.FILL_BOTH);
+			listData.minimumHeight = 1;
+			listData.minimumWidth = 1;
+			listData.widthHint = 150;
+			listData.heightHint = 150;
+			listOfInts.getControl().setLayoutData(listData);
+
+			Composite buttonBar = new Composite(addRemoveComposite, SWT.NONE);
+			{ // Initialize button bar
+
+				Button add = new Button(buttonBar, SWT.PUSH);
+				add.setText("Add"); //$NON-NLS-1$
+				add.addSelectionListener(new SelectionAdapter() {
+					public void widgetSelected(SelectionEvent e) {
+						inputSet.add(new Double(random.nextDouble() * 100.0));
+						super.widgetSelected(e);
+					}
+				});
+				add.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+						| GridData.VERTICAL_ALIGN_FILL));
+
+				final Button remove = new Button(buttonBar, SWT.PUSH);
+				remove.setText("Remove"); //$NON-NLS-1$
+				// Enable the Remove button if and only if there is currently an
+				// element selected.
+				new ControlUpdater(remove) {
+					protected void updateControl() {
+						// This block demonstrates auto-listening.
+						// When updateControl is running, the framework
+						// remembers each
+						// updatable that gets touched. Since we're calling
+						// selectedInt.getValue()
+						// here, this updator will be flagged as dependant on
+						// selectedInt. This
+						// means that whenever selectedInt changes, this block
+						// of code will re-run
+						// itself.
+
+						// The result is that the remove button will recompute
+						// its enablement
+						// whenever the selection in the listbox changes, and it
+						// was not necessary
+						// to attach any listeners.
+						remove.setEnabled(selectedInt.getValue() != null);
+					}
+				};
+
+				remove.addSelectionListener(new SelectionAdapter() {
+					public void widgetSelected(SelectionEvent e) {
+						inputSet.remove(selectedInt.getValue());
+						super.widgetSelected(e);
+					}
+				});
+				remove.setLayoutData(new GridData(
+						GridData.HORIZONTAL_ALIGN_FILL
+								| GridData.VERTICAL_ALIGN_FILL));
+
+				GridLayout buttonLayout = new GridLayout();
+				buttonLayout.numColumns = 1;
+				buttonBar.setLayout(buttonLayout);
+
+			} // End button bar
+			buttonBar.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL
+					| GridData.VERTICAL_ALIGN_BEGINNING));
+
+			GridLayout addRemoveLayout = new GridLayout();
+			addRemoveLayout.numColumns = 2;
+			addRemoveComposite.setLayout(addRemoveLayout);
+		}
+		return addRemoveComposite;
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		Display display = Display.getDefault();
+		realm = SWTObservables.getRealm(display);
+		StructuredContentProviderTest test = new StructuredContentProviderTest();
+		Shell s = test.getShell();
+		s.pack();
+		s.setVisible(true);
+
+		while (!s.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+		display.dispose();
+	}
+
+	private Shell getShell() {
+		return shell;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/TreeContentProviderTest.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/TreeContentProviderTest.java
new file mode 100644
index 0000000..101429d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/contentprovider/test/TreeContentProviderTest.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 263693
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.contentprovider.test;
+
+import org.eclipse.core.databinding.observable.IObservable;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.UnionSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider;
+import org.eclipse.jface.internal.databinding.provisional.viewers.ViewerLabelProvider;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.LayoutConstants;
+import org.eclipse.jface.viewers.IViewerLabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests UpdatableTreeContentProvider and DirtyIndicationLabelProvider. Creates
+ * a tree containing three randomly-generated sets of integers, and one node
+ * that contains the union of the other sets.
+ * 
+ * @since 3.2
+ */
+public class TreeContentProviderTest {
+
+	private Shell shell;
+	private TreeViewer tree;
+
+	// Three randomly-generated sets of doubles
+	private AsynchronousTestSet set1;
+	private AsynchronousTestSet set2;
+	private AsynchronousTestSet set3;
+
+	// The union of the above three sets
+	private UnionSet union;
+	private Button randomize;
+
+	public TreeContentProviderTest() {
+
+		// Create the data model
+		set1 = new AsynchronousTestSet();
+		set2 = new AsynchronousTestSet();
+		set3 = new AsynchronousTestSet();
+
+		// A union of the above sets
+		union = new UnionSet(new IObservableSet[] { set1, set2, set3 });
+
+		// Create shell
+		shell = new Shell(Display.getCurrent());
+
+		createTree();
+
+		Composite buttonBar = new Composite(shell, SWT.NONE);
+		{
+			buttonBar.setLayout(new FillLayout(SWT.HORIZONTAL));
+			randomize = new Button(buttonBar, SWT.PUSH);
+			randomize.setText("Randomize");
+			randomize.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+					AsynchronousTestSet.recomputeAll();
+					super.widgetSelected(e);
+				}
+			});
+
+			GridLayoutFactory.fillDefaults().generateLayout(buttonBar);
+		}
+
+		GridLayoutFactory.fillDefaults().margins(LayoutConstants.getMargins())
+				.generateLayout(shell);
+
+		shell.addDisposeListener(new DisposeListener() {
+			/*
+			 * (non-Javadoc)
+			 * 
+			 * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
+			 */
+			public void widgetDisposed(DisposeEvent e) {
+				dispose();
+			}
+		});
+	}
+
+	/**
+	 * 
+	 */
+	protected void dispose() {
+		set1.dispose();
+		set2.dispose();
+		set3.dispose();
+		union.dispose();
+	}
+
+	private void createTree() {
+		// Create the tree provider. This provides the structure of the tree.
+		// This tree will
+		// have an instance of RootNode as the root (which is really a
+		// placeholder), several
+		// SimpleNodes as top-level nodes, and sets of randomly generated
+		// Doubles below each
+		// SimpleNode.
+		IObservableFactory childrenFactory = new IObservableFactory() {
+			public IObservable createObservable(Object element) {
+				// If the parent is the root node, return the union of some
+				// randomly-generated
+				// nodes and some hardcoded nodes
+				if (element == tree.getInput()) {
+					// Set of hardcoded nodes
+					WritableSet topElements = new WritableSet();
+					topElements.add(new SimpleNode("Random Set 1", set1));
+					topElements.add(new SimpleNode("Random Set 2", set2));
+					topElements.add(new SimpleNode("Random Set 3", set3));
+					topElements.add(new SimpleNode("Union of the other sets",
+							union));
+					return topElements;
+				}
+
+				// If the parent is a RandomChildrenNode, return a
+				// randomly-generated
+				// set of Doubles for its children
+				if (element instanceof SimpleNode) {
+					// We return a new DelegatingObservableSet in order to
+					// prevent the
+					// original from being disposed.
+					return Observables
+							.proxyObservableSet(((SimpleNode) element)
+									.getChildren());
+				}
+
+				// Otherwise the node is a Double, which will have no children
+				return null;
+			}
+		};
+
+		// Label provider for the tree
+		IViewerLabelProvider labelProvider = new ViewerLabelProvider() {
+			public void updateLabel(ViewerLabel label, Object element) {
+				if (element instanceof SimpleNode) {
+					SimpleNode node = (SimpleNode) element;
+
+					label.setText(node.getNodeName());
+				}
+
+				if (element instanceof Integer) {
+					Integer node = (Integer) element;
+
+					label.setText("Integer " + node);
+				}
+			}
+		};
+
+		// Create tree viewer
+		tree = new TreeViewer(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+
+		// UpdatableTreeContentProvider converts an ITreeProvider into a
+		// standard JFace content provider
+		ObservableSetTreeContentProvider contentProvider = new ObservableSetTreeContentProvider(
+				childrenFactory, null);
+
+		tree.setContentProvider(contentProvider);
+		tree.setLabelProvider(labelProvider);
+
+		// For the ITreeProvider above, it doesn't matter what we select as the
+		// input.
+		tree.setInput(new Object());
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				TreeContentProviderTest test = new TreeContentProviderTest();
+				Shell s = test.getShell();
+				s.pack();
+				s.setVisible(true);
+
+				while (!s.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	private Shell getShell() {
+		return shell;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/DuckType.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/DuckType.java
new file mode 100644
index 0000000..795e5a7
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/DuckType.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2005, 2006 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
+ */
+package org.eclipse.jface.examples.databinding.ducks;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * DuckType. Implements Duck Typing for Java.  ("If it walks like a duck, 
+ * quacks like a duck, it...").  Essentially allows programs to treat
+ * objects from separate hierarchies as if they were designed with common
+ * interfaces as long as they adhere to common naming conventions.
+ * <p>
+ * This version is the strict DuckType.  All methods present in
+ * interfaceToImplement must be present on the target object.
+ *
+ * @author djo
+ */
+public class DuckType implements InvocationHandler {
+   
+   /**
+    * Interface DuckType#Wrapper.  An interface for DuckType proxies that
+    * allows clients to access the proxied value.  The value returned by
+    * calling DuckType#implement always implements this interface.
+    */
+   public static interface Wrapper {
+      /**
+       * Method duckType_GetWrappedValue.  Returns the proxied value.
+       * 
+       * @return The proxied value.
+       */
+      public Object duckType_GetWrappedValue();
+   }
+   
+	/**
+     * Causes object to implement the interfaceToImplement and returns
+     * an instance of the object implementing interfaceToImplement even
+     * if interfaceToImplement was not declared in object.getClass()'s 
+     * implements declaration.<p>
+     * 
+     * This works as long as all methods declared in interfaceToImplement
+     * are present on object.
+     * 
+	 * @param interfaceToImplement The Java class of the interface to implement
+	 * @param object The object to force to implement interfaceToImplement
+	 * @return object, but now implementing interfaceToImplement
+	 */
+	public static Object implement(Class interfaceToImplement, Object object) {
+		return Proxy.newProxyInstance(interfaceToImplement.getClassLoader(), 
+				new Class[] {interfaceToImplement, Wrapper.class}, new DuckType(object));
+	}
+    
+    /**
+     * Indicates if object is a (DuckType) instace of intrface.  That is,
+     * is every method in intrface present on object.
+     * 
+     * @param intrface The interface to implement
+     * @param object The object to test
+     * @return true if every method in intrface is present on object.  false otherwise
+     */
+    public static boolean instanceOf(Class intrface, Object object) {
+        final Method[] methods = intrface.getMethods();
+        Class candclass=object.getClass();
+        for (int methodidx = 0; methodidx < methods.length; methodidx++) {
+            Method method=methods[methodidx];
+            try {
+                candclass.getMethod(method.getName(), method.getParameterTypes());
+            } catch (NoSuchMethodException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+	
+	protected DuckType(Object object) {
+		this.object = object;
+		this.objectClass = object.getClass();
+	}
+
+	protected Object object;
+	protected Class objectClass;
+	
+	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+      if (method.getName().equals("equals") && args != null && args.length == 1) {
+         return new Boolean(equals(args[0]));
+      }
+      if (method.getName().equals("hashCode") && args == null) {
+         return new Integer(hashCode());
+      }
+      if (method.getName().equals("duckType_GetWrappedValue") && args == null) {
+         return object;
+      }
+		Method realMethod = objectClass.getMethod(method.getName(), method.getParameterTypes());
+      if (!realMethod.isAccessible()) {
+         realMethod.setAccessible(true);
+      }
+		return realMethod.invoke(object, args);
+	}
+   
+   public boolean equals(Object obj) {
+      if (obj instanceof Wrapper) {
+         Wrapper proxy = (Wrapper) obj;
+         Object wrappedValue = proxy.duckType_GetWrappedValue();
+         return wrappedValue.equals(object);
+      }
+      return obj == this || super.equals(obj) || object.equals(obj);
+   }
+   
+   public int hashCode() {
+      return object.hashCode();
+   }
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/ReflectedMethod.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/ReflectedMethod.java
new file mode 100644
index 0000000..7dcbf20
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/ReflectedMethod.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2005, 2006 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
+ */
+package org.eclipse.jface.examples.databinding.ducks;
+
+import java.lang.reflect.Method;
+
+/**
+ * ReflectedMethod.  Encapsulates a method that may or may not exist on 
+ * some receiver.  Invocation policy is that if the method can be invoked,
+ * it is.  On failure, returns null.
+ *
+ * @author djo
+ */
+public class ReflectedMethod {
+    
+    private Object subject;
+    private Method method;
+    
+    /**
+     * Constructor ReflectedMethod.  Create a ReflectedMethod object.
+     * 
+     * @param subject The object on which the method lives.
+     * @param methodName The name of the method.
+     * @param paramTypes The method's parameter types.
+     */
+    public ReflectedMethod(Object subject, String methodName, Class[] paramTypes) {
+        this.subject = subject;
+        method = null;
+        try {
+        	method = subject.getClass().getMethod(methodName, paramTypes);
+        } catch (Exception e) {
+        	System.out.println(e);
+        }
+    }
+    
+    /**
+     * Method exists.  Returns true if the underlying method exists, false
+     * otherwise.
+     * 
+     * @return true if the underlying method exists, false otherwise.
+     */
+    public boolean exists() {
+        return method != null;
+    }
+    
+    /**
+     * Method invoke.  If possible, invoke the encapsulated method with the
+     * specified parameters.
+     * 
+     * @param params An Object[] containing the parameters to pass.
+     * @return any return value or null if there was no return value or an
+     * error occured.
+     */
+    public Object invoke(Object[] params) {
+        if (method == null)
+            return null;
+        try {
+        	if (!method.isAccessible()) {
+        		method.setAccessible(true);
+        	}
+        	return method.invoke(subject, params);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+	/**
+	 * Method getType.  Returns the return type of the method.
+	 * 
+	 * @return The return type or null if none.
+	 */
+	public Class getType() {
+		return method.getReturnType();
+	}
+}
+
+
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/ReflectedProperty.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/ReflectedProperty.java
new file mode 100644
index 0000000..3ec9a58
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/ReflectedProperty.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.ducks;
+
+/**
+ * Encapsulates a single JavaBeans-style property
+ * 
+ * @since 3.3
+ */
+public class ReflectedProperty {
+	private String propertyName;
+	private ReflectedMethod getter;
+	private ReflectedMethod setter;
+
+	/**
+	 * Construct a ReflectedProperty on some object, given the property name.
+	 * 
+	 * @param object The object
+	 * @param propertyName The property name
+	 */
+	public ReflectedProperty(Object object, String propertyName) {
+		this.propertyName = propertyName;
+		getter = new ReflectedMethod(object, makeGetterName(propertyName), new Class[] {});
+		if (!getter.exists()) {
+			getter = new ReflectedMethod(object, makeBooleanGetterName(propertyName), new Class[] {});
+			if (!getter.exists()) {
+				throw new IllegalArgumentException("Cannot find getter for " + propertyName);
+			}
+		}
+		setter = new ReflectedMethod(object, makeSetterName(propertyName), new Class[] {getter.getType()});
+	}
+	
+	private String makeBooleanGetterName(String propertyName) {
+		return "is" + capitalize(propertyName);
+	}
+
+	private String makeSetterName(String propertyName) {
+		return "set" + capitalize(propertyName);
+	}
+
+	private String makeGetterName(String propertyName) {
+		return "get" + capitalize(propertyName);
+	}
+
+	private String capitalize(String string) {
+		return string.substring(0, 1).toUpperCase() + string.substring(1);
+	}
+
+	/**
+	 * Return the property's type.  This is the same as the type returned by
+	 * the getter.
+	 * 
+	 * @return The property's data type.
+	 */
+	public Class getType() {
+		return getter.getType();
+	}
+	
+	/**
+	 * Return the property's value.
+	 * 
+	 * @return The value in the property.
+	 */
+	public Object get() {
+		return getter.invoke(new Object[] {});
+	}
+	
+	/**
+	 * Set the property's value.  If the property is read-only, the request
+	 * is ignored.
+	 * 
+	 * @param newValue The value to set.
+	 */
+	public void set(Object newValue) {
+		setter.invoke(new Object[] {newValue});
+	}
+	
+	/**
+	 * Returns if the property is read-only.
+	 * 
+	 * @return false if the property has a setter; true otherwise.
+	 */
+	public boolean isReadOnly() {
+		return !setter.exists();
+	}
+
+	/**
+	 * Returns the property's name.
+	 * 
+	 * @return The property name.
+	 */
+	public String getPropertyName() {
+		return propertyName;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/RelaxedDuckType.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/RelaxedDuckType.java
new file mode 100644
index 0000000..2e5b347
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/ducks/RelaxedDuckType.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005, 2006 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
+ */
+package org.eclipse.jface.examples.databinding.ducks;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+
+/**
+ * RelaxedDuckType. Implements Duck Typing for Java.  ("If it walks like a duck, 
+ * quacks like a duck, it...").  Essentially allows programs to treat
+ * objects from separate hierarchies as if they were designed with common
+ * interfaces as long as they adhere to common naming conventions.
+ * <p>
+ * This version is the relaxed DuckType.  If a method in the interface is
+ * not present on the underlying object, the proxy simply returns null.
+ *
+ * @author djo
+ */
+public class RelaxedDuckType extends DuckType implements InvocationHandler {
+
+	public static Object implement(Class interfaceToImplement, Object object) {
+		return Proxy.newProxyInstance(interfaceToImplement.getClassLoader(), 
+				new Class[] {interfaceToImplement}, new RelaxedDuckType(object));
+	}
+    
+    public static boolean includes(Object object, String method, Class[] args) {
+        try {
+            object.getClass().getMethod(method, args);
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+        return true;
+    }
+    
+    private static final HashMap NULL_VALUES = new HashMap(); {
+        NULL_VALUES.put(Boolean.TYPE, Boolean.FALSE);
+        NULL_VALUES.put(Integer.TYPE, new Integer(0));
+        NULL_VALUES.put(Float.TYPE, new Float(0));
+        NULL_VALUES.put(Long.TYPE, new Long(0));
+        NULL_VALUES.put(Double.TYPE, new Double(0));
+        NULL_VALUES.put(Character.TYPE, new Character(' '));
+    }
+	
+	protected RelaxedDuckType(Object object) {
+		super(object);
+	}
+	
+	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+		try {
+			Method realMethod = objectClass.getMethod(method.getName(), method.getParameterTypes());
+            return realMethod.invoke(object, args);
+		} catch (NoSuchMethodException e) {
+			return NULL_VALUES.get(method.getReturnType());
+		} catch (Throwable t) {
+			throw t;
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMask.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMask.java
new file mode 100644
index 0000000..484922e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMask.java
@@ -0,0 +1,451 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.mask;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser;
+import org.eclipse.jface.examples.databinding.mask.internal.SWTUtil;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Ensures text widget has the format specified by the edit mask.  Edit masks
+ * are currently defined as follows:
+ * 
+ * The following characters are reserved words that match specific kinds of 
+ * characters:
+ * 
+ * # - digits 0-9
+ * A - uppercase A-Z
+ * a - upper or lowercase a-z, A-Z
+ * n - alphanumeric 0-9, a-z, A-Z
+ *
+ * All other characters are literals.  The above characters may be turned into
+ * literals by preceeding them with a backslash.  Use two backslashes to
+ * denote a backslash.
+ * 
+ * Examples:
+ * 
+ * (###) ###-####  U.S. phone number 
+ * ###-##-####     U.S. Social Security number
+ * \\\###          A literal backslash followed by a literal pound symbol followed by two digits
+ * 
+ * Ideas for future expansion:
+ * 
+ * Quantifiers as postfix modifiers to a token.  ie:
+ * 
+ * #{1, 2}/#{1,2}/####   MM/DD/YYYY date format allowing 1 or 2 digit month or day
+ * 
+ * Literals may only be quantified as {0,1} which means that they only appear
+ * if placeholders on both sides of the literal have data.  This will be used
+ * along with:
+ * 
+ * Right-to-left support for numeric entry.  When digits are being entered and
+ * a decimal point is present in the mask, digits to the left of the decimal
+ * are entered right-to-left but digits to the right of the decimal left-to-right.
+ * This might need to be a separate type of edit mask. (NumericMask, maybe?)
+ * 
+ * Example:
+ * 
+ * $#{0,3},{0,1}#{0,3}.#{0,2}  ie: $123,456.12 or $12.12 or $1,234.12 or $123.12
+ *
+ * 
+ * Here's the basic idea of how the current implementation works (the actual 
+ * implementation is slightly more abstracted and complicated than this):
+ * 
+ * We always let the verify event pass if the user typed a new character or selected/deleted anything.
+ * During the verify event, we post an async runnable.
+ * Inside that async runnable, we:
+ *   - Remember the selection position
+ *   - getText(), then
+ *   - Strip out all literal characters from text
+ *   - Truncate the resulting string to raw length of edit mask without literals
+ *   - Insert literal characters back in the correct positions
+ *   - setText() the resulting string
+ *   - reset the selection to the correct location
+ *   
+ * @since 3.3
+ */
+public class EditMask {
+	
+	public static final String FIELD_TEXT = "text";
+	public static final String FIELD_RAW_TEXT = "rawText";
+	public static final String FIELD_COMPLETE = "complete";
+	protected Text text;
+	protected EditMaskParser editMaskParser;
+	private boolean fireChangeOnKeystroke = true;
+	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
+	
+	protected String oldValidRawText = "";
+	protected String oldValidText = ""; 
+	
+	/**
+	 * Creates an instance that wraps around a text widget and manages its<br>
+	 * formatting.
+	 * 
+	 * @param text
+	 * @param editMask
+	 */
+	public EditMask(Text text) {
+		this.text = text;
+	}
+	
+	/**
+	 * @return the underlying Text control used by EditMask
+	 */
+	public Text getControl() {
+		return this.text;
+	}
+	
+	/**
+	 * Set the edit mask string on the edit mask control.
+	 * 
+	 * @param editMask The edit mask string
+	 */
+	public void setMask(String editMask) {
+		editMaskParser = new EditMaskParser(editMask);
+		text.addVerifyListener(verifyListener);
+		text.addFocusListener(focusListener);
+		text.addDisposeListener(disposeListener);
+		updateTextField.run();
+		oldValidText = text.getText();
+		oldValidRawText = editMaskParser.getRawResult();
+	}
+
+    /**
+     * @param string Sets the text string in the receiver
+     */
+    public void setText(String string) {
+    	String oldValue = text.getText();
+    	if (editMaskParser != null) {
+			editMaskParser.setInput(string);
+			String formattedResult = editMaskParser.getFormattedResult();
+			text.setText(formattedResult);
+			firePropertyChange(FIELD_TEXT, oldValue, formattedResult);
+    	} else {
+    		text.setText(string);
+			firePropertyChange(FIELD_TEXT, oldValue, string);
+    	}
+		oldValidText = text.getText();
+		oldValidRawText = editMaskParser.getRawResult();
+	}
+
+	/**
+	 * @return the actual (formatted) text
+	 */
+	public String getText() {
+		if (editMaskParser != null) {
+			return editMaskParser.getFormattedResult();
+		}
+		return text.getText();
+	}
+	
+	/**
+	 * setRawText takes raw text as a parameter but formats it before
+	 * setting the text in the Text control.
+	 * 
+	 * @param string the raw (unformatted) text
+	 */
+	public void setRawText(String string)  {
+		if (string == null) {
+			string = "";
+		}
+		if (editMaskParser != null) {
+			String oldValue = editMaskParser.getRawResult();
+			editMaskParser.setInput(string);
+			text.setText(editMaskParser.getFormattedResult());
+			firePropertyChange(FIELD_RAW_TEXT, oldValue, string);
+		} else {
+	    	String oldValue = text.getText();
+    		text.setText(string);
+			firePropertyChange(FIELD_RAW_TEXT, oldValue, string);
+		}
+		oldValidText = text.getText();
+		oldValidRawText = editMaskParser.getRawResult();
+	}
+
+	/**
+	 * @return The input text with literals removed
+	 */
+	public String getRawText() {
+		if (editMaskParser != null) {
+			return editMaskParser.getRawResult();
+		}
+		return text.getText();
+	}
+	
+	/**
+	 * @return true if the field is complete according to the mask; false otherwise
+	 */
+	public boolean isComplete() {
+		if (editMaskParser == null) {
+			return true;
+		}
+		return editMaskParser.isComplete();
+	}
+	
+	/**
+	 * Returns the placeholder character.  The placeholder 
+	 * character must be a different character than any character that is 
+	 * allowed as input anywhere in the edit mask.  For example, if the edit
+	 * mask permits spaces to be used as input anywhere, the placeholder 
+	 * character must be something other than a space character.
+	 * <p>
+	 * The space character is the default placeholder character.
+	 * 
+	 * @return the placeholder character
+	 */
+	public char getPlaceholder() {
+		if (editMaskParser == null) {
+			throw new IllegalArgumentException("Have to set an edit mask first");
+		}
+		return editMaskParser.getPlaceholder();
+	}
+	
+	/**
+	 * Sets the placeholder character for the edit mask.  The placeholder 
+	 * character must be a different character than any character that is 
+	 * allowed as input anywhere in the edit mask.  For example, if the edit
+	 * mask permits spaces to be used as input anywhere, the placeholder 
+	 * character must be something other than a space character.
+	 * <p>
+	 * The space character is the default placeholder character.
+	 * 
+	 * @param placeholder The character to use as a placeholder
+	 */
+	public void setPlaceholder(char placeholder) {
+		if (editMaskParser == null) {
+			throw new IllegalArgumentException("Have to set an edit mask first");
+		}
+		editMaskParser.setPlaceholder(placeholder);
+		updateTextField.run();
+		oldValidText = text.getText();
+	}
+
+	/**
+	 * Indicates if change notifications will be fired after every keystroke
+	 * that affects the value of the rawText or only when the value is either
+	 * complete or empty.
+	 * 
+	 * @return true if every change (including changes from one invalid state to
+	 *         another) triggers a change event; false if only empty or valid
+	 *         values trigger a change event.  Defaults to false.
+	 */
+	public boolean isFireChangeOnKeystroke() {
+		return fireChangeOnKeystroke;
+	}
+
+	/**
+	 * Sets if change notifications will be fired after every keystroke that
+	 * affects the value of the rawText or only when the value is either
+	 * complete or empty.
+	 * 
+	 * @param fireChangeOnKeystroke
+	 *            true if every change (including changes from one invalid state
+	 *            to another) triggers a change event; false if only empty or
+	 *            valid values trigger a change event.  Defaults to false.
+	 */
+	public void setFireChangeOnKeystroke(boolean fireChangeOnKeystroke) {
+		this.fireChangeOnKeystroke = fireChangeOnKeystroke;
+	}
+
+	/**
+	 * JavaBeans boilerplate code...
+	 * 
+	 * @param listener
+	 */
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		propertyChangeSupport.addPropertyChangeListener(listener);
+	}
+
+	/**
+	 * JavaBeans boilerplate code...
+	 * 
+	 * @param propertyName
+	 * @param listener
+	 */
+	public void addPropertyChangeListener(String propertyName,
+			PropertyChangeListener listener) {
+		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
+	}
+
+	/**
+	 * JavaBeans boilerplate code...
+	 * 
+	 * @param listener
+	 */
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		propertyChangeSupport.removePropertyChangeListener(listener);
+	}
+
+	/**
+	 * JavaBeans boilerplate code...
+	 * 
+	 * @param propertyName
+	 * @param listener
+	 */
+	public void removePropertyChangeListener(String propertyName,
+			PropertyChangeListener listener) {
+		propertyChangeSupport.removePropertyChangeListener(propertyName,
+				listener);
+	}
+
+	private boolean isEitherValueNotNull(Object oldValue, Object newValue) {
+		return oldValue != null || newValue != null;
+	}
+
+	private void firePropertyChange(String propertyName, Object oldValue,
+			Object newValue) {
+		if (isEitherValueNotNull(oldValue, newValue)) {
+			propertyChangeSupport.firePropertyChange(propertyName,
+					oldValue, newValue);
+		}
+	}
+
+	protected boolean updating = false;
+
+	protected int oldSelection = 0;
+	protected int selection = 0;
+	protected String oldRawText = "";
+   protected boolean replacedSelectedText = false;
+	
+	private VerifyListener verifyListener = new VerifyListener() {
+		public void verifyText(VerifyEvent e) {
+         // If the edit mask is already full, don't let the user type
+         // any new characters
+         if (editMaskParser.isComplete() && // should eventually be .isFull() to account for optional characters
+               e.start == e.end && 
+               e.text.length() > 0) 
+         {
+            e.doit=false;
+            return;
+         }
+         
+			oldSelection = selection;
+			Point selectionRange = text.getSelection();
+         selection = selectionRange.x;
+         
+			if (!updating) {
+   			replacedSelectedText = false;
+   			if (selectionRange.y - selectionRange.x > 0 && e.text.length() > 0) {
+   			   replacedSelectedText = true;
+   			}
+            // If the machine is loaded down (ie: spyware, malware), we might
+            // get another keystroke before asyncExec can process, so we use 
+            // greedyExec instead.
+            SWTUtil.greedyExec(Display.getCurrent(), updateTextField);
+//				Display.getCurrent().asyncExec(updateTextField);
+         }
+		}
+	};
+
+	private Runnable updateTextField = new Runnable() {
+		public void run() {
+			updating = true;
+			try {
+				if (!text.isDisposed()) {
+					Boolean oldIsComplete = new Boolean(editMaskParser.isComplete());
+				
+					editMaskParser.setInput(text.getText());
+					text.setText(editMaskParser.getFormattedResult());
+					String newRawText = editMaskParser.getRawResult();
+				
+					updateSelectionPosition(newRawText);
+					firePropertyChangeEvents(oldIsComplete, newRawText);
+				}
+			} finally {
+				updating = false;
+			}
+		}
+
+		private void updateSelectionPosition(String newRawText) {
+
+         // Adjust the selection
+         if (isInsertingNewCharacter(newRawText) || replacedSelectedText) {
+            // Find the position after where the new character was actually inserted
+            int selectionDelta = 
+               editMaskParser.getNextInputPosition(oldSelection)
+               - oldSelection;
+            if (selectionDelta == 0) {
+               selectionDelta = editMaskParser.getNextInputPosition(selection)
+               - selection;
+            }
+            selection += selectionDelta;
+         }
+         
+			// Did we just type something that was accepted by the mask?
+			if (!newRawText.equals(oldRawText)) { // yep
+            
+            // If the user hits <end>, bounce them back to the end of their actual input
+				int firstIncompletePosition = editMaskParser.getFirstIncompleteInputPosition();
+				if (firstIncompletePosition > 0 && selection > firstIncompletePosition)
+					selection = firstIncompletePosition;
+				text.setSelection(new Point(selection, selection));
+            
+			} else { // nothing was accepted by the mask
+            
+				// Either we backspaced over a literal or we typed an illegal character
+				if (selection > oldSelection) { // typed an illegal character; backup
+					text.setSelection(new Point(selection-1, selection-1));
+				} else { // backspaced over a literal; don't interfere with selection position
+					text.setSelection(new Point(selection, selection));
+				}
+			}
+			oldRawText = newRawText;
+		}
+
+		private boolean isInsertingNewCharacter(String newRawText) {
+			return newRawText.length() > oldRawText.length();
+		}
+
+		private void firePropertyChangeEvents(Boolean oldIsComplete, String newRawText) {
+			Boolean newIsComplete = new Boolean(editMaskParser.isComplete());
+			if (!oldIsComplete.equals(newIsComplete)) {
+				firePropertyChange(FIELD_COMPLETE, oldIsComplete, newIsComplete);
+			}
+			if (!newRawText.equals(oldValidRawText)) {
+				if ( newIsComplete.booleanValue() || "".equals(newRawText) || fireChangeOnKeystroke) {
+					firePropertyChange(FIELD_RAW_TEXT, oldValidRawText, newRawText);
+					firePropertyChange(FIELD_TEXT, oldValidText, text.getText());
+					oldValidText = text.getText();
+					oldValidRawText = newRawText;
+				}
+			}
+		}
+	};
+	
+	private FocusListener focusListener = new FocusAdapter() {
+		public void focusGained(FocusEvent e) {
+			selection = editMaskParser.getFirstIncompleteInputPosition();
+			text.setSelection(selection, selection);
+		}
+	};
+	
+	private DisposeListener disposeListener = new DisposeListener() {
+		public void widgetDisposed(DisposeEvent e) {
+			text.removeVerifyListener(verifyListener);
+			text.removeFocusListener(focusListener);
+			text.removeDisposeListener(disposeListener);
+		}
+	};
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMaskParseException.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMaskParseException.java
new file mode 100644
index 0000000..f2b6aec
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMaskParseException.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.mask;
+
+/**
+ * Indicates a parse error while parsing an edit mask.
+ * 
+ * @since 3.3
+ */
+public class EditMaskParseException extends RuntimeException {
+
+	private static final long serialVersionUID = 8142999683999681500L;
+
+	/**
+	 * Construct a MaskParseException
+	 */
+	public EditMaskParseException() {
+		super();
+	}
+
+	/**
+	 * @param message
+	 * @param cause
+	 */
+	public EditMaskParseException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * @param message
+	 */
+	public EditMaskParseException(String message) {
+		super(message);
+	}
+
+	/**
+	 * @param cause
+	 */
+	public EditMaskParseException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMaskTest.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMaskTest.java
new file mode 100644
index 0000000..49c9c1c
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/EditMaskTest.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.mask;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class EditMaskTest {
+
+   public static void main(String[] args) {
+      Display display = new Display();
+      Shell shell = new Shell(display);
+
+      Text text = new Text(shell, SWT.BORDER);
+      text.setText("XXXXXXXXXXXXX");// Put some X's in there to pad out the field's default size
+      
+      Text text2 = new Text(shell, SWT.BORDER);
+      text2.setText("630XXXXXXXXXX");
+      
+      shell.setLayout(new RowLayout(SWT.VERTICAL));
+      shell.setSize(800, 600);
+      
+      new EditMask(text).setMask("(###) ###-####");
+      new EditMask(text2).setMask("(###) ###-####");
+      
+      shell.open();
+      while (!shell.isDisposed()) {
+         if (!display.readAndDispatch()) {
+            display.sleep();
+         }
+      }
+   }
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/EditMaskLexerAndToken.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/EditMaskLexerAndToken.java
new file mode 100644
index 0000000..d9c61b4
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/EditMaskLexerAndToken.java
@@ -0,0 +1,189 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.mask.internal;
+
+import java.util.ArrayList;
+
+import org.eclipse.jface.examples.databinding.mask.EditMaskParseException;
+
+/**
+ * Lexical analyzer and token for an input mask.  Since input masks have exactly
+ * one token type, we use the same class to be the recognizer and the token 
+ * itself.
+ * 
+ * @since 3.3
+ */
+public class EditMaskLexerAndToken {
+
+	/*
+	 * First the literals that represent the types of characters
+	 */
+	private static ArrayList reservedWords = new ArrayList();{
+		reservedWords.add("#");
+		reservedWords.add("A");
+		reservedWords.add("a");
+		reservedWords.add("n");
+	}
+	
+	/*
+	 * ...and their corresponding regular expressions
+	 */
+	private static ArrayList inputRegexes = new ArrayList();{
+		inputRegexes.add("^[0-9]$");
+		inputRegexes.add("^[A-Z]$");
+		inputRegexes.add("^[a-zA-Z]$");
+		inputRegexes.add("^[0-9a-zA-Z]$");
+	}
+	
+	private String charRegex = null;	// A regex for matching input characters or null
+	private String literal = null;		// The literal character if charRegex is null
+	private boolean readOnly;
+	private String input = null;		// The user's input
+	
+	private boolean recognizeReservedWord(String inputMask, int position) {
+		String input = inputMask.substring(position, position+1);
+		for (int reservedWord = 0; reservedWord < reservedWords.size(); reservedWord++) {
+			if (input.equals(reservedWords.get(reservedWord))) {
+				charRegex = (String) inputRegexes.get(reservedWord);
+				literal = null;
+				input = null;
+				readOnly = false;
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean recognizeBackslashLiteral(String inputMask, int position) throws EditMaskParseException {
+		String input = inputMask.substring(position, position+1);
+		if (input.equals("\\")) {
+			try {
+				input = inputMask.substring(position+1, position+2);
+				charRegex = null;
+				this.input = input;
+				literal = input;
+				readOnly = true;
+				return true;
+			} catch (Throwable t) {
+				throw new EditMaskParseException("Found a \\ without a character after it: " + inputMask);
+			}
+		}
+		return false;
+	}
+
+	private boolean recognizeLiteral(String inputMask, int position) {
+		literal = inputMask.substring(position, position+1);
+		this.input = literal;
+		charRegex = null;
+		readOnly = true;
+		return true;
+	}
+
+	/**
+	 * Initializes itself based on characters in edit mask starting at position; 
+	 * returns number of chars consumed
+	 * 
+	 * @param inputMask The entire edit mask
+	 * @param position The position to begin parsing
+	 * @return The number of characters consumed
+	 * @throws EditMaskParseException If it encountered a syntax error during the parse
+	 */
+	public int initializeEditMask(String inputMask, int position) throws EditMaskParseException {
+		clear();
+		if (recognizeReservedWord(inputMask, position)) {
+			return 1;
+		}
+		if (recognizeBackslashLiteral(inputMask, position)) {
+			return 2;
+		}
+		if (!recognizeLiteral(inputMask, position)) {
+			throw new EditMaskParseException("Should never see this error in this implementation!");
+		}
+		readOnly = true;
+		return 1;
+	}
+
+	/**
+	 * ignores invalid input; stores valid input
+	 * @param inputCharacter 
+	 * @return 
+	 */
+	public boolean accept(String inputCharacter) {
+		if (readOnly) {
+			return false;
+		}
+		if (literal != null) {
+			return false;
+		}
+		if (!canAcceptMoreCharacters()) {
+			return false;
+		}
+		if (inputCharacter.matches(charRegex)) {
+			this.input = inputCharacter;
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * @return Returns the characters it has accepted.  In the current implementation,
+	 * this is exactly one character.  Once quantifiers are implemented, this could
+	 * be many characters.  If no characters have been accepted, returns null.
+	 */
+	public String getInput() {
+		return input;
+	}
+	
+	/**
+	 * Clear any accepted input
+	 */
+	public void clear() {
+		if (!isReadOnly())
+			input = null;
+	}
+
+	/**
+	 * @return true if it's a literal; false if it's a placeholder
+	 */
+	public boolean isReadOnly() {
+		return readOnly;
+	}
+
+	/**
+	 * @return true if it is a literal or if it has accepted the minimum 
+	 * required number of characters
+	 */
+	public boolean isComplete() {
+		if (input != null) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * @return A position may be complete and yet able to accept more characters if
+	 * the position includes optional characters via a quantifier of some type.
+	 * Not implemented right now.
+	 */
+	public boolean canAcceptMoreCharacters() {
+		return !isComplete();
+	}
+
+	/**
+	 * @return the minimum number of characters this RegexLexer must accept 
+	 * in order to be complete.  Because we don't yet support quantifiers, this
+	 * is currently always 1.
+	 */
+	public int getMinimumLength() {
+		return 1;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/EditMaskParser.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/EditMaskParser.java
new file mode 100644
index 0000000..fc87f9e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/EditMaskParser.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.mask.internal;
+
+import java.util.LinkedList;
+
+import org.eclipse.jface.examples.databinding.mask.EditMaskParseException;
+
+/**
+ * @since 3.3
+ */
+public class EditMaskParser {
+	private EditMaskLexerAndToken[] expectedTokens;
+	private char placeholder = ' ';
+	
+	/**
+	 * @param editMask The complete edit mask 
+	 * @throws EditMaskParseException
+	 */
+	public EditMaskParser(String editMask) throws EditMaskParseException {
+		LinkedList tokens = new LinkedList();
+		int position = 0;
+		while (position < editMask.length()) {
+			EditMaskLexerAndToken token = new EditMaskLexerAndToken();
+			position += token.initializeEditMask(editMask, position);
+			tokens.add(token);
+		}
+		expectedTokens = (EditMaskLexerAndToken[]) tokens.toArray(new EditMaskLexerAndToken[tokens.size()]);
+	}
+	
+	/**
+	 * @param input the user input which may or may not be in valid format
+	 */
+	public void setInput(String input) {
+		for (int i = 0; i < expectedTokens.length; i++) {
+			expectedTokens[i].clear();
+		}
+		int tokenPosition = 0;
+		int inputPosition = 0;
+		while (inputPosition < input.length() && tokenPosition < expectedTokens.length) {
+			while (tokenPosition < expectedTokens.length &&
+					(expectedTokens[tokenPosition].isComplete() || 
+					 expectedTokens[tokenPosition].isReadOnly())) 
+			{
+				++tokenPosition;
+			}
+			if (tokenPosition < expectedTokens.length) {
+				while (inputPosition < input.length() && !expectedTokens[tokenPosition].isComplete()) {
+					String inputChar = input.substring(inputPosition, inputPosition+1);
+					expectedTokens[tokenPosition].accept(inputChar);
+					++inputPosition;
+				}
+			}
+		}
+	}
+	
+	/**
+	 * @return the formatted version of the user input
+	 */
+	public String getFormattedResult() {
+		StringBuffer result = new StringBuffer();
+		for (int i = 0; i < expectedTokens.length; i++) {
+			String outputChar = expectedTokens[i].getInput();
+			if (outputChar == null) {
+				outputChar = "" + placeholder;
+			}
+			result.append(outputChar);
+		}
+		return result.toString();
+	}
+	
+	/**
+	 * @return the user input with all literals removed
+	 */
+	public String getRawResult() {
+		StringBuffer result = new StringBuffer();
+		for (int i = 0; i < expectedTokens.length; i++) {
+			if (expectedTokens[i].isReadOnly()) {
+				continue;
+			}
+			String outputChar = expectedTokens[i].getInput();
+			if (outputChar == null) {
+				outputChar = "";
+			}
+			result.append(outputChar);
+		}
+		return result.toString();
+	}
+
+	/**
+	 * @return true if the current input is a valid input
+	 */
+	public boolean isComplete() {
+		for (int i = 0; i < expectedTokens.length; i++) {
+			if (!expectedTokens[i].isComplete()) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	/**
+	 * @param startingAt The current index within the user input string
+	 * @return The first non-read-only index greater than or equal to startingAt
+	 */
+	public int getNextInputPosition(int startingAt) {
+		while (startingAt < expectedTokens.length-1 && expectedTokens[startingAt].isReadOnly()) {
+			++startingAt;
+		}
+		return startingAt;
+	}
+	
+	/**
+	 * @return the first input position whose token is not marked as complete.  Returns -1 if all are complete
+	 */
+	public int getFirstIncompleteInputPosition() {
+		for (int position = 0; position < expectedTokens.length; position++) {
+			if (!expectedTokens[position].isComplete()) {
+				return position;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * @return Returns the placeholder.
+	 */
+	public char getPlaceholder() {
+		return placeholder;
+	}
+
+	/**
+	 * @param placeholder The placeholder to set.
+	 */
+	public void setPlaceholder(char placeholder) {
+		this.placeholder = placeholder;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/SWTUtil.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/SWTUtil.java
new file mode 100644
index 0000000..8bd2b2a
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/SWTUtil.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.mask.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 1.0
+ */
+public class SWTUtil {
+    /**
+     * Stores a work queue for each display
+     */
+    private static Map mapDisplayOntoWorkQueue = new HashMap();
+
+    private SWTUtil() {
+    }
+
+    /**
+     * 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() == 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(final Display d) {
+        WorkQueue result;
+        synchronized (mapDisplayOntoWorkQueue) {
+            // Look for existing queue
+            result = (WorkQueue) mapDisplayOntoWorkQueue.get(d);
+
+            if (result == null) {
+                // If none, create new queue
+                result = new WorkQueue(d);
+                final WorkQueue q = result;
+                mapDisplayOntoWorkQueue.put(d, result);
+                d.asyncExec(new Runnable() {
+                    public void run() {
+                        d.disposeExec(new Runnable() {
+                            public void run() {
+                                synchronized (mapDisplayOntoWorkQueue) {
+                                    q.cancelAll();
+                                    mapDisplayOntoWorkQueue.remove(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 = (int)(i1 * ratio + i2 * (1.0d - ratio));
+        if (result < 0) result = 0;
+        if (result > 255) result = 255;
+        return result;
+    }
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/WorkQueue.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/WorkQueue.java
new file mode 100644
index 0000000..e5eeaea
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/mask/internal/WorkQueue.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package org.eclipse.jface.examples.databinding.mask.internal;
+
+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;
+
+/**
+ * @since 3.2
+ *
+ */
+public class WorkQueue {
+	
+    private boolean updateScheduled = false;
+
+    private boolean paintListenerAttached = false;
+
+    private LinkedList pendingWork = new LinkedList();
+
+    private Display d;
+
+    private Set pendingWorkSet = new HashSet();
+
+    private Runnable updateJob = new Runnable() {
+        public void run() {
+            doUpdate();
+            updateScheduled = false;
+        }
+    };
+
+    private Listener paintListener = new Listener() {
+        public void handleEvent(Event event) {
+            paintListenerAttached = false;
+            d.removeFilter(SWT.Paint, this);
+            doUpdate();
+        }
+    };
+
+    /**
+     * @param targetDisplay
+     */
+    public WorkQueue(Display targetDisplay) {
+        d = targetDisplay;
+    }
+
+    private void doUpdate() {
+        for (;;) {
+            Runnable next;
+            synchronized (pendingWork) {
+                if (pendingWork.isEmpty()) {
+                    break;
+                }
+                next = (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() == d) {
+                if (!paintListenerAttached) {
+                    paintListenerAttached = true;
+                    d.addFilter(SWT.Paint, paintListener);
+                }
+            }
+        }
+    }
+
+    /**
+     * Cancels a previously-scheduled runnable. Has no effect if the given
+     * runnable was not previously scheduled or has already executed.
+     * 
+     * @param toCancel
+     *            runnable to cancel
+     */
+    public void cancelExec(Runnable toCancel) {
+        synchronized (pendingWork) {
+            pendingWork.remove(toCancel);
+            pendingWorkSet.remove(toCancel);
+        }
+    }
+
+    /**
+     * Cancels all pending work.
+     */
+    public void cancelAll() {
+        synchronized (pendingWork) {
+            pendingWork.clear();
+            pendingWorkSet.clear();
+        }
+    }
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Account.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Account.java
new file mode 100644
index 0000000..7509d64
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Account.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import java.util.Date;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Account extends ModelObject {
+
+	private String country;
+	private String firstName;
+	private String lastName;
+	private String state;
+	private String phone;	
+	private Date expiryDate;
+
+	public void setFirstName(String string) {
+		String oldValue = firstName;
+		firstName = string;
+		firePropertyChange("firstName", oldValue, string);		
+	}
+
+	public void setLastName(String string) {
+		String oldValue = lastName;
+		lastName = string;
+		firePropertyChange("lastName", oldValue, string);				
+	}
+
+	public void setState(String string) {
+		String oldValue = state;
+		state = string;
+		firePropertyChange("state", oldValue, string);		
+	}
+
+	public void setPhone(String string) {
+		String oldValue = phone;
+		phone = string;
+		firePropertyChange("phone", oldValue, phone);		
+	}
+
+	public void setCountry(String string) {
+		Object oldValue = country;
+		country = string;
+		firePropertyChange("country", oldValue, string);
+	}
+
+	public String getCountry() {
+		return country;
+	}
+
+	public String getFirstName() {
+		return firstName;
+	}
+
+	public String getLastName() {
+		return lastName;
+	}
+
+	public String getState() {
+		return state;
+	}
+
+	public String getPhone() {
+		return phone;
+	}
+
+	public Date getExpiryDate() {
+		return expiryDate;
+	}
+
+	public void setExpiryDate(Date expiryDate) {
+		firePropertyChange("expiryDate", this.expiryDate,
+				this.expiryDate = expiryDate);
+	}
+
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Adventure.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Adventure.java
new file mode 100644
index 0000000..d78eed0
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Adventure.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Adventure extends ModelObject {
+
+	private boolean petsAllowed;
+
+	private double price;
+
+	private Lodging defaultLodging;
+
+	private String name;
+
+	private String description;
+
+	private String location;
+	
+	private int maxNumberOfPeople;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String string) {
+		Object oldValue = name;
+		name = string;
+		firePropertyChange("name", oldValue, name);
+	}
+	
+	public int getMaxNumberOfPeople(){
+		return maxNumberOfPeople;
+	}
+	
+	public void setMaxNumberOfPeople(int anInt) {
+		int oldValue = maxNumberOfPeople;
+		maxNumberOfPeople = anInt;
+		firePropertyChange("maxNumberOfPeople", oldValue, maxNumberOfPeople);
+	}
+	
+	public IValidator getMaxNumberOfPeopleDomainValidator() {
+		return new IValidator() {
+			public IStatus validate(Object value) {
+				int intValue = ((Integer)value).intValue();
+				if (intValue < 1 || intValue > 20) {
+					return ValidationStatus
+							.error("Max number of people must be between 1 and 20 inclusive");
+				}
+				return null;
+			}
+		};
+	}
+
+	public Lodging getDefaultLodging() {
+		return defaultLodging;
+	}
+
+	public void setDefaultLodging(Lodging lodging) {
+		Object oldValue = defaultLodging;
+		defaultLodging = lodging;
+		firePropertyChange("defaultLodging", oldValue, defaultLodging);
+	}
+
+	public void setPrice(double d) {
+		double oldValue = price;
+		price = d;
+		firePropertyChange("price", new Double(oldValue), new Double(price));
+	}
+
+	public double getPrice() {
+		return price;
+	}
+
+	public void setPetsAllowed(boolean b) {
+		boolean oldValue = petsAllowed;
+		petsAllowed = b;
+		firePropertyChange("petsAllowed", new Boolean(oldValue), new Boolean(
+				petsAllowed));
+	}
+
+	public boolean isPetsAllowed() {
+		return petsAllowed;
+	}
+
+	public void setDescription(String string) {
+		Object oldValue = description;
+		description = string;
+		firePropertyChange("description", oldValue, description);
+	}
+
+	public void setLocation(String string) {
+		Object oldValue = location;
+		location = string;
+		firePropertyChange("location", oldValue, location);
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/AdventureFactory.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/AdventureFactory.java
new file mode 100644
index 0000000..2b95e13
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/AdventureFactory.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+public class AdventureFactory {
+
+	public Catalog createCatalog() {
+		return new Catalog();
+	}
+
+	public Category createCategory() {
+		return new Category();
+	}
+
+	public Adventure createAdventure() {
+		return new Adventure();
+	}
+
+	public Lodging createLodging() {
+		return new Lodging();
+	}
+
+	public Transportation createTransportation() {
+		return new Transportation();
+	}
+
+	public Account createAccount() {
+		return new Account();
+	}
+
+	public Cart createCart() {
+		return new Cart();
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/AggregateObservableValue.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/AggregateObservableValue.java
new file mode 100644
index 0000000..728d7e1
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/AggregateObservableValue.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import java.util.StringTokenizer;
+
+import org.eclipse.core.databinding.observable.Diffs;
+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;
+
+/**
+ * @since 3.2
+ *
+ */
+public class AggregateObservableValue extends AbstractObservableValue {
+
+	private IObservableValue[] observableValues;
+
+	private String delimiter;
+
+	private boolean updating = false;
+
+	private String currentValue;
+
+	private IValueChangeListener listener = new IValueChangeListener() {
+		public void handleValueChange(ValueChangeEvent event) {
+			if (!updating) {
+				fireValueChange(Diffs.createValueDiff(currentValue,
+						doGetValue()));
+			}
+		}
+	};
+
+	/**
+	 * @param observableValues
+	 * @param delimiter
+	 */
+	public AggregateObservableValue(IObservableValue[] observableValues,
+			String delimiter) {
+		this.observableValues = observableValues;
+		this.delimiter = delimiter;
+		for (int i = 0; i < observableValues.length; i++) {
+			observableValues[i].addValueChangeListener(listener);
+		}
+		doGetValue();
+	}
+
+	public void doSetValue(Object value) {
+		Object oldValue = doGetValue();
+		StringTokenizer tokenizer = new StringTokenizer((String) value,
+				delimiter);
+		try {
+			updating = true;
+			for (int i = 0; i < observableValues.length; i++) {
+				if (tokenizer.hasMoreElements()) {
+					observableValues[i].setValue(tokenizer.nextElement());
+				} else {
+					observableValues[i].setValue(null);
+				}
+			}
+		} finally {
+			updating = false;
+		}
+		doGetValue();
+		fireValueChange(Diffs.createValueDiff(oldValue, value));
+	}
+
+	public Object doGetValue() {
+		StringBuffer result = new StringBuffer();
+		for (int i = 0; i < observableValues.length; i++) {
+			if (i > 0 & i < observableValues.length) {
+				result.append(delimiter);
+			}
+			result.append(observableValues[i].getValue());
+		}
+		currentValue = result.toString();
+		return currentValue;
+	}
+
+	public Object getValueType() {
+		return String.class;
+	}
+
+	public synchronized void dispose() {
+		for (int i = 0; i < observableValues.length; i++) {
+			observableValues[i].removeValueChangeListener(listener);
+		}
+		super.dispose();
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Cart.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Cart.java
new file mode 100644
index 0000000..b82f45e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Cart.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+public class Cart {
+
+	public void setAdventureDays(int i) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public int getLodgingDays() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Catalog.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Catalog.java
new file mode 100644
index 0000000..edbae49
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Catalog.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Catalog extends ModelObject {
+
+	private Category[] categories = new Category[0];
+
+	private Lodging[] lodgings = new Lodging[0];
+
+	private Transportation[] transportations = new Transportation[0];
+
+	private Account[] accounts = new Account[0];
+	
+	private List signons = new ArrayList();
+	
+	public List getSignons(){
+		return signons;
+	}
+	
+	public void addSignon(Signon aSignon){
+		signons.add(aSignon);
+		firePropertyChange("signons",null,null);
+	}
+	
+	public void removeSignon(Signon aSignon){
+		signons.remove(aSignon);
+		firePropertyChange("signons",null,null);
+	}	
+
+	public Category[] getCategories() {
+		return categories;
+	}
+
+	public void addCategory(Category category) {
+		categories = (Category[]) append(categories, category);
+		firePropertyChange("categories", null, null);
+	}
+
+	public void addLodging(Lodging lodging) {
+		lodgings = (Lodging[]) append(lodgings, lodging);
+		firePropertyChange("lodgings", null, null);
+	}
+
+	public void addTransportation(Transportation transportation) {
+		transportations = (Transportation[]) append(transportations, transportation);
+		firePropertyChange("transportations", null, null);
+	}
+
+	public void addAccount(Account account) {
+		accounts = (Account[]) append(accounts, account);
+		firePropertyChange("accounts", null, null);
+	}
+
+	public Lodging[] getLodgings() {
+		return lodgings;
+	}
+
+	public void removeLodging(Lodging lodging) {
+		lodgings = (Lodging[]) remove(lodgings, lodging);
+		firePropertyChange("lodgings", null, null);
+	}
+	
+	public void removeAccount(Account anAccount) {
+		accounts = (Account[]) remove(accounts, anAccount);
+		firePropertyChange("accounts", null, null);
+	}	
+
+	public Account[] getAccounts() {
+		return accounts;
+	}
+	
+	public Transportation[] getTransporations(){
+		return transportations;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Category.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Category.java
new file mode 100644
index 0000000..778a31c
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Category.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Category extends ModelObject {
+
+	private String name;
+
+	private Adventure[] adventures = new Adventure[0];
+
+	public void setName(String string) {
+		Object oldValue = name;
+		name = string;
+		firePropertyChange("name", oldValue, name);
+	}
+
+	public void addAdventure(Adventure adventure) {
+		adventures = (Adventure[]) append(adventures, adventure);
+		firePropertyChange("adventures", null, null);
+	}
+
+	public Adventure[] getAdventures() {
+		return adventures;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Lodging.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Lodging.java
new file mode 100644
index 0000000..0b9d081
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Lodging.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Lodging extends ModelObject {
+
+	private String name;
+	private String description;
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String string) {
+		Object oldValue = description;
+		description = string;
+		firePropertyChange("description",oldValue,description);
+	}
+
+	public void setName(String string) {
+		Object oldValue = name;
+		name = string;
+		firePropertyChange("name",oldValue,name);
+	}
+	
+	public String getName() {
+		return name;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/PriceModelObject.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/PriceModelObject.java
new file mode 100644
index 0000000..35e3f5e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/PriceModelObject.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+
+public class PriceModelObject extends ModelObject {
+	
+	private double price;
+	
+	public double getDouble(){
+		return price;
+	}
+	public void setPrice(double aPrice){
+		int oldDollars = getDollars();
+		int oldCents = getCents();
+		double oldValue = price;
+		price = aPrice;
+		firePropertyChange("dollars",oldDollars,getDollars());
+		firePropertyChange("cents",oldCents,getCents());
+		firePropertyChange("price",new Double(oldValue), new Double(price));
+	}
+	
+	public double getPrice(){
+		return price;
+	}
+
+	public int getCents(){
+		return (int) (100*price - 100*Math.floor(price));
+	}
+	
+	public void setCents(int cents){
+		double oldPrice = getPrice();
+		int oldCents = getCents();
+		price = getDollars() + cents *.01;
+		firePropertyChange("cents",oldCents,getCents());
+		firePropertyChange("price", new Double(oldPrice), new Double(price));
+	}
+	
+	public int getDollars(){
+		return new Double(price).intValue();
+	}
+	
+	public void setDollars(int dollars){
+		double oldPrice = getPrice();
+		int oldDollars = getDollars();
+		price = dollars + getCents() *.01;
+		firePropertyChange("dollars",oldDollars,getDollars());
+		firePropertyChange("price", new Double(oldPrice), new Double(price));
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SampleData.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SampleData.java
new file mode 100644
index 0000000..f30a159
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SampleData.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * 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
+ *     Brad Reynolds - bug 116920
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+
+public class SampleData {
+
+	public static Category WINTER_CATEGORY;
+
+	public static Category SUMMER_CATEGORY;
+
+	public static Adventure BEACH_HOLIDAY;
+
+	public static Adventure RAFTING_HOLIDAY;
+
+	public static Adventure WINTER_HOLIDAY;
+
+	public static Adventure ICE_FISHING;
+
+	public static Lodging FIVE_STAR_HOTEL;
+
+	public static Lodging YOUTH_HOSTEL;
+
+	public static Lodging CAMP_GROUND;
+
+	public static Catalog CATALOG_2005;
+
+	public static Transportation GREYHOUND_BUS;
+
+	public static Transportation EXECUTIVE_JET;
+
+	public static Account PRESIDENT;
+
+	public static Account DENTIST;
+
+	public static Account SANTA_CLAUS;
+
+	public static Cart CART;
+
+	public static AdventureFactory FACTORY;
+
+	// public static ITree CATALOG_TREE;
+	//	
+	// public static ITree CATEGORY_TREE;
+
+	public static Signon SIGNON_ADMINISTRATOR;
+
+	public static Signon SIGNON_JOEBLOGGS;
+
+	static {
+		initializeData();
+	}
+
+	public static void initializeData() {
+
+		FACTORY = new AdventureFactory();
+
+		CATALOG_2005 = FACTORY.createCatalog();
+
+		// Categories
+		WINTER_CATEGORY = FACTORY.createCategory();
+		WINTER_CATEGORY.setName("Freeze Adventures");
+		WINTER_CATEGORY.setId("100");
+		CATALOG_2005.addCategory(WINTER_CATEGORY);
+
+		SUMMER_CATEGORY = FACTORY.createCategory();
+		SUMMER_CATEGORY.setName("Hot Adventures");
+		SUMMER_CATEGORY.setId("200");
+		CATALOG_2005.addCategory(SUMMER_CATEGORY);
+
+		// Adventures
+		WINTER_HOLIDAY = FACTORY.createAdventure();
+		WINTER_HOLIDAY.setDescription("Winter holiday in France");
+		WINTER_HOLIDAY.setName("Ski Alps");
+		WINTER_HOLIDAY.setLocation("Chamonix");
+		WINTER_HOLIDAY.setPrice(4000.52d);
+		WINTER_HOLIDAY.setId("150");
+		WINTER_HOLIDAY.setMaxNumberOfPeople(3);
+		WINTER_CATEGORY.addAdventure(WINTER_HOLIDAY);
+
+		ICE_FISHING = FACTORY.createAdventure();
+		ICE_FISHING.setDescription("Ice Fishing in Helsinki");
+		ICE_FISHING.setName("Ice Fishing");
+		ICE_FISHING.setLocation("Finland");
+		ICE_FISHING.setPrice(375.55d);
+		WINTER_CATEGORY.addAdventure(ICE_FISHING);
+
+		BEACH_HOLIDAY = FACTORY.createAdventure();
+		BEACH_HOLIDAY.setDescription("Beach holiday in Spain");
+		BEACH_HOLIDAY.setName("Playa");
+		BEACH_HOLIDAY.setLocation("Lloret de Mar");
+		BEACH_HOLIDAY.setPrice(2000.52d);
+		BEACH_HOLIDAY.setId("250");
+		SUMMER_CATEGORY.addAdventure(BEACH_HOLIDAY);
+
+		RAFTING_HOLIDAY = FACTORY.createAdventure();
+		RAFTING_HOLIDAY
+				.setDescription("White water rafting on the Ottawa river");
+		RAFTING_HOLIDAY.setName("Whitewater");
+		RAFTING_HOLIDAY.setLocation("Ottawa");
+		RAFTING_HOLIDAY.setPrice(8000.52d);
+		RAFTING_HOLIDAY.setId("270");
+		SUMMER_CATEGORY.addAdventure(RAFTING_HOLIDAY);
+
+		// Lodgings
+		FIVE_STAR_HOTEL = FACTORY.createLodging();
+		FIVE_STAR_HOTEL.setDescription("Deluxe palace");
+		FIVE_STAR_HOTEL.setName("Flashy");
+		YOUTH_HOSTEL = FACTORY.createLodging();
+		YOUTH_HOSTEL.setDescription("Youth Hostel");
+		YOUTH_HOSTEL.setName("Basic");
+		CAMP_GROUND = FACTORY.createLodging();
+		CAMP_GROUND.setDescription("Camp ground");
+		CAMP_GROUND.setName("WetAndCold");
+		CATALOG_2005.addLodging(FIVE_STAR_HOTEL);
+		CATALOG_2005.addLodging(YOUTH_HOSTEL);
+		CATALOG_2005.addLodging(CAMP_GROUND);
+		WINTER_HOLIDAY.setDefaultLodging(YOUTH_HOSTEL);
+
+		// Transporation
+		GREYHOUND_BUS = FACTORY.createTransportation();
+		GREYHOUND_BUS.setArrivalTime("14:30");
+		GREYHOUND_BUS.setPrice(25.50);
+		CATALOG_2005.addTransportation(GREYHOUND_BUS);
+		EXECUTIVE_JET = FACTORY.createTransportation();
+		EXECUTIVE_JET.setArrivalTime("11:10");
+		EXECUTIVE_JET.setPrice(1500.99);
+		CATALOG_2005.addTransportation(EXECUTIVE_JET);
+
+		// Accounts
+		PRESIDENT = FACTORY.createAccount();
+		PRESIDENT.setFirstName("George");
+		PRESIDENT.setLastName("Bush");
+		PRESIDENT.setState("TX");
+		PRESIDENT.setPhone("1112223333");
+		PRESIDENT.setCountry("U.S.A");
+		DENTIST = FACTORY.createAccount();
+		DENTIST.setFirstName("Tooth");
+		DENTIST.setLastName("Fairy");
+		DENTIST.setState("CA");
+		DENTIST.setPhone("4543219876");
+		DENTIST.setCountry("PainLand");
+		SANTA_CLAUS = FACTORY.createAccount();
+		SANTA_CLAUS.setFirstName("Chris");
+		SANTA_CLAUS.setLastName("Chringle");
+		SANTA_CLAUS.setState("WI");
+		SANTA_CLAUS.setPhone("8617429856");
+		SANTA_CLAUS.setCountry("NorthPole");
+		CATALOG_2005.addAccount(PRESIDENT);
+		CATALOG_2005.addAccount(DENTIST);
+		CATALOG_2005.addAccount(SANTA_CLAUS);
+
+		// Signons
+		SIGNON_ADMINISTRATOR = new Signon("Administrator", "Foo123Bar");
+		SIGNON_JOEBLOGGS = new Signon("JoeBloggs", "Harry5Potter");
+		CATALOG_2005.addSignon(SIGNON_ADMINISTRATOR);
+		CATALOG_2005.addSignon(SIGNON_JOEBLOGGS);
+
+		CART = FACTORY.createCart();
+
+		// initTrees();
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Signon.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Signon.java
new file mode 100644
index 0000000..f6fae9f
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Signon.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Signon extends ModelObject {
+	
+	String userId;
+	String password;
+	
+	public Signon(String aUserId, String aPassword) {
+		userId = aUserId;
+		password = aPassword;
+	}
+	public String getPassword() {
+		return password;
+	}
+	public void setPassword(String aPassword) {
+		String oldValue = password;
+		password = aPassword;
+		firePropertyChange("password",oldValue,password);
+	}
+	public String getUserId() {
+		return userId;
+	}
+	public void setUserId(String aUserId) {
+		String oldValue = userId;
+		userId = aUserId;
+		firePropertyChange("userId",oldValue,userId);
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleCart.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleCart.java
new file mode 100644
index 0000000..8fa45fa
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleCart.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+/**
+ * @since 3.2
+ *
+ */
+public class SimpleCart extends ModelObject {
+
+	private int numItems;
+
+	public int getNumItems() {
+		return numItems;
+	}
+
+	public void setNumItems(int numItems) {
+		firePropertyChange("numItems", this.numItems, this.numItems = numItems);
+	}
+	
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleModel.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleModel.java
new file mode 100644
index 0000000..a1d0657
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleModel.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005, 2006 David Orme <djo@coconut-palm-software.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:
+ *     David Orme     - Initial API and implementation
+ */
+package org.eclipse.jface.examples.databinding.model;
+
+import java.util.LinkedList;
+
+
+public class SimpleModel {
+	public SimpleModel() {
+		// Add some sample data to our personList...
+		personList.add(new SimplePerson("John", "1234", "Wheaton", "IL"));
+		personList.add(new SimplePerson("Jane", "1234", "Glen Ellyn", "IL"));
+		personList.add(new SimplePerson("Frank", "1234", "Lombard", "IL"));
+		personList.add(new SimplePerson("Joe", "1234", "Elmhurst", "IL"));
+		personList.add(new SimplePerson("Chet", "1234", "Oak Lawn", "IL"));
+		personList.add(new SimplePerson("Wilbur", "1234", "Austin", "IL"));
+		personList.add(new SimplePerson("Elmo", "1234", "Chicago", "IL"));
+	}
+
+	// Now a PersonList property...
+	
+	LinkedList personList = new LinkedList();
+	
+	public LinkedList getPersonList() {
+		return personList;
+	}
+
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleOrder.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleOrder.java
new file mode 100644
index 0000000..7636a4c
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimpleOrder.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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:
+ *     The Pampered Chef - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.model;
+
+import java.util.Date;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+/**
+ * @since 1.0
+ *
+ */
+public class SimpleOrder extends ModelObject {
+
+	private int orderNumber;
+	private Date date;
+
+	/**
+	 * @return Returns the date.
+	 */
+	public Date getDate() {
+		return date;
+	}
+
+	/**
+	 * @param date The date to set.
+	 */
+	public void setDate(Date date) {
+		this.date = date;
+	}
+
+	/**
+	 * @return Returns the orderNumber.
+	 */
+	public int getOrderNumber() {
+		return orderNumber;
+	}
+
+	/**
+	 * @param orderNumber The orderNumber to set.
+	 */
+	public void setOrderNumber(int orderNumber) {
+		this.orderNumber = orderNumber;
+	}
+
+	/**
+	 * @param i
+	 * @param date
+	 */
+	public SimpleOrder(int i, Date date) {
+		this.orderNumber = i;
+		this.date = date;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimplePerson.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimplePerson.java
new file mode 100644
index 0000000..a04ecf6
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/SimplePerson.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005, 2008 David Orme <djo@coconut-palm-software.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:
+ *     David Orme     - Initial API and implementation
+ *     Brad Reynolds (bug 139407)
+ */
+package org.eclipse.jface.examples.databinding.model;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class SimplePerson extends ModelObject {
+	
+	private String name = "";
+	private String address = "";
+	private String city = "";
+	private String state = "";
+	private SimpleCart cart = new SimpleCart();
+	
+	private List orders = new LinkedList();
+	
+	public SimplePerson(String name, String address, String city, String state) {
+		this.name = name;
+		this.address = address;
+		this.city = city;
+		this.state = state;
+		
+		int numOrders = (int) (Math.random() * 5);
+		for (int i=0; i < numOrders; ++i) {
+			orders.add(new SimpleOrder(i, new Date()));
+		}
+	}
+	
+	public SimplePerson() {}
+
+	/**
+	 * @return Returns the address.
+	 */
+	public String getAddress() {
+		return address;
+	}
+
+	/**
+	 * @param address The address to set.
+	 */
+	public void setAddress(String address) {
+		String old = this.address;
+		this.address = address;
+		firePropertyChange("address", old, address);
+	}
+
+	/**
+	 * @return Returns the city.
+	 */
+	public String getCity() {
+		return city;
+	}
+
+	/**
+	 * @param city The city to set.
+	 */
+	public void setCity(String city) {
+		String old = this.city;
+		firePropertyChange("city", old, this.city = city);
+	}
+
+	/**
+	 * @return Returns the name.
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @param name The name to set.
+	 */
+	public void setName(String name) {
+		firePropertyChange("name", this.name, this.name = name);
+	}
+
+	/**
+	 * @return Returns the state.
+	 */
+	public String getState() {
+		return state;
+	}
+
+	/**
+	 * @param state The state to set.
+	 */
+	public void setState(String state) {
+		firePropertyChange("state", this.state, this.state = state); //$NON-NLS-1$
+	}
+	
+	/**
+	 * @return Returns the orders.
+	 */
+	public List getOrders() {
+		return orders;
+	}
+
+	public SimpleCart getCart() {
+		return cart;
+	}
+
+	public void setCart(SimpleCart cart) {
+		firePropertyChange("cart", this.cart, this.cart = cart); //$NON-NLS-1$
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Transportation.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Transportation.java
new file mode 100644
index 0000000..39fbc08
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/model/Transportation.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.model;
+
+import org.eclipse.jface.examples.databinding.ModelObject;
+
+public class Transportation extends ModelObject {
+
+	private String arrivalTime;
+	private double price;
+
+	public void setArrivalTime(String string) {
+		String oldValue = arrivalTime;
+		arrivalTime = string;
+		firePropertyChange("arrivaltime",oldValue,string);
+	}
+	
+	public String getArrivalTime(){
+		return arrivalTime;
+	}
+
+	public double getPrice() {
+		return price;
+	}
+
+	public void setPrice(double aPrice) {
+		double oldPrice = price;
+		price = aPrice;
+		firePropertyChange("price",new Double(oldPrice),new Double(price));
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/nestedselection/TestMasterDetail.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/nestedselection/TestMasterDetail.java
new file mode 100644
index 0000000..a88f8bc
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/nestedselection/TestMasterDetail.java
@@ -0,0 +1,286 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 260329, 260337
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.nestedselection;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.IObserving;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+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.conversion.ObjectToStringConverter;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.examples.databinding.model.SimpleModel;
+import org.eclipse.jface.examples.databinding.model.SimpleOrder;
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 1.0
+ * 
+ */
+public class TestMasterDetail {
+	/**
+	 * @since 3.2
+	 * 
+	 */
+	private static final class CustomUpdateValueStrategy extends
+			UpdateValueStrategy {
+		protected IStatus doSet(IObservableValue observableValue, Object value) {
+			IStatus result = super.doSet(observableValue, value);
+			if (result.isOK()) {
+				Object changed = observableValue;
+				if (changed instanceof IObserving) {
+					changed = ((IObserving) changed).getObserved();
+				}
+				System.out.println("changed: " + changed);
+			}
+			return result;
+		}
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		new TestMasterDetail().run();
+	}
+
+	private Shell shell = null; // @jve:decl-index=0:visual-constraint="10,10"
+
+	private Table personsTable = null;
+
+	private Label label1 = null;
+
+	private Text name = null;
+
+	private Label label2 = null;
+
+	private Text address = null;
+
+	private Label label3 = null;
+
+	private Text city = null;
+
+	private Label label4 = null;
+
+	private Text state = null;
+
+	private Table ordersTable = null;
+
+	private Text validationStatus;
+
+	/**
+	 * This method initializes table
+	 * 
+	 */
+	private void createTable() {
+		GridData gridData = new org.eclipse.swt.layout.GridData();
+		gridData.grabExcessHorizontalSpace = true;
+		gridData.verticalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData.horizontalSpan = 2;
+		gridData.grabExcessVerticalSpace = true;
+		gridData.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		personsTable = new Table(shell, SWT.FULL_SELECTION);
+		personsTable.setHeaderVisible(true);
+		personsTable.setLayoutData(gridData);
+		personsTable.setLinesVisible(true);
+		TableColumn tableColumn = new TableColumn(personsTable, SWT.NONE);
+		tableColumn.setWidth(60);
+		tableColumn.setText("Name");
+		TableColumn tableColumn1 = new TableColumn(personsTable, SWT.NONE);
+		tableColumn1.setWidth(60);
+		tableColumn1.setText("State");
+	}
+
+	/**
+	 * This method initializes table1
+	 * 
+	 */
+	private void createTable1() {
+		GridData gridData5 = new org.eclipse.swt.layout.GridData();
+		gridData5.horizontalSpan = 2;
+		gridData5.verticalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData5.grabExcessHorizontalSpace = true;
+		gridData5.grabExcessVerticalSpace = true;
+		gridData5.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		ordersTable = new Table(shell, SWT.FULL_SELECTION);
+		ordersTable.setHeaderVisible(true);
+		ordersTable.setLayoutData(gridData5);
+		ordersTable.setLinesVisible(true);
+		TableColumn tableColumn2 = new TableColumn(ordersTable, SWT.NONE);
+		tableColumn2.setWidth(60);
+		tableColumn2.setText("Order No");
+		TableColumn tableColumn3 = new TableColumn(ordersTable, SWT.NONE);
+		tableColumn3.setWidth(60);
+		tableColumn3.setText("Date");
+	}
+
+	/**
+	 * This method initializes sShell
+	 */
+	private void createShell() {
+		GridData gridData5 = new org.eclipse.swt.layout.GridData();
+		gridData5.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData5.verticalAlignment = org.eclipse.swt.layout.GridData.CENTER;
+		GridData gridData4 = new org.eclipse.swt.layout.GridData();
+		gridData4.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData4.verticalAlignment = org.eclipse.swt.layout.GridData.CENTER;
+		GridData gridData3 = new org.eclipse.swt.layout.GridData();
+		gridData3.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData3.verticalAlignment = org.eclipse.swt.layout.GridData.CENTER;
+		GridData gridData2 = new org.eclipse.swt.layout.GridData();
+		gridData2.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData2.verticalAlignment = org.eclipse.swt.layout.GridData.CENTER;
+		GridData gridData1 = new org.eclipse.swt.layout.GridData();
+		gridData1.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
+		gridData1.verticalAlignment = org.eclipse.swt.layout.GridData.CENTER;
+		GridLayout gridLayout = new GridLayout();
+		gridLayout.numColumns = 2;
+		shell = new Shell();
+		shell.setText("Shell");
+		createTable();
+		shell.setLayout(gridLayout);
+		shell.setSize(new org.eclipse.swt.graphics.Point(495, 357));
+		label1 = new Label(shell, SWT.NONE);
+		label1.setText("Name");
+		name = new Text(shell, SWT.BORDER);
+		name.setLayoutData(gridData1);
+		label2 = new Label(shell, SWT.NONE);
+		label2.setText("Address");
+		address = new Text(shell, SWT.BORDER);
+		address.setLayoutData(gridData2);
+		label3 = new Label(shell, SWT.NONE);
+		label3.setText("City");
+		city = new Text(shell, SWT.BORDER);
+		city.setLayoutData(gridData4);
+		label4 = new Label(shell, SWT.NONE);
+		label4.setText("State");
+		state = new Text(shell, SWT.BORDER);
+		state.setLayoutData(gridData3);
+		createTable1();
+		validationStatus = new Text(shell, SWT.READ_ONLY | SWT.BORDER);
+	}
+
+	private void run() {
+		final Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				createShell();
+				bind(shell);
+
+				shell.setSize(600, 600);
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	SimpleModel model = new SimpleModel();
+
+	private void bind(Control parent) {
+		Realm realm = SWTObservables.getRealm(parent.getDisplay());
+
+		TableViewer peopleViewer = new TableViewer(personsTable);
+		ViewerSupport.bind(peopleViewer, new WritableList(realm, model
+				.getPersonList(), SimpleModel.class), BeanProperties.values(
+				SimplePerson.class, new String[] { "name", "state" }));
+
+		IObservableValue selectedPerson = ViewersObservables
+				.observeSingleSelection(peopleViewer);
+
+		DataBindingContext dbc = new DataBindingContext(realm) {
+			protected UpdateValueStrategy createTargetToModelUpdateValueStrategy(
+					IObservableValue fromValue, IObservableValue toValue) {
+				return new CustomUpdateValueStrategy();
+			}
+		};
+		IConverter upperCaseConverter = new IConverter() {
+			public Object convert(Object fromObject) {
+				return ((String) fromObject).toUpperCase();
+			}
+
+			public Object getFromType() {
+				return String.class;
+			}
+
+			public Object getToType() {
+				return String.class;
+			}
+		};
+		IValidator vowelValidator = new IValidator() {
+			public IStatus validate(Object value) {
+				String s = (String) value;
+				if (!s.matches("[aeiouAEIOU]*")) {
+					return ValidationStatus.error("only vowels allowed");
+				}
+				return Status.OK_STATUS;
+			}
+		};
+		Binding b = dbc.bindValue(SWTObservables.observeText(name, SWT.Modify),
+				BeansObservables.observeDetailValue(selectedPerson, "name",
+						String.class), new CustomUpdateValueStrategy()
+						.setConverter(upperCaseConverter).setAfterGetValidator(
+								vowelValidator), null);
+
+		// AggregateValidationStatus status = new AggregateValidationStatus(dbc
+		// .getBindings(), AggregateValidationStatus.MAX_SEVERITY);
+		dbc.bindValue(SWTObservables.observeText(validationStatus, SWT.NONE), b
+				.getValidationStatus(), null, new UpdateValueStrategy()
+				.setConverter(new ObjectToStringConverter()));
+
+		dbc.bindValue(SWTObservables.observeText(address, SWT.Modify),
+				BeansObservables.observeDetailValue(selectedPerson, "address",
+						String.class));
+
+		dbc.bindValue(SWTObservables.observeText(city, SWT.Modify),
+				BeansObservables.observeDetailValue(selectedPerson, "city",
+						String.class));
+
+		dbc.bindValue(SWTObservables.observeText(state, SWT.Modify),
+				BeansObservables.observeDetailValue(selectedPerson, "state",
+						String.class));
+
+		TableViewer ordersViewer = new TableViewer(ordersTable);
+		ViewerSupport.bind(ordersViewer, BeansObservables.observeDetailList(
+				selectedPerson, "orders", SimpleOrder.class), BeanProperties
+				.values(SimpleOrder.class,
+						new String[] { "orderNumber", "date" }));
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/radioGroup/RadioGroup.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/radioGroup/RadioGroup.java
new file mode 100644
index 0000000..02e8f5d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/radioGroup/RadioGroup.java
@@ -0,0 +1,559 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.radioGroup;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.jface.examples.databinding.ducks.DuckType;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+
+/** 
+ * This object decorates a bunch of SWT.RADIO buttons and provides saner 
+ * selection semantics than you get by default with those radio buttons.
+ * <p>
+ * Its API is basically the same API as List, but with unnecessary methods
+ * removed.
+ */
+public class RadioGroup {
+   
+   private final IRadioButton[] buttons;
+   private final Object[] values;
+   IRadioButton oldSelection = null;
+   IRadioButton selectedButton = null;
+   IRadioButton potentialNewSelection = null;
+
+   /** (Non-API)
+    * Interface IRadioButton.  A duck interface that is used internally by RadioGroup
+    * and by RadioGroup's unit tests.
+    */
+   public static interface IRadioButton {
+      void setData(String string, Object object);
+      void addSelectionListener(SelectionListener selectionListener);
+      void setSelection(boolean b);
+      boolean getSelection();
+      boolean isFocusControl();
+      String getText();
+      void setText(String string);
+      void notifyListeners(int eventType, Event object);
+   }
+   
+   /**
+    * Constructs an instance of this widget given an array of Button objects to wrap.
+    * The Button objects must have been created with the SWT.RADIO style bit set,
+    * and they must all be in the same Composite.
+    * 
+    * @param radioButtons Object[] an array of radio buttons to wrap.
+    * @param values Object[] an array of objects corresponding to the value of each radio button.
+    */
+   public RadioGroup(Object[] radioButtons, Object[] values) {
+      IRadioButton[] buttons = new IRadioButton[radioButtons.length];
+      if (buttons.length < 1) {
+         throw new IllegalArgumentException("A RadioGroup must manage at least one Button");
+      }
+      for (int i = 0; i < buttons.length; i++) {
+         if (!DuckType.instanceOf(IRadioButton.class, radioButtons[i])) {
+            throw new IllegalArgumentException("A radio button was not passed");
+         }
+         buttons[i] = (IRadioButton) DuckType.implement(IRadioButton.class, radioButtons[i]);
+         buttons[i].setData(Integer.toString(i), new Integer(i));
+         buttons[i].addSelectionListener(selectionListener);
+      }
+      this.buttons = buttons;
+      this.values = values;
+   }
+   
+   /**
+    * Returns the object corresponding to the currently-selected radio button
+    * or null if no radio button is selected.
+    * 
+    * @return the object corresponding to the currently-selected radio button
+    * or null if no radio button is selected.
+    */
+   public Object getSelection() {
+      int selectionIndex = getSelectionIndex();
+      if (selectionIndex < 0)
+         return "";
+      return values[selectionIndex];
+   }
+   
+   /**
+    * Sets the selected radio button to the radio button whose model object
+    * equals() the object specified by newSelection.  If !newSelection.equals()
+    * any model object managed by this radio group, deselects all radio buttons.
+    * 
+    * @param newSelection A model object corresponding to one of the model
+    * objects associated with one of the radio buttons.
+    */
+   public void setSelection(Object newSelection) {
+      deselectAll();
+      for (int i = 0; i < values.length; i++) {
+         if (values[i].equals(newSelection)) {
+            setSelection(i);
+            return;
+         }
+      }
+   }
+   
+   private SelectionListener selectionListener = new SelectionListener() {
+      public void widgetDefaultSelected(SelectionEvent e) {
+         widgetSelected(e);
+      }
+      
+      public void widgetSelected(SelectionEvent e) {
+         potentialNewSelection = getButton(e);
+         if (! potentialNewSelection.getSelection()) {
+            return;
+         }
+         if (potentialNewSelection.equals(selectedButton)) {
+            return;
+         }
+         
+         if (fireWidgetChangeSelectionEvent(e)) {
+            oldSelection = selectedButton;
+            selectedButton = potentialNewSelection;
+            if (oldSelection == null) {
+               oldSelection = selectedButton;
+            }
+   
+            fireWidgetSelectedEvent(e);
+         }
+      }
+
+      private IRadioButton getButton(SelectionEvent e) {
+         // If the actual IRadioButton is a test fixture, then the test fixture can't
+         // set e.widget, so the button object will be in e.data instead and a dummy 
+         // Widget will be in e.widget.
+         if (e.data != null) {
+            return (IRadioButton) e.data;
+         }
+         return (IRadioButton) DuckType.implement(IRadioButton.class, e.widget);
+      }
+   };
+   
+   private List widgetChangeListeners = new LinkedList();
+   
+   protected boolean fireWidgetChangeSelectionEvent(SelectionEvent e) {
+      for (Iterator listenersIter = widgetChangeListeners.iterator(); listenersIter.hasNext();) {
+         VetoableSelectionListener listener = (VetoableSelectionListener) listenersIter.next();
+         listener.canWidgetChangeSelection(e);
+         if (!e.doit) {
+            rollbackSelection();
+            return false;
+         }
+      }
+      return true;
+   }
+
+   private void rollbackSelection() {
+      Display.getCurrent().asyncExec(new Runnable() {
+         public void run() {
+            potentialNewSelection.setSelection(false);
+            selectedButton.setSelection(true);
+//            selectedButton.notifyListeners(SWT.Selection, null);
+         }
+      });
+   }
+
+
+   /**
+    * Adds the listener to the collection of listeners who will
+    * be notified when the receiver's selection is about to change, by sending
+    * it one of the messages defined in the <code>VetoableSelectionListener</code>
+    * interface.
+    * <p>
+    * <code>widgetSelected</code> is called when the selection changes.
+    * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+    * </p>
+    *
+    * @param listener the listener which should be notified
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    *
+    * @see VetoableSelectionListener
+    * @see #removeVetoableSelectionListener
+    * @see SelectionEvent
+    */
+   public void addVetoableSelectionListener(VetoableSelectionListener listener) {
+      widgetChangeListeners.add(listener);
+   }
+
+   /**
+    * Removes the listener from the collection of listeners who will
+    * be notified when the receiver's selection is about to change.
+    *
+    * @param listener the listener which should no longer be notified
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    *
+    * @see VetoableSelectionListener
+    * @see #addVetoableSelectionListener
+    */
+   public void removeVetoableSelectionListener(VetoableSelectionListener listener) {
+      widgetChangeListeners.remove(listener);
+   }
+   
+
+   private List widgetSelectedListeners = new ArrayList();
+   
+   protected void fireWidgetSelectedEvent(SelectionEvent e) {
+      for (Iterator listenersIter = widgetSelectedListeners.iterator(); listenersIter.hasNext();) {
+         SelectionListener listener = (SelectionListener) listenersIter.next();
+         listener.widgetSelected(e);
+      }
+   }
+
+   protected void fireWidgetDefaultSelectedEvent(SelectionEvent e) {
+      fireWidgetSelectedEvent(e);
+   }
+   
+   /**
+    * Adds the listener to the collection of listeners who will
+    * be notified when the receiver's selection changes, by sending
+    * it one of the messages defined in the <code>SelectionListener</code>
+    * interface.
+    * <p>
+    * <code>widgetSelected</code> is called when the selection changes.
+    * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
+    * </p>
+    *
+    * @param listener the listener which should be notified
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    *
+    * @see SelectionListener
+    * @see #removeSelectionListener
+    * @see SelectionEvent
+    */
+   public void addSelectionListener(SelectionListener listener) {
+      widgetSelectedListeners.add(listener);
+   }
+
+   /**
+    * Removes the listener from the collection of listeners who will
+    * be notified when the receiver's selection changes.
+    *
+    * @param listener the listener which should no longer be notified
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    *
+    * @see SelectionListener
+    * @see #addSelectionListener
+    */
+   public void removeSelectionListener(SelectionListener listener) {
+      widgetSelectedListeners.remove(listener);
+   }
+   
+   /**
+    * Deselects the item at the given zero-relative index in the receiver.
+    * If the item at the index was already deselected, it remains
+    * deselected. Indices that are out of range are ignored.
+    *
+    * @param index the index of the item to deselect
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public void deselect (int index) {
+      if (index < 0 || index >= buttons.length)
+         return;
+      buttons[index].setSelection(false);
+   }
+   
+   /**
+    * Deselects all selected items in the receiver.
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public void deselectAll () {
+      for (int i = 0; i < buttons.length; i++)
+         buttons[i].setSelection(false);
+   }   
+
+   /**
+    * Returns the zero-relative index of the item which currently
+    * has the focus in the receiver, or -1 if no item has focus.
+    *
+    * @return the index of the selected item
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public int getFocusIndex () {
+      for (int i = 0; i < buttons.length; i++) {
+         if (buttons[i].isFocusControl()) {
+            return i;
+         }
+      }
+      return -1;
+   }
+
+   /**
+    * Returns the item at the given, zero-relative index in the
+    * receiver. Throws an exception if the index is out of range.
+    *
+    * @param index the index of the item to return
+    * @return the item at the given index
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    * 
+    * FIXME: tck - this should be renamed to getItemText()
+    */
+   public String getItem (int index) {
+      if (index < 0 || index >= buttons.length)
+         SWT.error(SWT.ERROR_INVALID_RANGE, null, "getItem for a nonexistant item");
+      return buttons[index].getText();
+   }
+
+   /**
+    * Returns the number of items contained in the receiver.
+    *
+    * @return the number of items
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public int getItemCount () {
+      return buttons.length;
+   }
+
+   /**
+    * Returns a (possibly empty) array of <code>String</code>s which
+    * are the items in the receiver. 
+    * <p>
+    * Note: This is not the actual structure used by the receiver
+    * to maintain its list of items, so modifying the array will
+    * not affect the receiver. 
+    * </p>
+    *
+    * @return the items in the receiver's list
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public String [] getItems () {
+      List itemStrings = new ArrayList();
+      for (int i = 0; i < buttons.length; i++) {
+         itemStrings.add(buttons[i].getText());
+      }
+      return (String[]) itemStrings.toArray(new String[itemStrings.size()]);
+   }
+   
+   public Object[] getButtons() {
+      return buttons;
+   }
+
+   /**
+    * Returns the zero-relative index of the item which is currently
+    * selected in the receiver, or -1 if no item is selected.
+    *
+    * @return the index of the selected item or -1
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public int getSelectionIndex () {
+      for (int i = 0; i < buttons.length; i++) {
+         if (buttons[i].getSelection() == true) {
+            return i;
+         }
+      }
+      return -1;
+   }
+
+  /**
+    * Gets the index of an item.
+    * <p>
+    * The list is searched starting at 0 until an
+    * item is found that is equal to the search item.
+    * If no item is found, -1 is returned.  Indexing
+    * is zero based.
+    *
+    * @param string the search item
+    * @return the index of the item
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public int indexOf (String string) {
+      for (int i = 0; i < buttons.length; i++) {
+         if (buttons[i].getText().equals(string)) {
+            return i;
+         }
+      }
+      return -1;
+   }
+
+   /**
+    * Searches the receiver's list starting at the given, 
+    * zero-relative index until an item is found that is equal
+    * to the argument, and returns the index of that item. If
+    * no item is found or the starting index is out of range,
+    * returns -1.
+    *
+    * @param string the search item
+    * @param start the zero-relative index at which to start the search
+    * @return the index of the item
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public int indexOf (String string, int start) {
+      for (int i = start; i < buttons.length; i++) {
+         if (buttons[i].getText().equals(string)) {
+            return i;
+         }
+      }
+      return -1;
+   }
+
+   /**
+    * Returns <code>true</code> if the item is selected,
+    * and <code>false</code> otherwise.  Indices out of
+    * range are ignored.
+    *
+    * @param index the index of the item
+    * @return the visibility state of the item at the index
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public boolean isSelected (int index) {
+      return buttons[index].getSelection();
+   }
+
+   /**
+    * Selects the item at the given zero-relative index in the receiver's 
+    * list.  If the item at the index was already selected, it remains
+    * selected. Indices that are out of range are ignored.
+    *
+    * @param index the index of the item to select
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public void select (int index) {
+      if (index < 0 || index >= buttons.length)
+         return;
+      buttons[index].setSelection(true);
+   }
+
+   /**
+    * Sets the text of the item in the receiver's list at the given
+    * zero-relative index to the string argument. This is equivalent
+    * to <code>remove</code>'ing the old item at the index, and then
+    * <code>add</code>'ing the new item at that index.
+    *
+    * @param index the index for the item
+    * @param string the new text for the item
+    *
+    * @exception IllegalArgumentException <ul>
+    *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
+    *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
+    * </ul>
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    */
+   public void setItem (int index, String string) {
+      if (index < 0 || index >= buttons.length)
+         SWT.error(SWT.ERROR_INVALID_RANGE, null, "setItem for a nonexistant item");
+      buttons[index].setText(string);
+   }
+
+   /**
+    * Selects the item at the given zero-relative index in the receiver. 
+    * If the item at the index was already selected, it remains selected.
+    * The current selection is first cleared, then the new item is selected.
+    * Indices that are out of range are ignored.
+    *
+    * @param index the index of the item to select
+    *
+    * @exception SWTException <ul>
+    *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
+    *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
+    * </ul>
+    * @see List#deselectAll()
+    * @see List#select(int)
+    */
+   public void setSelection (int index) {
+      if (index < 0 || index > buttons.length - 1) {
+         return;
+      }
+      buttons[index].setSelection(true);
+   }
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/radioGroup/VetoableSelectionListener.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/radioGroup/VetoableSelectionListener.java
new file mode 100644
index 0000000..86f5d7b
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/radioGroup/VetoableSelectionListener.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.radioGroup;
+
+import org.eclipse.swt.events.SelectionEvent;
+
+/**
+ * Interface VetoableSelectionListener.  An interface for SelectionListeners
+ * that permit the new selection to be vetoed before widgetSelected or
+ * widgetDefaultSelected is called.
+ */
+public interface VetoableSelectionListener {
+   /**
+    * Method widgetCanChangeSelection.  Indicates that the selection is
+    * about to be changed.  Setting e.doit to false will prevent the 
+    * selection from changing.
+    * 
+    * @param e The SelectionEvent that is being processed.
+    */
+   public void canWidgetChangeSelection(SelectionEvent e);
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet000HelloWorld.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet000HelloWorld.java
new file mode 100644
index 0000000..795547a
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet000HelloWorld.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *     Brad Reynolds - bug 116920
+ *     Benjamin Cabe - bug 252219
+ *     Matthew Hall - bug 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Hello, databinding. Bind changes in a GUI to a Model object but don't worry
+ * about propogating changes from the Model to the GUI.
+ * <p>
+ * Illustrates the basic Model-ViewModel-Binding-View architecture typically
+ * used in data binding applications.
+ */
+public class Snippet000HelloWorld {
+	public static void main(String[] args) {
+		Display display = new Display();
+		final ViewModel viewModel = new ViewModel();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display),
+				new Runnable() {
+					public void run() {
+						final Shell shell = new View(viewModel).createShell();
+						// The SWT event loop
+						Display display = Display.getCurrent();
+						while (!shell.isDisposed()) {
+							if (!display.readAndDispatch()) {
+								display.sleep();
+							}
+						}
+					}
+				});
+		// Print the results
+		System.out.println("person.getName() = "
+				+ viewModel.getPerson().getName());
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	// 
+	// In this example, we only push changes from the GUI to the model, so we
+	// don't worry about implementing JavaBeans bound properties. If we need
+	// our GUI to automatically reflect changes in the Person object, the
+	// Person object would need to implement the JavaBeans property change
+	// listener methods.
+	static class Person {
+		// A property...
+		String name = "HelloWorld";
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// DAO. Since this snippet doesn't have any persistent objects to
+	// retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private Person person = new Person();
+
+		public Person getPerson() {
+			return person;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Text name;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			shell.setLayout(new RowLayout(SWT.VERTICAL));
+			name = new Text(shell, SWT.BORDER);
+
+			// Bind it
+			DataBindingContext bindingContext = new DataBindingContext();
+			Person person = viewModel.getPerson();
+
+			bindingContext.bindValue(SWTObservables.observeText(name,
+					SWT.Modify), PojoObservables.observeValue(person, "name"));
+
+			// Open and return the Shell
+			shell.pack();
+			shell.open();
+			return shell;
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet001NestedSelectionWithCombo.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet001NestedSelectionWithCombo.java
new file mode 100644
index 0000000..1441d2d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet001NestedSelectionWithCombo.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *     Brad Reynolds - bug 116920
+ *     Matthew Hall - bug 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Demonstrates nested selection.<br>
+ * At the first level, user may select a person.<br>
+ * At the second level, user may select a city to associate with the selected<br>
+ * person or edit the person's name.
+ */
+public class Snippet001NestedSelectionWithCombo {
+	public static void main(String[] args) {
+		ViewModel viewModel = new ViewModel();
+		Shell shell = new View(viewModel).createShell();
+
+		// The SWT event loop
+		Display display = Display.getCurrent();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch()) {
+				display.sleep();
+			}
+		}
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	// 
+	// This example implements full JavaBeans bound properties so that changes
+	// to instances of this class will automatically be propogated to the UI.
+	public static class Person extends AbstractModelObject {
+		// Constructor
+		public Person(String name, String city) {
+			this.name = name;
+			this.city = city;
+		}
+
+		// Some JavaBean bound properties...
+		String name;
+
+		String city;
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			String oldValue = this.name;
+			this.name = name;
+			firePropertyChange("name", oldValue, name);
+		}
+
+		public String getCity() {
+			return city;
+		}
+
+		public void setCity(String city) {
+			String oldValue = this.city;
+			this.city = city;
+			firePropertyChange("city", oldValue, city);
+		}
+	}
+
+	// The View's model--the root of our GUI's Model graph
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// DAO. Since this snippet doesn't have any persistent objects to
+	// retrieve, this ViewModel just instantiates some objects to edit.
+	// 
+	// This ViewModel also implements JavaBean bound properties.
+	static class ViewModel extends AbstractModelObject {
+		// The model to bind
+		private ArrayList people = new ArrayList();
+		{
+			people.add(new Person("Wile E. Coyote", "Tucson"));
+			people.add(new Person("Road Runner", "Lost Horse"));
+			people.add(new Person("Bugs Bunny", "Forrest"));
+		}
+
+		// Choice of cities for the Combo
+		private ArrayList cities = new ArrayList();
+		{
+			cities.add("Tucson");
+			cities.add("AcmeTown");
+			cities.add("Lost Horse");
+			cities.add("Forrest");
+			cities.add("Lost Mine");
+		}
+
+		public ArrayList getPeople() {
+			return people;
+		}
+
+		public ArrayList getCities() {
+			return cities;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Shell shell = new Shell(Display.getCurrent());
+			Realm realm = SWTObservables.getRealm(shell.getDisplay());
+
+			List peopleList = new List(shell, SWT.BORDER);
+			Text name = new Text(shell, SWT.BORDER);
+			Combo city = new Combo(shell, SWT.BORDER | SWT.READ_ONLY);
+
+			ListViewer peopleListViewer = new ListViewer(peopleList);
+			IObservableMap attributeMap = BeansObservables.observeMap(
+					Observables.staticObservableSet(realm, new HashSet(
+							viewModel.getPeople())), Person.class, "name");
+			peopleListViewer.setLabelProvider(new ObservableMapLabelProvider(
+					attributeMap));
+			peopleListViewer.setContentProvider(new ArrayContentProvider());
+			peopleListViewer.setInput(viewModel.getPeople());
+
+			DataBindingContext dbc = new DataBindingContext(realm);
+			IObservableValue selectedPerson = ViewersObservables
+					.observeSingleSelection(peopleListViewer);
+			dbc.bindValue(SWTObservables.observeText(name, SWT.Modify),
+					BeansObservables.observeDetailValue(selectedPerson,
+							"name", String.class));
+
+			ComboViewer cityViewer = new ComboViewer(city);
+			cityViewer.setContentProvider(new ArrayContentProvider());
+			cityViewer.setInput(viewModel.getCities());
+
+			IObservableValue citySelection = ViewersObservables
+					.observeSingleSelection(cityViewer);
+			dbc.bindValue(citySelection, BeansObservables.observeDetailValue(
+					selectedPerson, "city", String.class));
+
+			GridLayoutFactory.swtDefaults().applyTo(shell);
+			// Open and return the Shell
+			shell.pack();
+			shell.open();
+			return shell;
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet002UpdateComboRetainSelection.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet002UpdateComboRetainSelection.java
new file mode 100644
index 0000000..5ad9c06
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet002UpdateComboRetainSelection.java
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *     Brad Reynolds - bug 116920
+ *     Matthew Hall - bug 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+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.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Shows how to bind a Combo so that when update its items, the selection is
+ * retained if at all possible.
+ * 
+ * @since 3.2
+ */
+public class Snippet002UpdateComboRetainSelection {
+    public static void main(String[] args) {
+    	final Display display = new Display();
+    	Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+    		public void run() {
+    			ViewModel viewModel = new ViewModel();
+    			Shell shell = new View(viewModel).createShell();
+    			
+    			// The SWT event loop
+    			while (!shell.isDisposed()) {
+    				if (!display.readAndDispatch()) {
+    					display.sleep();
+    				}
+    			}
+    			
+    			// Print the results
+    			System.out.println(viewModel.getText());
+    		}
+    	});
+    	display.dispose();
+    }
+
+    // Minimal JavaBeans support
+    public static abstract class AbstractModelObject {
+        private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
+
+        public void addPropertyChangeListener(PropertyChangeListener listener) {
+            propertyChangeSupport.addPropertyChangeListener(listener);
+        }
+
+        public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+            propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
+        }
+
+        public void removePropertyChangeListener(PropertyChangeListener listener) {
+            propertyChangeSupport.removePropertyChangeListener(listener);
+        }
+
+        public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+            propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
+        }
+
+        protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+            propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
+        }
+    }
+
+    // The View's model--the root of our Model graph for this particular GUI.
+    public static class ViewModel extends AbstractModelObject {
+        private String text = "beef";
+
+        private List choices = new ArrayList();
+        {
+            choices.add("pork");
+            choices.add("beef");
+            choices.add("poultry");
+            choices.add("vegatables");
+        }
+
+        public List getChoices() {
+            return choices;
+        }
+
+        public void setChoices(List choices) {
+            List old = this.choices;
+            this.choices = choices;
+            firePropertyChange("choices", old, choices);
+        }
+
+        public String getText() {
+            return text;
+        }
+
+        public void setText(String text) {
+            String oldValue = this.text;
+            this.text = text;
+            firePropertyChange("text", oldValue, text);
+        }
+    }
+
+    // The GUI view
+    static class View {
+        private ViewModel viewModel;
+        /**
+         * used to make a new choices array unique
+         */
+        static int count;
+
+        public View(ViewModel viewModel) {
+            this.viewModel = viewModel;
+        }
+
+        public Shell createShell() {
+            // Build a UI
+            Shell shell = new Shell(Display.getCurrent());
+            shell.setLayout(new RowLayout(SWT.VERTICAL));
+
+            Combo combo = new Combo(shell, SWT.BORDER | SWT.READ_ONLY);
+            Button reset = new Button(shell, SWT.NULL);
+            reset.setText("reset collection");
+            reset.addSelectionListener(new SelectionAdapter() {
+                public void widgetSelected(SelectionEvent e) {
+                    List newList = new ArrayList();
+                    newList.add("Chocolate");
+                    newList.add("Vanilla");
+                    newList.add("Mango Parfait");
+                    newList.add("beef");
+                    newList.add("Cheesecake");
+                    newList.add(Integer.toString(++count));
+                    viewModel.setChoices(newList);
+                }
+            });
+
+            // Print value out first
+            System.out.println(viewModel.getText());
+
+            DataBindingContext dbc = new DataBindingContext();
+            
+            IObservableList list = MasterDetailObservables.detailList(BeansObservables.observeValue(viewModel, "choices"),
+                    getListDetailFactory(),
+                    String.class);
+            dbc.bindList(SWTObservables.observeItems(combo), list); 
+            dbc.bindValue(SWTObservables.observeText(combo), BeansObservables.observeValue(viewModel, "text"));
+            
+            // Open and return the Shell
+            shell.pack();
+            shell.open();
+            return shell;
+        }
+    }
+
+    private static IObservableFactory getListDetailFactory() {
+        return new IObservableFactory() {
+            public IObservable createObservable(Object target) {
+                WritableList list = WritableList.withElementType(String.class);
+                list.addAll((Collection) target);
+                return list;
+            }
+        };
+    }
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet003UpdateComboBindUsingViewer.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet003UpdateComboBindUsingViewer.java
new file mode 100644
index 0000000..0e1eeb0
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet003UpdateComboBindUsingViewer.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *     Brad Reynolds - bug 116920
+ *     Matthew Hall - bugs 260329, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.property.Properties;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Shows how to bind a Combo so that when update its items, the selection is
+ * retained if at all possible.
+ * 
+ * @since 3.2
+ */
+public class Snippet003UpdateComboBindUsingViewer {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				ViewModel viewModel = new ViewModel();
+				Shell shell = new View(viewModel).createShell();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+				// Print the results
+				System.out.println(viewModel.getText());
+			}
+		});
+		display.dispose();
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	public static class ViewModel extends AbstractModelObject {
+		private String text = "beef";
+
+		private List choices = new ArrayList();
+		{
+			choices.add("pork");
+			choices.add("beef");
+			choices.add("poultry");
+			choices.add("vegatables");
+		}
+
+		public List getChoices() {
+			return choices;
+		}
+
+		public void setChoices(List choices) {
+			this.choices = choices;
+			firePropertyChange("choices", null, null);
+		}
+
+		public String getText() {
+			return text;
+		}
+
+		public void setText(String text) {
+			String oldValue = this.text;
+			this.text = text;
+			firePropertyChange("text", oldValue, text);
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Shell shell = new Shell(Display.getCurrent());
+			shell.setLayout(new RowLayout(SWT.VERTICAL));
+
+			Combo combo = new Combo(shell, SWT.BORDER | SWT.READ_ONLY);
+			ComboViewer viewer = new ComboViewer(combo);
+			Button reset = new Button(shell, SWT.NULL);
+			reset.setText("reset collection");
+			reset.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+					List newList = new ArrayList();
+					newList.add("Chocolate");
+					newList.add("Vanilla");
+					newList.add("Mango Parfait");
+					newList.add("beef");
+					newList.add("Cheesecake");
+					viewModel.setChoices(newList);
+				}
+			});
+
+			// Print value out first
+			System.out.println(viewModel.getText());
+
+			DataBindingContext dbc = new DataBindingContext();
+			ViewerSupport.bind(viewer, BeansObservables.observeList(viewModel,
+					"choices", String.class), Properties
+					.selfValue(String.class));
+
+			dbc.bindValue(ViewersObservables.observeSingleSelection(viewer),
+					BeansObservables.observeValue(viewModel, "text"));
+
+			// Open and return the Shell
+			shell.pack();
+			shell.open();
+			return shell;
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet004DataBindingContextErrorLabel.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet004DataBindingContextErrorLabel.java
new file mode 100644
index 0000000..ef8c3f9
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet004DataBindingContextErrorLabel.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 116920, 159768
+ *     Matthew Hall - bug 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.AggregateValidationStatus;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Snippet that displays how to bind the validation error of the
+ * {@link DataBindingContext} to a label. http://www.eclipse.org
+ * 
+ * @since 3.2
+ */
+public class Snippet004DataBindingContextErrorLabel {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+				shell.setText("Data Binding Snippet 004");
+				shell.setLayout(new GridLayout(2, false));
+
+				new Label(shell, SWT.NONE).setText("Enter '5' to be valid:");
+
+				Text text = new Text(shell, SWT.BORDER);
+				WritableValue value = WritableValue.withValueType(String.class);
+				new Label(shell, SWT.NONE).setText("Error:");
+
+				Label errorLabel = new Label(shell, SWT.BORDER);
+				errorLabel.setForeground(display.getSystemColor(SWT.COLOR_RED));
+				GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT).applyTo(
+						errorLabel);
+
+				DataBindingContext dbc = new DataBindingContext();
+
+				// Bind the text to the value.
+				dbc.bindValue(
+						SWTObservables.observeText(text, SWT.Modify),
+						value,
+						new UpdateValueStrategy().setAfterConvertValidator(new FiveValidator()),
+						null);
+
+				// Bind the error label to the validation error on the dbc.
+				dbc.bindValue(SWTObservables.observeText(errorLabel),
+						new AggregateValidationStatus(dbc.getBindings(),
+								AggregateValidationStatus.MAX_SEVERITY));
+
+				shell.pack();
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	/**
+	 * Validator that returns validation errors for any value other than 5.
+	 * 
+	 * @since 3.2
+	 */
+	private static class FiveValidator implements IValidator {
+		public IStatus validate(Object value) {
+			return ("5".equals(value)) ? Status.OK_STATUS : ValidationStatus
+					.error("the value was '" + value + "', not '5'");
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet005MenuUpdater.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet005MenuUpdater.java
new file mode 100644
index 0000000..f84b40c
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet005MenuUpdater.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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 116920
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.provisional.swt.MenuUpdater;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ */
+public class Snippet005MenuUpdater {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+
+				final WritableList menuItemStrings = new WritableList();
+				display.asyncExec(new Runnable() {
+					public void run() {
+						System.out.println("adding item");
+						menuItemStrings.add(new Date().toString());
+						display.timerExec(5000, this);
+					}
+				});
+
+				Menu bar = new Menu(shell, SWT.BAR);
+				shell.setMenuBar(bar);
+				MenuItem fileItem = new MenuItem(bar, SWT.CASCADE);
+				fileItem.setText("&Test Menu");
+				final Menu submenu = new Menu(shell, SWT.DROP_DOWN);
+				fileItem.setMenu(submenu);
+				new MenuUpdater(submenu) {
+					protected void updateMenu() {
+						System.out.println("updating menu");
+						MenuItem[] items = submenu.getItems();
+						int itemIndex = 0;
+						for (Iterator it = menuItemStrings.iterator(); it
+								.hasNext();) {
+							MenuItem item;
+							if (itemIndex < items.length) {
+								item = items[itemIndex++];
+							} else {
+								item = new MenuItem(submenu, SWT.NONE);
+							}
+							String string = (String) it.next();
+							item.setText(string);
+						}
+						while (itemIndex < items.length) {
+							items[itemIndex++].dispose();
+						}
+					}
+				};
+
+				shell.open();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+		display.dispose();
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet006Spreadsheet.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet006Spreadsheet.java
new file mode 100644
index 0000000..214b4f1
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet006Spreadsheet.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * 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 116920
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.provisional.swt.TableUpdater;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ControlEditor;
+import org.eclipse.swt.custom.TableCursor;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 1.1
+ * 
+ */
+public class Snippet006Spreadsheet {
+
+	private static final int COUNTER_UPDATE_DELAY = 1000;
+
+	private static final int NUM_COLUMNS = 6;
+
+	private static final int NUM_ROWS = 16;
+
+	/**
+	 * 0 for no output, 1 for some, 2 for more
+	 */
+	private static int DEBUG_LEVEL = 0;
+
+	/**
+	 * If true, there will be a automatic counter at B1.
+	 */
+	private static boolean FUNKY_COUNTER = false;
+
+	/**
+	 * // * If true, all formulas (except for row 1 and column A) will be the
+	 * sum of the values of their left and top neighbouring cells.
+	 */
+	private static boolean FUNKY_FORMULAS = true;
+
+	static WritableValue[][] cellFormulas = new WritableValue[NUM_ROWS][NUM_COLUMNS];
+
+	static ComputedValue[][] cellValues = new ComputedValue[NUM_ROWS][NUM_COLUMNS];
+
+	static class ComputedCellValue extends ComputedValue {
+		private final IObservableValue cellFormula;
+
+		private boolean calculating;
+
+		ComputedCellValue(IObservableValue cellFormula) {
+			this.cellFormula = cellFormula;
+		}
+
+		protected Object calculate() {
+			if (calculating) {
+				return "#cycle";
+			}
+			try {
+				calculating = true;
+				return evaluate(cellFormula.getValue());
+			} finally {
+				calculating = false;
+			}
+		}
+
+		private Object evaluate(Object value) {
+			if (DEBUG_LEVEL >= 2) {
+				System.out.println("evaluating " + this + " ...");
+			}
+			if (value == null) {
+				return "";
+			}
+			try {
+				String s = (String) value;
+				if (!s.startsWith("=")) {
+					return s;
+				}
+				String addition = s.substring(1);
+				int indexOfPlus = addition.indexOf('+');
+				String operand1 = addition.substring(0, indexOfPlus);
+				double value1 = eval(operand1);
+				String operand2 = addition.substring(indexOfPlus + 1);
+				double value2 = eval(operand2);
+				return NumberFormat.getNumberInstance().format(value1 + value2);
+			} catch (Exception ex) {
+				return ex.getMessage();
+			}
+		}
+
+		/**
+		 * @param s
+		 * @return
+		 * @throws ParseException
+		 */
+		private double eval(String s) throws ParseException {
+			if (s.length() == 0) {
+				return 0;
+			}
+			char character = s.charAt(0);
+			if (Character.isLetter(character)) {
+				character = Character.toLowerCase(character);
+				// reference to other cell
+				int columnIndex = character - 'a';
+				int rowIndex = 0;
+				rowIndex = NumberFormat.getNumberInstance().parse(
+						s.substring(1)).intValue() - 1;
+				String value = (String) cellValues[rowIndex][columnIndex]
+						.getValue();
+				return value.length() == 0 ? 0 : NumberFormat
+						.getNumberInstance().parse(value).doubleValue();
+			}
+			return NumberFormat.getNumberInstance().parse(s).doubleValue();
+		}
+	}
+
+	protected static int counter;
+
+	public static void main(String[] args) {
+
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+				shell.setText("Data Binding Snippet 006");
+
+				final Table table = new Table(shell, SWT.BORDER | SWT.MULTI
+						| SWT.FULL_SELECTION | SWT.VIRTUAL);
+				table.setLinesVisible(true);
+				table.setHeaderVisible(true);
+
+				for (int i = 0; i < NUM_COLUMNS; i++) {
+					TableColumn tableColumn = new TableColumn(table, SWT.NONE);
+					tableColumn.setText(Character.toString((char) ('A' + i)));
+					tableColumn.setWidth(60);
+				}
+				WritableList list = new WritableList();
+				for (int i = 0; i < NUM_ROWS; i++) {
+					list.add(new Object());
+					for (int j = 0; j < NUM_COLUMNS; j++) {
+						cellFormulas[i][j] = new WritableValue();
+						cellValues[i][j] = new ComputedCellValue(
+								cellFormulas[i][j]);
+						if (!FUNKY_FORMULAS || i == 0 || j == 0) {
+							cellFormulas[i][j].setValue("");
+						} else {
+							cellFormulas[i][j].setValue("="
+									+ cellReference(i - 1, j) + "+"
+									+ cellReference(i, j - 1));
+						}
+					}
+				}
+
+				new TableUpdater(table, list) {
+					protected void updateItem(int rowIndex, TableItem item, Object element) {
+						if (DEBUG_LEVEL >= 1) {
+							System.out.println("updating row " + rowIndex);
+						}
+						for (int j = 0; j < NUM_COLUMNS; j++) {
+							item.setText(j, (String) cellValues[rowIndex][j]
+									.getValue());
+						}
+					}
+				};
+
+				if (FUNKY_COUNTER) {
+					// counter in A1
+					display.asyncExec(new Runnable() {
+						public void run() {
+							cellFormulas[0][1].setValue("" + counter++);
+							display.timerExec(COUNTER_UPDATE_DELAY, this);
+						}
+					});
+				}
+
+				// create a TableCursor to navigate around the table
+				final TableCursor cursor = new TableCursor(table, SWT.NONE);
+				// create an editor to edit the cell when the user hits "ENTER"
+				// while over a cell in the table
+				final ControlEditor editor = new ControlEditor(cursor);
+				editor.grabHorizontal = true;
+				editor.grabVertical = true;
+
+				cursor.addSelectionListener(new SelectionAdapter() {
+					// when the TableEditor is over a cell, select the
+					// corresponding row
+					// in
+					// the table
+					public void widgetSelected(SelectionEvent e) {
+						table.setSelection(new TableItem[] { cursor.getRow() });
+					}
+
+					// when the user hits "ENTER" in the TableCursor, pop up a
+					// text
+					// editor so that
+					// they can change the text of the cell
+					public void widgetDefaultSelected(SelectionEvent e) {
+						final Text text = new Text(cursor, SWT.NONE);
+						TableItem row = cursor.getRow();
+						int rowIndex = table.indexOf(row);
+						int columnIndex = cursor.getColumn();
+						text
+								.setText((String) cellFormulas[rowIndex][columnIndex]
+										.getValue());
+						text.addKeyListener(new KeyAdapter() {
+							public void keyPressed(KeyEvent e) {
+								// close the text editor and copy the data over
+								// when the user hits "ENTER"
+								if (e.character == SWT.CR) {
+									TableItem row = cursor.getRow();
+									int rowIndex = table.indexOf(row);
+									int columnIndex = cursor.getColumn();
+									cellFormulas[rowIndex][columnIndex]
+											.setValue(text.getText());
+									text.dispose();
+								}
+								// close the text editor when the user hits
+								// "ESC"
+								if (e.character == SWT.ESC) {
+									text.dispose();
+								}
+							}
+						});
+						editor.setEditor(text);
+						text.setFocus();
+					}
+				});
+				// Hide the TableCursor when the user hits the "MOD1" or "MOD2"
+				// key.
+				// This alows the user to select multiple items in the table.
+				cursor.addKeyListener(new KeyAdapter() {
+					public void keyPressed(KeyEvent e) {
+						if (e.keyCode == SWT.MOD1 || e.keyCode == SWT.MOD2
+								|| (e.stateMask & SWT.MOD1) != 0
+								|| (e.stateMask & SWT.MOD2) != 0) {
+							cursor.setVisible(false);
+						}
+					}
+				});
+				// Show the TableCursor when the user releases the "MOD2" or
+				// "MOD1" key.
+				// This signals the end of the multiple selection task.
+				table.addKeyListener(new KeyAdapter() {
+					public void keyReleased(KeyEvent e) {
+						if (e.keyCode == SWT.MOD1
+								&& (e.stateMask & SWT.MOD2) != 0)
+							return;
+						if (e.keyCode == SWT.MOD2
+								&& (e.stateMask & SWT.MOD1) != 0)
+							return;
+						if (e.keyCode != SWT.MOD1
+								&& (e.stateMask & SWT.MOD1) != 0)
+							return;
+						if (e.keyCode != SWT.MOD2
+								&& (e.stateMask & SWT.MOD2) != 0)
+							return;
+
+						TableItem[] selection = table.getSelection();
+						TableItem row = (selection.length == 0) ? table
+								.getItem(table.getTopIndex()) : selection[0];
+						table.showItem(row);
+						cursor.setSelection(row, 0);
+						cursor.setVisible(true);
+						cursor.setFocus();
+					}
+				});
+
+				GridLayoutFactory.fillDefaults().generateLayout(shell);
+				shell.setSize(400, 300);
+				shell.open();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	private static String cellReference(int rowIndex, int columnIndex) {
+		String cellReference = "" + ((char) ('A' + columnIndex))
+				+ (rowIndex + 1);
+		return cellReference;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet007ColorLabelProvider.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet007ColorLabelProvider.java
new file mode 100644
index 0000000..d9a5b58
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet007ColorLabelProvider.java
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     IBM Corporation - see bug 137934
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+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.map.IObservableMap;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ITableColorProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * An example showing how to create a {@link ILabelProvider label provider} that
+ * to provide colors.
+ * 
+ * @since 3.2
+ */
+public class Snippet007ColorLabelProvider {
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		final List persons = new ArrayList();
+		persons.add(new Person("Fiona Apple", Person.FEMALE));
+		persons.add(new Person("Elliot Smith", Person.MALE));
+		persons.add(new Person("Diana Krall", Person.FEMALE));
+		persons.add(new Person("David Gilmour", Person.MALE));
+
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+				shell.setText("Gender Bender");
+				shell.setLayout(new GridLayout());
+
+				Table table = new Table(shell, SWT.SINGLE | SWT.H_SCROLL
+						| SWT.V_SCROLL | SWT.BORDER);
+				GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+				table.setLayoutData(gridData);
+				table.setHeaderVisible(true);
+				table.setLinesVisible(true);
+				TableColumn column = new TableColumn(table, SWT.NONE);
+				column.setText("No");
+				column.setWidth(20);
+				column = new TableColumn(table, SWT.NONE);
+				column.setText("Name");
+				column.setWidth(100);
+				final TableViewer viewer = new TableViewer(table);
+
+				IObservableList observableList = Observables
+						.staticObservableList(persons);
+				ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+
+				viewer.setContentProvider(contentProvider);
+
+				// this does not have to correspond to the columns in the table,
+				// we just list all attributes that affect the table content.
+				IObservableMap[] attributes = BeansObservables.observeMaps(
+						contentProvider.getKnownElements(), Person.class,
+						new String[] { "name", "gender" });
+
+				class ColorLabelProvider extends ObservableMapLabelProvider
+						implements ITableColorProvider {
+					Color male = display.getSystemColor(SWT.COLOR_BLUE);
+
+					Color female = new Color(display, 255, 192, 203);
+
+					ColorLabelProvider(IObservableMap[] attributes) {
+						super(attributes);
+					}
+
+					// to drive home the point that attributes does not have to
+					// match
+					// the columns
+					// in the table, we change the column text as follows:
+					public String getColumnText(Object element, int index) {
+						if (index == 0) {
+							return Integer
+									.toString(persons.indexOf(element) + 1);
+						}
+						return ((Person) element).getName();
+					}
+
+					public Color getBackground(Object element, int index) {
+						return null;
+					}
+
+					public Color getForeground(Object element, int index) {
+						if (index == 0)
+							return null;
+						Person person = (Person) element;
+						return (person.getGender() == Person.MALE) ? male
+								: female;
+					}
+
+					public void dispose() {
+						super.dispose();
+						female.dispose();
+					}
+				}
+				viewer.setLabelProvider(new ColorLabelProvider(attributes));
+
+				viewer.setInput(observableList);
+
+				table.getColumn(0).pack();
+
+				Button button = new Button(shell, SWT.PUSH);
+				button.setText("Toggle Gender");
+				button.addSelectionListener(new SelectionAdapter() {
+					public void widgetSelected(SelectionEvent arg0) {
+						StructuredSelection selection = (StructuredSelection) viewer
+								.getSelection();
+						if (selection != null && !selection.isEmpty()) {
+							Person person = (Person) selection
+									.getFirstElement();
+							person
+									.setGender((person.getGender() == Person.MALE) ? Person.FEMALE
+											: Person.MALE);
+						}
+					}
+				});
+
+				shell.setSize(300, 400);
+				shell.open();
+
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	static class Person {
+		static final int MALE = 0;
+
+		static final int FEMALE = 1;
+
+		private String name;
+
+		private int gender;
+
+		private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+				this);
+
+		Person(String name, int gender) {
+			this.name = name;
+			this.gender = gender;
+		}
+
+		/**
+		 * Returns the name. Method declared public to satisfy Java bean
+		 * conventions
+		 * 
+		 * @return the name
+		 */
+		public String getName() {
+			return name;
+		}
+
+		/**
+		 * @param listener
+		 */
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.removePropertyChangeListener(listener);
+		}
+
+		/**
+		 * Returns the gender. Method declared public to satisfy Java bean
+		 * conventions
+		 * 
+		 * @return the gender
+		 */
+		public int getGender() {
+			return gender;
+		}
+
+		void setGender(int gender) {
+			changeSupport.firePropertyChange("gender", this.gender,
+					this.gender = gender);
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet008ComputedValue.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet008ComputedValue.java
new file mode 100644
index 0000000..92b146d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet008ComputedValue.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+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.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Snippet that demostrates a simple use case using ComputedValue to format a
+ * name as the user enters first and last name.
+ * 
+ * @since 3.2
+ */
+public class Snippet008ComputedValue {
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+				shell.setLayout(new FillLayout());
+
+				final UI ui = new UI(shell);
+				final Data data = new Data();
+
+				// Bind the UI to the Data.
+				DataBindingContext dbc = new DataBindingContext();
+				dbc.bindValue(SWTObservables.observeText(ui.firstName,
+						SWT.Modify), data.firstName);
+				dbc.bindValue(SWTObservables.observeText(ui.lastName,
+						SWT.Modify), data.lastName);
+
+				// Construct the formatted name observable.
+				FormattedName formattedName = new FormattedName(data.firstName,
+						data.lastName);
+
+				// Bind the formatted name Text to the formatted name
+				// observable.
+				dbc.bindValue(SWTObservables.observeText(ui.formattedName,
+						SWT.None), formattedName, new UpdateValueStrategy(false, UpdateValueStrategy.POLICY_NEVER), null);
+
+				shell.pack();
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	/**
+	 * Creates the formatted name on change of the first or last name
+	 * observables.
+	 * <p>
+	 * The key to understanding ComputedValue is understanding that it knows of
+	 * the observables that are queried without being told. This is done with
+	 * {@link ObservableTracker} voodoo. When calculate() is invoked
+	 * <code>ObservableTracker</code> records the observables that are
+	 * queried. It then exposes those observables and <code>ComputedValue</code>
+	 * can listen to changes in those objects and react accordingly.
+	 * </p>
+	 * 
+	 * @since 3.2
+	 */
+	static class FormattedName extends ComputedValue {
+		private IObservableValue firstName;
+
+		private IObservableValue lastName;
+
+		FormattedName(IObservableValue firstName, IObservableValue lastName) {
+			this.firstName = firstName;
+			this.lastName = lastName;
+		}
+
+		protected Object calculate() {
+			String lastName = (String) this.lastName.getValue();
+			String firstName = (String) this.firstName.getValue();
+			lastName = (lastName != null && lastName.length() > 0) ? lastName
+					: "[Last Name]";
+			firstName = (firstName != null && firstName.length() > 0) ? firstName
+					: "[First Name]";
+
+			StringBuffer buffer = new StringBuffer();
+			buffer.append(lastName).append(", ").append(firstName);
+
+			return buffer.toString();
+		}
+	}
+
+	static class Data {
+		final WritableValue firstName;
+
+		final WritableValue lastName;
+
+		Data() {
+			firstName = new WritableValue("", String.class);
+			lastName = new WritableValue("", String.class);
+		}
+	}
+
+	/**
+	 * Composite that creates the UI.
+	 * 
+	 * @since 3.2
+	 */
+	static class UI extends Composite {
+		final Text firstName;
+
+		final Text lastName;
+
+		final Text formattedName;
+
+		UI(Composite parent) {
+			super(parent, SWT.NONE);
+
+			GridLayoutFactory.swtDefaults().numColumns(2).applyTo(this);
+
+			new Label(this, SWT.NONE).setText("First Name:");
+			new Label(this, SWT.NONE).setText("Last Name");
+
+			GridDataFactory gdf = GridDataFactory.swtDefaults().align(SWT.FILL,
+					SWT.FILL).grab(true, false);
+			firstName = new Text(this, SWT.BORDER);
+			gdf.applyTo(firstName);
+
+			lastName = new Text(this, SWT.BORDER);
+			gdf.applyTo(lastName);
+
+			gdf = GridDataFactory.swtDefaults().span(2, 1).grab(true, false)
+					.align(SWT.FILL, SWT.BEGINNING);
+			Label label = new Label(this, SWT.NONE);
+			label.setText("Formatted Name:");
+			gdf.applyTo(label);
+
+			formattedName = new Text(this, SWT.BORDER);
+			formattedName.setEditable(false);
+			gdf.applyTo(formattedName);
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet009TableViewer.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet009TableViewer.java
new file mode 100644
index 0000000..4dff54d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet009TableViewer.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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:
+ *     Coconut Palm Software, Inc. - Initial API and implementation
+ *     Matthew Hall - bug 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * Demonstrates binding a TableViewer to a collection.
+ */
+public class Snippet009TableViewer {
+	public static void main(String[] args) {
+		final Display display = Display.getDefault();
+
+		// In an RCP application, the threading Realm will be set for you
+		// automatically by the Workbench. In an SWT application, you can do
+		// this once, wrpping your binding method call.
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+
+				ViewModel viewModel = new ViewModel();
+				Shell shell = new View(viewModel).createShell();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	static class Person extends AbstractModelObject {
+		// A property...
+		String name = "John Smith";
+
+		public Person(String name) {
+			this.name = name;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			String oldValue = this.name;
+			this.name = name;
+			firePropertyChange("name", oldValue, name);
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// ro retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private List people = new LinkedList();
+		{
+			people.add(new Person("Steve Northover"));
+			people.add(new Person("Grant Gayed"));
+			people.add(new Person("Veronika Irvine"));
+			people.add(new Person("Mike Wilson"));
+			people.add(new Person("Christophe Cornu"));
+			people.add(new Person("Lynne Kues"));
+			people.add(new Person("Silenio Quarti"));
+		}
+
+		public List getPeople() {
+			return people;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Table committers;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			shell.setLayout(new FillLayout());
+			committers = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
+			committers.setLinesVisible(true);
+			TableColumn column = new TableColumn(committers, SWT.NONE);
+
+			// Set up data binding.
+			TableViewer peopleViewer = new TableViewer(committers);
+			ViewerSupport.bind(peopleViewer, new WritableList(viewModel
+					.getPeople(), Person.class), BeanProperties.value(
+					Person.class, "name"));
+
+			column.pack();
+
+			// Open and return the Shell
+			shell.setSize(100, 300);
+			shell.open();
+			return shell;
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet010MasterDetail.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet010MasterDetail.java
new file mode 100644
index 0000000..6be3654
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet010MasterDetail.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Snippet that displays a simple master detail use case. A list of persons is
+ * displayed in a list and upon selection the name of the selected person will
+ * be displayed in a Text widget.
+ */
+public class Snippet010MasterDetail {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+				shell.setLayout(new GridLayout());
+
+				Person[] persons = new Person[] { new Person("Me"),
+						new Person("Myself"), new Person("I") };
+
+				ListViewer viewer = new ListViewer(shell);
+				viewer.setContentProvider(new ArrayContentProvider());
+				viewer.setInput(persons);
+
+				Text name = new Text(shell, SWT.BORDER | SWT.READ_ONLY);
+
+				// 1. Observe changes in selection.
+				IObservableValue selection = ViewersObservables
+						.observeSingleSelection(viewer);
+
+				// 2. Observe the name property of the current selection.
+				IObservableValue detailObservable = BeansObservables
+						.observeDetailValue(selection, "name", String.class);
+
+				// 3. Bind the Text widget to the name detail (selection's
+				// name).
+				new DataBindingContext().bindValue(SWTObservables.observeText(
+						name, SWT.None), detailObservable,
+						new UpdateValueStrategy(false,
+								UpdateValueStrategy.POLICY_NEVER), null);
+
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	public static class Person {
+		private String name;
+		private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
+
+		Person(String name) {
+			this.name = name;
+		}
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.addPropertyChangeListener(listener);
+		}
+		
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.removePropertyChangeListener(listener);
+		}
+		
+		/**
+		 * @return Returns the name.
+		 */
+		public String getName() {
+			return name;
+		}
+
+		public String toString() {
+			return name;
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet011ValidateMultipleBindingsSnippet.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet011ValidateMultipleBindingsSnippet.java
new file mode 100644
index 0000000..59ae4c2
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet011ValidateMultipleBindingsSnippet.java
@@ -0,0 +1,129 @@
+ /*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+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.ValueChangeEvent;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Snippet that validates values across multiple bindings on change of each
+ * observable. If the values of the target observables are not equal the model
+ * is not updated. When the values are equal they will be written to sysout.
+ * 
+ * @author Brad Reynolds
+ */
+public class Snippet011ValidateMultipleBindingsSnippet {
+	public static void main(String[] args) {
+		Realm.runWithDefault(SWTObservables.getRealm(Display.getDefault()),
+				new Runnable() {
+					public void run() {
+						Snippet011ValidateMultipleBindingsSnippet.run();
+					}
+				});
+	}
+
+	private static void run() {
+		Shell shell = new Shell();
+
+		View view = new View(shell);
+		final Model model = new Model();
+
+		DataBindingContext dbc = new DataBindingContext();
+		dbc.bindValue(SWTObservables.observeText(view.text1, SWT.Modify),
+				model.value1, new UpdateValueStrategy()
+						.setAfterConvertValidator(new CrossFieldValidator(
+								model.value2)), null);
+		dbc.bindValue(SWTObservables.observeText(view.text2, SWT.Modify),
+				model.value2, new UpdateValueStrategy()
+						.setAfterConvertValidator(new CrossFieldValidator(
+								model.value1)), null);
+
+		// DEBUG - print to show value change
+		model.value1.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				System.out.println("Value 1: " + model.value1.getValue());
+			}
+		});
+
+		// DEBUG - print to show value change
+		model.value2.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				System.out.println("Value 2: " + model.value2.getValue());
+			}
+		});
+
+		shell.pack();
+		shell.open();
+		Display display = shell.getDisplay();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+		display.dispose();
+	}
+
+	/**
+	 * @since 3.2
+	 * 
+	 */
+	private static final class CrossFieldValidator implements IValidator {
+		/**
+		 * 
+		 */
+		private final IObservableValue other;
+
+		/**
+		 * @param model
+		 */
+		private CrossFieldValidator(IObservableValue other) {
+			this.other = other;
+		}
+
+		public IStatus validate(Object value) {
+			if (!value.equals(other.getValue())) {
+				return ValidationStatus.ok();
+			}
+			return ValidationStatus.error("values cannot be the same");
+		}
+	}
+
+	static class Model {
+		WritableValue value1 = new WritableValue();
+		WritableValue value2 = new WritableValue();
+	}
+
+	static class View {
+		Text text1;
+		Text text2;
+
+		View(Composite composite) {
+			composite.setLayout(new GridLayout(2, true));
+			text1 = new Text(composite, SWT.BORDER);
+			text2 = new Text(composite, SWT.BORDER);
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet012CompositeUpdater.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet012CompositeUpdater.java
new file mode 100644
index 0000000..af9fee0
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet012CompositeUpdater.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.provisional.swt.CompositeUpdater;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet012CompositeUpdater {
+
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell(display);
+
+				final WritableList list = new WritableList();
+
+				Button button = new Button(shell, SWT.PUSH);
+				button.setText("add");
+				button.addSelectionListener(new SelectionAdapter() {
+					public void widgetSelected(
+							org.eclipse.swt.events.SelectionEvent e) {
+						list.add(0, new Counter());
+					}
+				});
+
+				final Composite composite = new Composite(shell, SWT.None);
+
+				new CompositeUpdater(composite, list) {
+					protected Widget createWidget(int index) {
+						Label label = new Label(composite, SWT.BORDER);
+						//requestLayout(label);
+						return label;
+					}
+
+					protected void updateWidget(Widget widget, Object element) {
+						((Label) widget).setText(((Counter) element).getValue()
+								+ "");
+						requestLayout((Label)widget);
+					}
+				};
+				GridLayoutFactory.fillDefaults().numColumns(10).generateLayout(composite);
+
+				GridDataFactory.fillDefaults().grab(true, true).applyTo(
+						composite);
+
+				GridLayoutFactory.fillDefaults().generateLayout(shell);
+				shell.pack();
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+		display.dispose();
+	}
+
+	static Timer timer = new Timer(true);
+
+	static class Counter extends WritableValue {
+		Counter() {
+			super(new Integer(0), Integer.class);
+			scheduleIncrementTask();
+		}
+
+		private void scheduleIncrementTask() {
+			timer.schedule(new TimerTask() {
+				public void run() {
+					// we have to get onto the realm (UI thread) to perform the
+					// increment
+					getRealm().asyncExec(new Runnable() {
+						public void run() {
+							Integer currentVal = (Integer) getValue();
+							setValue(new Integer(currentVal.intValue() + 1));
+						}
+					});
+					scheduleIncrementTask();
+				}
+			}, 1000);
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet013TableViewerEditing.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet013TableViewerEditing.java
new file mode 100644
index 0000000..987049d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet013TableViewerEditing.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *     Tom Schindl - cell editing
+ *     Matthew Hall - bugs 260329, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+/**
+ * Demonstrates binding a TableViewer to a collection using the 3.3 Viewer APIs.
+ */
+public class Snippet013TableViewerEditing {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				ViewModel viewModel = new ViewModel();
+				Shell shell = new View(viewModel).createShell();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	static class Person extends AbstractModelObject {
+		// A property...
+		String name = "John Smith";
+
+		public Person(String name) {
+			this.name = name;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			String oldValue = this.name;
+			this.name = name;
+			firePropertyChange("name", oldValue, name);
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// ro retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private List people = new LinkedList();
+		{
+			people.add(new Person("Steve Northover"));
+			people.add(new Person("Grant Gayed"));
+			people.add(new Person("Veronika Irvine"));
+			people.add(new Person("Mike Wilson"));
+			people.add(new Person("Christophe Cornu"));
+			people.add(new Person("Lynne Kues"));
+			people.add(new Person("Silenio Quarti"));
+		}
+
+		public List getPeople() {
+			return people;
+		}
+	}
+
+	/**
+	 * Editing support that uses JFace Data Binding to control the editing
+	 * lifecycle. The standard EditingSupport get/setValue(...) lifecycle is not
+	 * used.
+	 * 
+	 * @since 3.3
+	 */
+	private static class InlineEditingSupport extends
+			ObservableValueEditingSupport {
+		private CellEditor cellEditor;
+
+		/**
+		 * @param viewer
+		 * @param dbc
+		 */
+		public InlineEditingSupport(ColumnViewer viewer, DataBindingContext dbc) {
+
+			super(viewer, dbc);
+			cellEditor = new TextCellEditor((Composite) viewer.getControl());
+		}
+
+		protected CellEditor getCellEditor(Object element) {
+			return cellEditor;
+		}
+
+		protected IObservableValue doCreateCellEditorObservable(
+				CellEditor cellEditor) {
+
+			return SWTObservables.observeText(cellEditor.getControl(),
+					SWT.Modify);
+		}
+
+		protected IObservableValue doCreateElementObservable(Object element,
+				ViewerCell cell) {
+			return BeansObservables.observeValue(element, "name");
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Table committers;
+		private Label selectedCommitter;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			shell.setLayout(new FillLayout(SWT.VERTICAL));
+			committers = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
+			committers.setLinesVisible(true);
+
+			selectedCommitter = new Label(shell, SWT.NONE);
+			// Set up data binding. In an RCP application, the threading
+			// Realm
+			// will be set for you automatically by the Workbench. In an SWT
+			// application, you can do this once, wrpping your binding
+			// method call.
+			DataBindingContext bindingContext = new DataBindingContext();
+			bindGUI(bindingContext);
+
+			// Open and return the Shell
+			shell.setSize(100, 300);
+			shell.open();
+			return shell;
+		}
+
+		protected void bindGUI(DataBindingContext bindingContext) {
+			// Since we're using a JFace Viewer, we do first wrap our Table...
+			TableViewer peopleViewer = new TableViewer(committers);
+			TableViewerColumn column = new TableViewerColumn(peopleViewer,
+					SWT.NONE);
+			column.setEditingSupport(new InlineEditingSupport(peopleViewer,
+					bindingContext));
+			column.getColumn().setWidth(100);
+
+			// Bind viewer to model
+			ViewerSupport.bind(peopleViewer, new WritableList(viewModel
+					.getPeople(), Person.class), BeanProperties.value(
+					Person.class, "name"));
+
+			// bind selectedCommitter label to the name of the current selection
+			IObservableValue selection = ViewersObservables
+					.observeSingleSelection(peopleViewer);
+			bindingContext.bindValue(SWTObservables
+					.observeText(selectedCommitter), BeansObservables
+					.observeDetailValue(selection, "name", String.class));
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet014WizardDialog.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet014WizardDialog.java
new file mode 100644
index 0000000..c4b3753
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet014WizardDialog.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 260329
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Date;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Creates and opens a wizard dialog with two simple wizard pages.
+ */
+public class Snippet014WizardDialog {
+
+	static class FirstWizardPage extends WizardPage {
+		private final class SingleDigitValidator implements IValidator {
+			public IStatus validate(Object value) {
+				Integer i = (Integer) value;
+				if (i == null) {
+					return ValidationStatus
+							.info("Please enter a value.");
+				}
+				if (i.intValue() < 0 || i.intValue() > 9) {
+					return ValidationStatus
+							.error("Value must be between 0 and 9.");
+				}
+				return ValidationStatus.ok();
+			}
+		}
+
+		protected FirstWizardPage() {
+			super("First", "First Page", ImageDescriptor
+					.createFromImage(new Image(Display.getDefault(), 16, 16)));
+		}
+
+		public void createControl(Composite parent) {
+			DataBindingContext dbc = new DataBindingContext();
+			WizardPageSupport.create(this, dbc);
+			Composite composite = new Composite(parent, SWT.NONE);
+			Label label = new Label(composite, SWT.NONE);
+			label.setText("Enter a number between 0 and 9:");
+			Text text = new Text(composite, SWT.BORDER);
+			
+			dbc.bindValue(
+							SWTObservables.observeText(text, SWT.Modify),
+							((SampleWizard) getWizard()).getModel().intValue,
+							new UpdateValueStrategy().setAfterConvertValidator(new SingleDigitValidator()),
+							null);
+			
+			GridLayoutFactory.swtDefaults().numColumns(2).generateLayout(
+					composite);
+			setControl(composite);
+		}
+	}
+
+	static class SecondWizardPage extends WizardPage {
+		protected SecondWizardPage() {
+			super("Second", "Second Page", ImageDescriptor
+					.createFromImage(new Image(Display.getDefault(), 16, 16)));
+		}
+
+		public void createControl(Composite parent) {
+			DataBindingContext dbc = new DataBindingContext();
+			WizardPageSupport.create(this, dbc);
+			Composite composite = new Composite(parent, SWT.NONE);
+			Label label = new Label(composite, SWT.NONE);
+			label.setText("Enter a date:");
+			Text text = new Text(composite, SWT.BORDER);
+			
+			dbc.bindValue(
+							SWTObservables.observeText(text, SWT.Modify),
+							((SampleWizard) getWizard()).getModel().dateValue);
+
+			GridLayoutFactory.swtDefaults().numColumns(2).generateLayout(
+					composite);
+			setControl(composite);
+		}
+	}
+
+	static class SampleWizardModel {
+		IObservableValue intValue = new WritableValue(null, Integer.class);
+		IObservableValue dateValue = new WritableValue(null, Date.class);
+	}
+
+	static class SampleWizard extends Wizard {
+
+		private SampleWizardModel model = new SampleWizardModel();
+
+		public void addPages() {
+			addPage(new FirstWizardPage());
+			addPage(new SecondWizardPage());
+		}
+
+		public SampleWizardModel getModel() {
+			return model;
+		}
+		
+		public String getWindowTitle() {
+			return "Data Binding Snippet014";
+		}
+
+		public boolean performFinish() {
+			return true;
+		}
+
+	}
+
+	public static void main(String[] args) {
+		Display display = new Display();
+
+		// note that the "runWithDefault" will be done for you if you are using
+		// the
+		// Workbench as opposed to just JFace/SWT.
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				IWizard wizard = new SampleWizard();
+				WizardDialog dialog = new WizardDialog(null, wizard);
+				dialog.open();
+				// The SWT event loop
+				Display display = Display.getCurrent();
+				while (dialog.getShell() != null
+						&& !dialog.getShell().isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet015DelayTextModifyEvents.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet015DelayTextModifyEvents.java
new file mode 100644
index 0000000..7156598
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet015DelayTextModifyEvents.java
@@ -0,0 +1,119 @@
+/************************************************************************************************************
+ * Copyright (c) 2007, 2009 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 - bugs 260329, 264286
+ ***********************************************************************************************************/
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.resource.FontDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class Snippet015DelayTextModifyEvents {
+
+	private static void createControls(Shell shell) {
+		final Label field1 = createLabel(shell, SWT.NONE, "Field 1 ");
+
+		Text text1 = new Text(shell, SWT.BORDER);
+		GridDataFactory.fillDefaults().grab(true, false).hint(200, SWT.DEFAULT)
+				.applyTo(text1);
+		createLabel(shell, SWT.NONE, "200ms delay");
+
+		final Label field2 = createLabel(shell, SWT.NONE, "Field 2 ");
+
+		Text text2 = new Text(shell, SWT.BORDER);
+		GridDataFactory.fillDefaults().grab(true, false).hint(200, SWT.DEFAULT)
+				.applyTo(text2);
+
+		createLabel(shell, SWT.NONE, "1000ms delay");
+
+		final ISWTObservableValue delayed1 = WidgetProperties.text(SWT.Modify)
+				.observeDelayed(200, text1);
+		final ISWTObservableValue delayed2 = WidgetProperties.text(SWT.Modify)
+				.observeDelayed(1000, text2);
+
+		// (In a real application,you would want to dispose the resource manager
+		// when you are done with it)
+		ResourceManager resourceManager = new LocalResourceManager(
+				JFaceResources.getResources());
+		final Font shellFont = shell.getFont();
+		final Font italicFont = resourceManager.createFont(FontDescriptor
+				.createFrom(shellFont).setStyle(SWT.ITALIC));
+
+		final IObservableValue stale1 = Observables.observeStale(delayed1);
+		new ControlUpdater(field2) {
+			protected void updateControl() {
+				boolean stale = ((Boolean) stale1.getValue()).booleanValue();
+				field2.setFont(stale ? italicFont : shellFont);
+			}
+		};
+
+		final IObservableValue stale2 = Observables.observeStale(delayed2);
+		new ControlUpdater(field1) {
+			protected void updateControl() {
+				boolean stale = ((Boolean) stale2.getValue()).booleanValue();
+				field1.setFont(stale ? italicFont : shellFont);
+			}
+		};
+
+		String info = "Pending changes are applied immediately if the observed control loses focus";
+		GridDataFactory.fillDefaults().span(3, 1).applyTo(
+				createLabel(shell, SWT.WRAP, info));
+
+		DataBindingContext dbc = new DataBindingContext();
+
+		dbc.bindValue(delayed1, delayed2);
+	}
+
+	private static Label createLabel(Composite parent, int style, String text) {
+		Label label = new Label(parent, style);
+		label.setText(text);
+		return label;
+	}
+
+	public static void main(String[] args) {
+		final Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Shell();
+				shell.setLayout(new GridLayout(3, false));
+
+				createControls(shell);
+
+				shell.pack();
+				shell.open();
+				while (!shell.isDisposed())
+					if (!display.readAndDispatch())
+						display.sleep();
+			}
+
+		});
+
+		display.dispose();
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet016TableUpdater.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet016TableUpdater.java
new file mode 100644
index 0000000..c0316fa
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet016TableUpdater.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.provisional.swt.TableUpdater;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet016TableUpdater {
+	public static void main(String[] args) {
+		final Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				final Shell shell = createShell(display);
+				GridLayoutFactory.fillDefaults().generateLayout(shell);
+				shell.open();
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	static class Stuff {
+		private WritableValue counter = new WritableValue(new Integer(1), Integer.class);
+
+		public Stuff(final Display display) {
+			display.timerExec(1000, new Runnable() {
+				public void run() {
+					counter.setValue(new Integer(1 + ((Integer) counter
+							.getValue()).intValue()));
+					display.timerExec(1000, this);
+				}
+			});
+		}
+		
+		public String toString() {
+			return counter.getValue().toString();
+		}
+	}
+
+	protected static Shell createShell(final Display display) {
+		Shell shell = new Shell();
+		Table t = new Table(shell, SWT.VIRTUAL);
+		final WritableList list = new WritableList();
+		new TableUpdater(t, list) {
+
+			protected void updateItem(int index, TableItem item, Object element) {
+				item.setText(element.toString());
+			}
+		};
+		display.timerExec(2000, new Runnable() {
+			public void run() {
+				list.add(new Stuff(display));
+				display.timerExec(2000, this);
+			}
+		});
+		return shell;
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet017TableViewerWithDerivedColumns.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet017TableViewerWithDerivedColumns.java
new file mode 100644
index 0000000..fc740cd
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet017TableViewerWithDerivedColumns.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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:
+ *     Coconut Palm Software, Inc. - Initial API and implementation
+ *     Matthew Hall - bugs 260329, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+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.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Demonstrates binding a TableViewer to a collection.
+ */
+public class Snippet017TableViewerWithDerivedColumns {
+	public static void main(String[] args) {
+		final Display display = new Display();
+
+		// Set up data binding. In an RCP application, the threading Realm
+		// will be set for you automatically by the Workbench. In an SWT
+		// application, you can do this once, wrapping your binding
+		// method call.
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				ViewModel viewModel = new ViewModel();
+				Shell shell = new View(viewModel).createShell();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	private static Person UNKNOWN = new Person("unknown", null, null);
+
+	// The data model class. This is normally a persistent class of some sort.
+	static class Person extends AbstractModelObject {
+		// A property...
+		String name = "Donald Duck";
+		Person mother;
+		Person father;
+
+		public Person(String name, Person mother, Person father) {
+			this.name = name;
+			this.mother = mother;
+			this.father = father;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			String oldValue = this.name;
+			this.name = name;
+			firePropertyChange("name", oldValue, name);
+		}
+
+		public Person getMother() {
+			return mother;
+		}
+
+		public void setMother(Person mother) {
+			firePropertyChange("mother", this.mother, this.mother = mother);
+		}
+
+		public Person getFather() {
+			return father;
+		}
+
+		public void setFather(Person father) {
+			firePropertyChange("father", this.father, this.father = father);
+		}
+
+		public String toString() {
+			return name;
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// ro retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private IObservableList people = new WritableList();
+		{
+			Person fergus = new Person("Fergus McDuck", UNKNOWN, UNKNOWN);
+			Person downy = new Person("Downy O'Drake", UNKNOWN, UNKNOWN);
+			Person scrooge = new Person("Scrooge McDuck", downy, fergus);
+			Person hortense = new Person("Hortense McDuck", downy, fergus);
+			Person quackmore = new Person("Quackmore Duck", UNKNOWN, UNKNOWN);
+			Person della = new Person("Della Duck", hortense, quackmore);
+			Person donald = new Person("Donald Duck", hortense, quackmore);
+			donald.setFather(quackmore);
+			donald.setMother(hortense);
+			della.setFather(quackmore);
+			della.setMother(hortense);
+			hortense.setMother(downy);
+			hortense.setFather(fergus);
+			scrooge.setMother(downy);
+			scrooge.setFather(fergus);
+			people.add(UNKNOWN);
+			people.add(downy);
+			people.add(fergus);
+			people.add(scrooge);
+			people.add(quackmore);
+			people.add(hortense);
+			people.add(della);
+			people.add(donald);
+		}
+
+		public IObservableList getPeople() {
+			return people;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Table duckFamily;
+		private Text nameText;
+		private Combo motherCombo;
+		private Combo fatherCombo;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			duckFamily = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
+			duckFamily.setHeaderVisible(true);
+			GridDataFactory.defaultsFor(duckFamily).span(2, 1).applyTo(
+					duckFamily);
+			createColumn("Name");
+			createColumn("Mother");
+			createColumn("Father");
+			createColumn("Grandmother");
+			duckFamily.setLinesVisible(true);
+
+			new Label(shell, SWT.NONE).setText("Name:");
+			nameText = new Text(shell, SWT.BORDER);
+			GridDataFactory.defaultsFor(nameText).grab(true, false).applyTo(
+					nameText);
+
+			new Label(shell, SWT.NONE).setText("Mother:");
+			motherCombo = new Combo(shell, SWT.READ_ONLY);
+
+			new Label(shell, SWT.NONE).setText("Father:");
+			fatherCombo = new Combo(shell, SWT.READ_ONLY);
+
+			DataBindingContext bindingContext = new DataBindingContext();
+			bindGUI(bindingContext);
+
+			GridLayoutFactory.swtDefaults().numColumns(2).applyTo(shell);
+			// Open and return the Shell
+			shell.setSize(500, 300);
+			shell.open();
+			return shell;
+		}
+
+		private void createColumn(String string) {
+			final TableColumn column = new TableColumn(duckFamily, SWT.NONE);
+			column.setWidth(100);
+			column.setText(string);
+		}
+
+		protected void bindGUI(DataBindingContext bindingContext) {
+			// Since we're using a JFace Viewer, we do first wrap our Table...
+			TableViewer peopleViewer = new TableViewer(duckFamily);
+			peopleViewer.addFilter(new ViewerFilter() {
+				public boolean select(Viewer viewer, Object parentElement,
+						Object element) {
+					return element != UNKNOWN;
+				}
+			});
+
+			// Bind viewers to model
+			ViewerSupport.bind(peopleViewer, viewModel.getPeople(),
+					BeanProperties.values(new String[] { "name", "mother.name",
+							"father.name", "mother.mother.name" }));
+
+			// Bind viewer selection to detail fields
+			IObservableValue selection = ViewersObservables
+					.observeSingleSelection(peopleViewer);
+			bindingContext.bindValue(SWTObservables.observeText(nameText,
+					SWT.Modify), BeansObservables.observeDetailValue(selection,
+					"name", String.class));
+
+			ComboViewer mothercomboViewer = new ComboViewer(motherCombo);
+			ViewerSupport.bind(mothercomboViewer, viewModel.getPeople(),
+					BeanProperties.value("name"));
+			bindingContext.bindValue(ViewersObservables
+					.observeSingleSelection(mothercomboViewer),
+					BeansObservables.observeDetailValue(selection, "mother",
+							Person.class));
+
+			ComboViewer fatherComboViewer = new ComboViewer(fatherCombo);
+			ViewerSupport.bind(fatherComboViewer, viewModel.getPeople(),
+					BeanProperties.value("name"));
+			bindingContext.bindValue(ViewersObservables
+					.observeSingleSelection(fatherComboViewer),
+					BeansObservables.observeDetailValue(selection, "father",
+							Person.class));
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet018CheckboxTableViewerCheckedSelection.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet018CheckboxTableViewerCheckedSelection.java
new file mode 100644
index 0000000..e978d0e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet018CheckboxTableViewerCheckedSelection.java
@@ -0,0 +1,362 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bugs 260329, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Snippet 018: Binding to the checked elements in a CheckboxTableViewer.
+ */
+public class Snippet018CheckboxTableViewerCheckedSelection {
+	public static void main(String[] args) {
+		// The SWT event loop
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				ViewModel viewModel = createSampleModel();
+
+				Shell shell = new View(viewModel).createShell();
+				shell.open();
+				while (!shell.isDisposed())
+					if (!display.readAndDispatch())
+						display.sleep();
+			}
+		});
+		display.dispose();
+	}
+
+	private static ViewModel createSampleModel() {
+		ViewModel viewModel = new ViewModel();
+
+		Person stan = createPerson("Stan");
+		Person kyle = createPerson("Kyle");
+		Person eric = createPerson("Eric");
+		Person kenny = createPerson("Kenny");
+		Person wendy = createPerson("Wendy");
+		Person butters = createPerson("Butters");
+
+		setFriends(stan, new Person[] { kyle, eric, kenny, wendy });
+		setFriends(kyle, new Person[] { stan, eric, kenny });
+		setFriends(eric, new Person[] { eric });
+		setFriends(kenny, new Person[] { stan, kyle, eric });
+		setFriends(wendy, new Person[] { stan });
+		setFriends(butters, new Person[0]);
+
+		Person[] people = new Person[] { stan, kyle, eric, kenny, wendy,
+				butters };
+		viewModel.setPeople(Arrays.asList(people));
+		return viewModel;
+	}
+
+	private static Person createPerson(String name) {
+		Person person = new Person();
+		person.setName(name);
+		return person;
+	}
+
+	private static void setFriends(Person person, Person[] friends) {
+		person.setFriends(new HashSet(Arrays.asList(friends)));
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The data model class.
+	static class Person extends AbstractModelObject {
+		private String name;
+		private Set friends = new HashSet();
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = name);
+		}
+
+		public Set getFriends() {
+			return new HashSet(friends);
+		}
+
+		public void setFriends(Set friends) {
+			firePropertyChange("friends", this.friends,
+					this.friends = new HashSet(friends));
+		}
+
+		public String toString() {
+			return name;
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	//
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// to retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel extends AbstractModelObject {
+		private List people = new ArrayList();
+
+		public List getPeople() {
+			return new ArrayList(people);
+		}
+
+		public void setPeople(List people) {
+			firePropertyChange("people", this.people,
+					this.people = new ArrayList(people));
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+
+		private Shell shell;
+
+		private Button addPersonButton;
+		private Button removePersonButton;
+		private TableViewer peopleViewer;
+		private Text personName;
+		private CheckboxTableViewer friendsViewer;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			final Display display = Display.getCurrent();
+			shell = new Shell(display);
+
+			createUI(shell);
+
+			// Bind UI
+			bindUI();
+
+			// Open and return the Shell
+			shell.setSize(shell.computeSize(400, SWT.DEFAULT));
+			shell.open();
+			return shell;
+		}
+
+		private void createUI(Shell shell) {
+			shell.setText("Binding checked elements in CheckboxTableViewer");
+			shell.setLayout(new GridLayout(2, false));
+
+			new Label(shell, SWT.NONE).setText("People");
+
+			Composite buttons = new Composite(shell, SWT.NONE);
+			GridDataFactory.swtDefaults().align(SWT.RIGHT, SWT.CENTER).applyTo(
+					buttons);
+			GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(true)
+					.applyTo(buttons);
+			addPersonButton = new Button(buttons, SWT.PUSH);
+			addPersonButton.setText("Add");
+			GridDataFactory.fillDefaults().applyTo(addPersonButton);
+			removePersonButton = new Button(buttons, SWT.PUSH);
+			removePersonButton.setText("Remove");
+			GridDataFactory.fillDefaults().applyTo(removePersonButton);
+
+			Composite peopleComposite = new Composite(shell, SWT.NONE);
+			GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(
+					peopleComposite);
+			TableColumnLayout peopleColumnLayout = new TableColumnLayout();
+			peopleComposite.setLayout(peopleColumnLayout);
+
+			peopleViewer = new TableViewer(peopleComposite, SWT.SINGLE
+					| SWT.BORDER | SWT.FULL_SELECTION);
+
+			Table peopleTable = peopleViewer.getTable();
+			peopleTable.setHeaderVisible(true);
+			peopleTable.setLinesVisible(true);
+
+			TableColumn nameColumn = new TableColumn(peopleTable, SWT.NONE);
+			nameColumn.setText("Name");
+			peopleColumnLayout.setColumnData(nameColumn,
+					new ColumnWeightData(1));
+
+			TableColumn friendsColumn = new TableColumn(peopleTable, SWT.NONE);
+			friendsColumn.setText("Friends");
+			peopleColumnLayout.setColumnData(friendsColumn,
+					new ColumnWeightData(3));
+
+			new Label(shell, SWT.NONE).setText("Name");
+
+			personName = new Text(shell, SWT.BORDER);
+			GridDataFactory.fillDefaults().grab(true, false)
+					.applyTo(personName);
+
+			new Label(shell, SWT.NONE).setText("Friends");
+
+			Composite friendsComposite = new Composite(shell, SWT.NONE);
+			GridDataFactory.fillDefaults().grab(true, true).applyTo(
+					friendsComposite);
+			TableColumnLayout friendsColumnLayout = new TableColumnLayout();
+			friendsComposite.setLayout(friendsColumnLayout);
+
+			friendsViewer = CheckboxTableViewer.newCheckList(friendsComposite,
+					SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION);
+
+			Table friendsTable = friendsViewer.getTable();
+			friendsTable.setHeaderVisible(true);
+			friendsTable.setLinesVisible(true);
+			TableColumn friendNameColumn = new TableColumn(friendsTable,
+					SWT.NONE);
+			friendNameColumn.setText("Name");
+			friendsColumnLayout.setColumnData(friendNameColumn,
+					new ColumnWeightData(1));
+
+			GridDataFactory.fillDefaults().grab(true, true).applyTo(
+					friendsViewer.getTable());
+		}
+
+		private void bindUI() {
+			DataBindingContext dbc = new DataBindingContext();
+
+			final IObservableList people = BeansObservables.observeList(Realm
+					.getDefault(), viewModel, "people");
+
+			addPersonButton.addListener(SWT.Selection, new Listener() {
+				public void handleEvent(Event event) {
+					InputDialog dlg = new InputDialog(shell, "Add Person",
+							"Enter name:", "<Name>", new IInputValidator() {
+								public String isValid(String newText) {
+									if (newText == null
+											|| newText.length() == 0)
+										return "Name cannot be empty";
+									return null;
+								}
+							});
+					if (dlg.open() == Window.OK) {
+						Person person = new Person();
+						person.setName(dlg.getValue());
+						people.add(person);
+						peopleViewer.setSelection(new StructuredSelection(
+								person));
+					}
+				}
+			});
+
+			removePersonButton.addListener(SWT.Selection, new Listener() {
+				public void handleEvent(Event event) {
+					IStructuredSelection selected = (IStructuredSelection) peopleViewer
+							.getSelection();
+					if (selected.isEmpty())
+						return;
+					Person person = (Person) selected.getFirstElement();
+					if (MessageDialog.openConfirm(shell, "Remove person",
+							"Remove " + person.getName() + "?"))
+						people.remove(person);
+				}
+			});
+
+			ViewerSupport.bind(peopleViewer, people, BeanProperties.values(
+					Person.class, new String[] { "name", "friends" }));
+
+			final IObservableValue selectedPerson = ViewersObservables
+					.observeSingleSelection(peopleViewer);
+
+			IObservableValue personSelected = new ComputedValue(Boolean.TYPE) {
+				protected Object calculate() {
+					return Boolean.valueOf(selectedPerson.getValue() != null);
+				}
+			};
+			dbc.bindValue(SWTObservables.observeEnabled(removePersonButton),
+					personSelected);
+			dbc.bindValue(SWTObservables.observeEnabled(friendsViewer
+					.getTable()), personSelected);
+
+			dbc.bindValue(SWTObservables.observeText(personName, SWT.Modify),
+					BeansObservables.observeDetailValue(selectedPerson, "name",
+							String.class));
+
+			ViewerSupport.bind(friendsViewer, people, BeanProperties.value(
+					Person.class, "name"));
+
+			dbc.bindSet(ViewersObservables.observeCheckedElements(
+					friendsViewer, Person.class), BeansObservables
+					.observeDetailSet(selectedPerson, "friends", Person.class));
+		}
+	}
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet019TreeViewerWithListFactory.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet019TreeViewerWithListFactory.java
new file mode 100644
index 0000000..d7437a1
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet019TreeViewerWithListFactory.java
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 260329, 260337
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+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.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+public class Snippet019TreeViewerWithListFactory {
+
+	private Button pasteButton;
+	private Button copyButton;
+	private Shell shell;
+	private Button addChildBeanButton;
+	private Button removeBeanButton;
+	private TreeViewer beanViewer;
+	private Tree tree;
+	private Text beanText;
+	private DataBindingContext m_bindingContext;
+
+	private Bean input = createBean("input");
+	private IObservableValue clipboard;
+
+	/**
+	 * Launch the application
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				try {
+					Snippet019TreeViewerWithListFactory window = new Snippet019TreeViewerWithListFactory();
+					window.open();
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		});
+	}
+
+	/**
+	 * Open the window
+	 */
+	public void open() {
+		final Display display = Display.getDefault();
+		createContents();
+		shell.open();
+		shell.layout();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+	}
+
+	/**
+	 * Create contents of the window
+	 */
+	protected void createContents() {
+		shell = new Shell();
+		final GridLayout gridLayout_1 = new GridLayout();
+		gridLayout_1.numColumns = 2;
+		shell.setLayout(gridLayout_1);
+		shell.setSize(535, 397);
+		shell.setText("SWT Application");
+
+		final Composite group = new Composite(shell, SWT.NONE);
+		final RowLayout rowLayout = new RowLayout();
+		rowLayout.marginTop = 0;
+		rowLayout.marginRight = 0;
+		rowLayout.marginLeft = 0;
+		rowLayout.marginBottom = 0;
+		rowLayout.pack = false;
+		group.setLayout(rowLayout);
+		group
+				.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false,
+						2, 1));
+
+		final Button addRootButton = new Button(group, SWT.NONE);
+		addRootButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				List list = input.getList();
+				Bean root = createBean("root");
+				list.add(root);
+				input.setList(list);
+
+				beanViewer.setSelection(new StructuredSelection(root));
+				beanText.selectAll();
+				beanText.setFocus();
+			}
+		});
+		addRootButton.setText("Add Root");
+
+		addChildBeanButton = new Button(group, SWT.NONE);
+		addChildBeanButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				Bean parent = getSelectedBean();
+				List list = new ArrayList(parent.getList());
+				Bean child = createBean("child");
+				list.add(child);
+				parent.setList(list);
+
+				beanViewer.setSelection(new StructuredSelection(child));
+				beanText.selectAll();
+				beanText.setFocus();
+			}
+		});
+		addChildBeanButton.setText("Add Child");
+
+		removeBeanButton = new Button(group, SWT.NONE);
+		removeBeanButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				TreeItem selectedItem = beanViewer.getTree().getSelection()[0];
+				TreeItem parentItem = selectedItem.getParentItem();
+				Bean parent;
+				int index;
+				if (parentItem == null) {
+					parent = input;
+					index = beanViewer.getTree().indexOf(selectedItem);
+				} else {
+					parent = (Bean) parentItem.getData();
+					index = parentItem.indexOf(selectedItem);
+				}
+
+				List list = new ArrayList(parent.getList());
+				list.remove(index);
+				parent.setList(list);
+			}
+		});
+		removeBeanButton.setText("Remove");
+
+		copyButton = new Button(group, SWT.NONE);
+		copyButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				clipboard.setValue(getSelectedBean());
+			}
+		});
+		copyButton.setText("Copy");
+
+		pasteButton = new Button(group, SWT.NONE);
+		pasteButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				Bean copy = (Bean) clipboard.getValue();
+				if (copy == null)
+					return;
+				Bean parent = getSelectedBean();
+				if (parent == null)
+					parent = input;
+
+				List list = new ArrayList(parent.getList());
+				list.add(copy);
+				parent.setList(list);
+
+				beanViewer.setSelection(new StructuredSelection(copy));
+				beanText.selectAll();
+				beanText.setFocus();
+			}
+		});
+		pasteButton.setText("Paste");
+
+		final Button refreshButton = new Button(group, SWT.NONE);
+		refreshButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				beanViewer.refresh();
+			}
+		});
+		refreshButton.setText("Refresh");
+
+		beanViewer = new TreeViewer(shell, SWT.FULL_SELECTION | SWT.BORDER);
+		beanViewer.setUseHashlookup(true);
+		tree = beanViewer.getTree();
+		tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+
+		final Label itemNameLabel = new Label(shell, SWT.NONE);
+		itemNameLabel.setText("Item Name");
+
+		beanText = new Text(shell, SWT.BORDER);
+		final GridData gd_beanValue = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		beanText.setLayoutData(gd_beanValue);
+		m_bindingContext = initDataBindings();
+		//
+		initExtraBindings(m_bindingContext);
+	}
+
+	private static Bean createBean(String name) {
+		return new Bean(name);
+	}
+
+	protected DataBindingContext initDataBindings() {
+		IObservableValue treeViewerSelectionObserveSelection = ViewersObservables
+				.observeSingleSelection(beanViewer);
+		IObservableValue textTextObserveWidget = SWTObservables.observeText(
+				beanText, SWT.Modify);
+		IObservableValue treeViewerValueObserveDetailValue = BeansObservables
+				.observeDetailValue(treeViewerSelectionObserveSelection,
+						"text", String.class);
+		//
+		//
+		DataBindingContext bindingContext = new DataBindingContext();
+		//
+		bindingContext.bindValue(textTextObserveWidget,
+				treeViewerValueObserveDetailValue);
+		//
+		return bindingContext;
+	}
+
+	private Bean getSelectedBean() {
+		IStructuredSelection selection = (IStructuredSelection) beanViewer
+				.getSelection();
+		if (selection.isEmpty())
+			return null;
+		return (Bean) selection.getFirstElement();
+	}
+
+	private void initExtraBindings(DataBindingContext dbc) {
+		final IObservableValue beanViewerSelection = ViewersObservables
+				.observeSingleSelection(beanViewer);
+		IObservableValue beanSelected = new ComputedValue(Boolean.TYPE) {
+			protected Object calculate() {
+				return Boolean.valueOf(beanViewerSelection.getValue() != null);
+			}
+		};
+		dbc.bindValue(SWTObservables.observeEnabled(addChildBeanButton),
+				beanSelected);
+		dbc.bindValue(SWTObservables.observeEnabled(removeBeanButton),
+				beanSelected);
+
+		clipboard = new WritableValue();
+		dbc.bindValue(SWTObservables.observeEnabled(copyButton), beanSelected);
+		dbc.bindValue(SWTObservables.observeEnabled(pasteButton),
+				new ComputedValue(Boolean.TYPE) {
+					protected Object calculate() {
+						return Boolean.valueOf(clipboard.getValue() != null);
+					}
+				});
+
+		ViewerSupport.bind(beanViewer, input, BeanProperties.list("list",
+				Bean.class), BeanProperties.value(Bean.class, "text"));
+	}
+
+	static class Bean {
+		/* package */PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+				this);
+		private String text;
+		private List list;
+
+		public Bean(String text) {
+			this.text = text;
+			list = new ArrayList();
+		}
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.removePropertyChangeListener(listener);
+		}
+
+		public String getText() {
+			return text;
+		}
+
+		public void setText(String value) {
+			changeSupport.firePropertyChange("text", this.text,
+					this.text = value);
+		}
+
+		public List getList() {
+			if (list == null)
+				return null;
+			return new ArrayList(list);
+		}
+
+		public void setList(List list) {
+			if (list != null)
+				list = new ArrayList(list);
+			changeSupport.firePropertyChange("list", this.list,
+					this.list = list);
+		}
+
+		public boolean hasListeners(String propertyName) {
+			return changeSupport.hasListeners(propertyName);
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet020TreeViewerWithSetFactory.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet020TreeViewerWithSetFactory.java
new file mode 100644
index 0000000..2aca885
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet020TreeViewerWithSetFactory.java
@@ -0,0 +1,325 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 260329, 260337
+ *******************************************************************************/
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+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.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+public class Snippet020TreeViewerWithSetFactory {
+
+	private Button pasteButton;
+	private Button copyButton;
+	private Shell shell;
+	private Button addChildBeanButton;
+	private Button removeBeanButton;
+	private TreeViewer beanViewer;
+	private Tree tree;
+	private Text beanText;
+	private DataBindingContext m_bindingContext;
+
+	private Bean input = createBean("input");
+	private IObservableValue clipboard;
+	static int counter = 0;
+
+	/**
+	 * Launch the application
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				try {
+					Snippet020TreeViewerWithSetFactory window = new Snippet020TreeViewerWithSetFactory();
+					window.open();
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		});
+	}
+
+	/**
+	 * Open the window
+	 */
+	public void open() {
+		final Display display = Display.getDefault();
+		createContents();
+		shell.open();
+		shell.layout();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+	}
+
+	/**
+	 * Create contents of the window
+	 */
+	protected void createContents() {
+		shell = new Shell();
+		final GridLayout gridLayout_1 = new GridLayout();
+		gridLayout_1.numColumns = 2;
+		shell.setLayout(gridLayout_1);
+		shell.setSize(535, 397);
+		shell.setText("SWT Application");
+
+		final Composite group = new Composite(shell, SWT.NONE);
+		final RowLayout rowLayout = new RowLayout();
+		rowLayout.marginTop = 0;
+		rowLayout.marginRight = 0;
+		rowLayout.marginLeft = 0;
+		rowLayout.marginBottom = 0;
+		rowLayout.pack = false;
+		group.setLayout(rowLayout);
+		group
+				.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false,
+						2, 1));
+
+		final Button addRootButton = new Button(group, SWT.NONE);
+		addRootButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				Set set = input.getSet();
+				Bean root = createBean("root");
+				set.add(root);
+				input.setSet(set);
+
+				beanViewer.setSelection(new StructuredSelection(root));
+				beanText.selectAll();
+				beanText.setFocus();
+			}
+		});
+		addRootButton.setText("Add Root");
+
+		addChildBeanButton = new Button(group, SWT.NONE);
+		addChildBeanButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				Bean parent = getSelectedBean();
+				Set set = new HashSet(parent.getSet());
+				Bean child = createBean("child" + (counter++));
+				set.add(child);
+				parent.setSet(set);
+
+				// beanViewer.setSelection(new StructuredSelection(parent));
+				// beanText.selectAll();
+				// beanText.setFocus();
+			}
+		});
+		addChildBeanButton.setText("Add Child");
+
+		removeBeanButton = new Button(group, SWT.NONE);
+		removeBeanButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				TreeItem selectedItem = beanViewer.getTree().getSelection()[0];
+				Bean bean = (Bean) selectedItem.getData();
+				TreeItem parentItem = selectedItem.getParentItem();
+				Bean parent;
+				if (parentItem == null)
+					parent = input;
+				else
+					parent = (Bean) parentItem.getData();
+
+				Set set = new HashSet(parent.getSet());
+				set.remove(bean);
+				parent.setSet(set);
+			}
+		});
+		removeBeanButton.setText("Remove");
+
+		copyButton = new Button(group, SWT.NONE);
+		copyButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				clipboard.setValue(getSelectedBean());
+			}
+		});
+		copyButton.setText("Copy");
+
+		pasteButton = new Button(group, SWT.NONE);
+		pasteButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				Bean copy = (Bean) clipboard.getValue();
+				if (copy == null)
+					return;
+				Bean parent = getSelectedBean();
+				if (parent == null)
+					parent = input;
+
+				Set set = new HashSet(parent.getSet());
+				set.add(copy);
+				parent.setSet(set);
+
+				beanViewer.setSelection(new StructuredSelection(copy));
+				beanText.selectAll();
+				beanText.setFocus();
+			}
+		});
+		pasteButton.setText("Paste");
+
+		final Button refreshButton = new Button(group, SWT.NONE);
+		refreshButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				beanViewer.refresh();
+			}
+		});
+		refreshButton.setText("Refresh");
+
+		beanViewer = new TreeViewer(shell, SWT.FULL_SELECTION | SWT.BORDER);
+		beanViewer.setUseHashlookup(true);
+		beanViewer.setComparator(new ViewerComparator());
+		tree = beanViewer.getTree();
+		tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+
+		final Label itemNameLabel = new Label(shell, SWT.NONE);
+		itemNameLabel.setText("Item Name");
+
+		beanText = new Text(shell, SWT.BORDER);
+		final GridData gd_beanValue = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		beanText.setLayoutData(gd_beanValue);
+		m_bindingContext = initDataBindings();
+		//
+		initExtraBindings(m_bindingContext);
+	}
+
+	private static Bean createBean(String name) {
+		return new Bean(name);
+	}
+
+	protected DataBindingContext initDataBindings() {
+		IObservableValue treeViewerSelectionObserveSelection = ViewersObservables
+				.observeSingleSelection(beanViewer);
+		IObservableValue textTextObserveWidget = SWTObservables.observeText(
+				beanText, SWT.Modify);
+		IObservableValue treeViewerValueObserveDetailValue = BeansObservables
+				.observeDetailValue(treeViewerSelectionObserveSelection,
+						"text", String.class);
+		//
+		//
+		DataBindingContext bindingContext = new DataBindingContext();
+		//
+		bindingContext.bindValue(textTextObserveWidget,
+				treeViewerValueObserveDetailValue);
+		//
+		return bindingContext;
+	}
+
+	private Bean getSelectedBean() {
+		IStructuredSelection selection = (IStructuredSelection) beanViewer
+				.getSelection();
+		if (selection.isEmpty())
+			return null;
+		return (Bean) selection.getFirstElement();
+	}
+
+	private void initExtraBindings(DataBindingContext dbc) {
+		final IObservableValue beanViewerSelection = ViewersObservables
+				.observeSingleSelection(beanViewer);
+		IObservableValue beanSelected = new ComputedValue(Boolean.TYPE) {
+			protected Object calculate() {
+				return Boolean.valueOf(beanViewerSelection.getValue() != null);
+			}
+		};
+		dbc.bindValue(SWTObservables.observeEnabled(addChildBeanButton),
+				beanSelected);
+		dbc.bindValue(SWTObservables.observeEnabled(removeBeanButton),
+				beanSelected);
+
+		clipboard = new WritableValue();
+		dbc.bindValue(SWTObservables.observeEnabled(copyButton), beanSelected);
+		dbc.bindValue(SWTObservables.observeEnabled(pasteButton),
+				new ComputedValue(Boolean.TYPE) {
+					protected Object calculate() {
+						return Boolean.valueOf(clipboard.getValue() != null);
+					}
+				});
+
+		ViewerSupport.bind(beanViewer, input, BeanProperties.set("set",
+				Bean.class), BeanProperties.value(Bean.class, "text"));
+	}
+
+	static class Bean {
+		/* package */PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+				this);
+		private String text;
+		private Set set;
+
+		public Bean(String text) {
+			this.text = text;
+			set = new HashSet();
+		}
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.removePropertyChangeListener(listener);
+		}
+
+		public String getText() {
+			return text;
+		}
+
+		public void setText(String value) {
+			changeSupport.firePropertyChange("text", this.text,
+					this.text = value);
+		}
+
+		public Set getSet() {
+			if (set == null)
+				return null;
+			return new HashSet(set);
+		}
+
+		public void setSet(Set set) {
+			if (set != null)
+				set = new HashSet(set);
+			changeSupport.firePropertyChange("set", this.set, this.set = set);
+		}
+
+		public boolean hasListeners(String propertyName) {
+			return changeSupport.hasListeners(propertyName);
+		}
+	}
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet021MultiFieldValidation.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet021MultiFieldValidation.java
new file mode 100644
index 0000000..cb0ba1e
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet021MultiFieldValidation.java
@@ -0,0 +1,357 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bug 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.DataBindingContext;
+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.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.MultiValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet021MultiFieldValidation extends WizardPage {
+
+	private List list_1;
+	private List list;
+	private Button addAddendButton;
+	private Button removeAddendButton;
+	private Text sumModelValue;
+	private Text field2ModelValue;
+	private Text field1ModelValue;
+	private Text sumTarget;
+	private Text field2Target;
+	private Text field1Target;
+	private ListViewer addendsTarget;
+	private ListViewer addendsModelValue;
+
+	/**
+	 * Create the wizard
+	 */
+	public Snippet021MultiFieldValidation() {
+		super("snippet021");
+		setTitle("Snippet 021 - Multi-field Validators");
+		setDescription("Enter values which satisfy the cross-field constraints");
+	}
+
+	/**
+	 * Create contents of the wizard
+	 * 
+	 * @param parent
+	 */
+	public void createControl(Composite parent) {
+		Composite container = new Composite(parent, SWT.NULL);
+		final GridLayout gridLayout = new GridLayout();
+		gridLayout.numColumns = 2;
+		container.setLayout(gridLayout);
+		//
+		setControl(container);
+
+		final Group bothEvenOrGroup = new Group(container, SWT.NONE);
+		bothEvenOrGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false,
+				false));
+		bothEvenOrGroup.setText("Numbers must be both even or both odd");
+		final GridLayout gridLayout_1 = new GridLayout();
+		gridLayout_1.numColumns = 3;
+		bothEvenOrGroup.setLayout(gridLayout_1);
+		new Label(bothEvenOrGroup, SWT.NONE);
+
+		final Label targetLabel = new Label(bothEvenOrGroup, SWT.NONE);
+		targetLabel.setText("Target");
+
+		final Label modelLabel = new Label(bothEvenOrGroup, SWT.NONE);
+		modelLabel.setText("Model");
+
+		final Label field1Label = new Label(bothEvenOrGroup, SWT.NONE);
+		field1Label.setText("Field 1");
+
+		field1Target = new Text(bothEvenOrGroup, SWT.BORDER);
+		final GridData gd_field1Target = new GridData(SWT.FILL, SWT.CENTER,
+				true, false);
+		field1Target.setLayoutData(gd_field1Target);
+
+		field1ModelValue = new Text(bothEvenOrGroup, SWT.READ_ONLY | SWT.BORDER);
+		final GridData gd_field1ModelValue = new GridData(SWT.FILL, SWT.CENTER,
+				true, false);
+		field1ModelValue.setLayoutData(gd_field1ModelValue);
+
+		final Label field2Label = new Label(bothEvenOrGroup, SWT.NONE);
+		field2Label.setText("Field 2");
+
+		field2Target = new Text(bothEvenOrGroup, SWT.BORDER);
+		final GridData gd_field2Target = new GridData(SWT.FILL, SWT.CENTER,
+				true, false);
+		field2Target.setLayoutData(gd_field2Target);
+
+		field2ModelValue = new Text(bothEvenOrGroup, SWT.READ_ONLY | SWT.BORDER);
+		final GridData gd_field2ModelValue = new GridData(SWT.FILL, SWT.CENTER,
+				true, false);
+		field2ModelValue.setLayoutData(gd_field2ModelValue);
+
+		final Group sumOfAllGroup = new Group(container, SWT.NONE);
+		sumOfAllGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false,
+				true));
+		sumOfAllGroup.setText("Addends must add up to sum");
+		final GridLayout gridLayout_2 = new GridLayout();
+		gridLayout_2.numColumns = 3;
+		sumOfAllGroup.setLayout(gridLayout_2);
+		new Label(sumOfAllGroup, SWT.NONE);
+
+		final Label targetLabel_1 = new Label(sumOfAllGroup, SWT.NONE);
+		targetLabel_1.setText("Target");
+
+		final Label modelLabel_1 = new Label(sumOfAllGroup, SWT.NONE);
+		modelLabel_1.setText("Model");
+
+		final Label expectedSumLabel = new Label(sumOfAllGroup, SWT.NONE);
+		expectedSumLabel.setText("Sum");
+
+		sumTarget = new Text(sumOfAllGroup, SWT.BORDER);
+		final GridData gd_sumTarget = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		sumTarget.setLayoutData(gd_sumTarget);
+
+		sumModelValue = new Text(sumOfAllGroup, SWT.READ_ONLY | SWT.BORDER);
+		final GridData gd_sumModelValue = new GridData(SWT.FILL, SWT.CENTER,
+				true, false);
+		sumModelValue.setLayoutData(gd_sumModelValue);
+
+		final Label addendsLabel = new Label(sumOfAllGroup, SWT.NONE);
+		addendsLabel.setText("Addends");
+
+		addendsTarget = new ListViewer(sumOfAllGroup, SWT.V_SCROLL | SWT.BORDER);
+		list_1 = addendsTarget.getList();
+		list_1
+				.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true, 1,
+						2));
+
+		addendsModelValue = new ListViewer(sumOfAllGroup, SWT.V_SCROLL
+				| SWT.BORDER);
+		list = addendsModelValue.getList();
+		list.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true, 1, 2));
+
+		final Composite composite = new Composite(sumOfAllGroup, SWT.NONE);
+		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		final GridLayout gridLayout_3 = new GridLayout();
+		gridLayout_3.marginWidth = 0;
+		gridLayout_3.marginHeight = 0;
+		composite.setLayout(gridLayout_3);
+
+		addAddendButton = new Button(composite, SWT.NONE);
+		addAddendButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false,
+				false));
+		addAddendButton.setText("Add");
+
+		removeAddendButton = new Button(composite, SWT.NONE);
+		removeAddendButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,
+				false, false));
+		removeAddendButton.setText("Remove");
+
+		bindUI();
+	}
+
+	private void bindUI() {
+		DataBindingContext dbc = new DataBindingContext();
+
+		bindEvensAndOddsGroup(dbc);
+		bindSumAndAddendsGroup(dbc);
+
+		WizardPageSupport.create(this, dbc);
+	}
+
+	private void bindEvensAndOddsGroup(DataBindingContext dbc) {
+		IObservableValue targetField1 = SWTObservables.observeText(
+				field1Target, SWT.Modify);
+		final IObservableValue middleField1 = new WritableValue(null,
+				Integer.TYPE);
+		dbc.bindValue(targetField1, middleField1);
+
+		IObservableValue targetField2 = SWTObservables.observeText(
+				field2Target, SWT.Modify);
+		final IObservableValue middleField2 = new WritableValue(null,
+				Integer.TYPE);
+		dbc.bindValue(targetField2, middleField2);
+
+		MultiValidator validator = new MultiValidator() {
+			protected IStatus validate() {
+				Integer field1 = (Integer) middleField1.getValue();
+				Integer field2 = (Integer) middleField2.getValue();
+				if (Math.abs(field1.intValue()) % 2 != Math.abs(field2
+						.intValue()) % 2)
+					return ValidationStatus
+							.error("Fields 1 and 2 must be both even or both odd");
+				return null;
+			}
+		};
+		dbc.addValidationStatusProvider(validator);
+
+		IObservableValue modelField1 = new WritableValue(new Integer(1),
+				Integer.TYPE);
+		IObservableValue modelField2 = new WritableValue(new Integer(4),
+				Integer.TYPE);
+		dbc.bindValue(validator.observeValidatedValue(middleField1),
+				modelField1);
+		dbc.bindValue(validator.observeValidatedValue(middleField2),
+				modelField2);
+
+		dbc.bindValue(SWTObservables.observeText(field1ModelValue, SWT.Modify),
+				modelField1);
+		dbc.bindValue(SWTObservables.observeText(field2ModelValue, SWT.Modify),
+				modelField2);
+	}
+
+	private void bindSumAndAddendsGroup(DataBindingContext dbc) {
+		IObservableValue targetSum = SWTObservables.observeText(sumTarget,
+				SWT.Modify);
+		final IObservableValue middleSum = new WritableValue(null, Integer.TYPE);
+		dbc.bindValue(targetSum, middleSum);
+
+		final IObservableList targetAddends = new WritableList(new ArrayList(),
+				Integer.TYPE);
+		addendsTarget.setContentProvider(new ObservableListContentProvider());
+		addendsTarget.setInput(targetAddends);
+
+		addAddendButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(final SelectionEvent e) {
+				InputDialog dialog = new InputDialog(getShell(),
+						"Input addend", "Enter an integer addend", "0",
+						new IInputValidator() {
+							public String isValid(String newText) {
+								try {
+									Integer.valueOf(newText);
+									return null;
+								} catch (NumberFormatException e) {
+									return "Enter a number between "
+											+ Integer.MIN_VALUE + " and "
+											+ Integer.MAX_VALUE;
+								}
+							}
+						});
+				if (dialog.open() == Window.OK) {
+					targetAddends.add(Integer.valueOf(dialog.getValue()));
+				}
+			}
+		});
+
+		removeAddendButton.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection selection = (IStructuredSelection) addendsTarget
+						.getSelection();
+				if (!selection.isEmpty())
+					targetAddends.remove(selection.getFirstElement());
+			}
+		});
+
+		IObservableValue modelSum = new WritableValue(new Integer(5),
+				Integer.TYPE);
+		dbc.bindValue(SWTObservables.observeText(sumModelValue, SWT.Modify),
+				modelSum);
+
+		IObservableList modelAddends = new WritableList(new ArrayList(),
+				Integer.TYPE);
+
+		MultiValidator validator = new MultiValidator() {
+			protected IStatus validate() {
+				Integer sum = (Integer) middleSum.getValue();
+				int actualSum = 0;
+				for (Iterator iterator = targetAddends.iterator(); iterator
+						.hasNext();) {
+					actualSum += ((Integer) iterator.next()).intValue();
+				}
+				if (sum.intValue() != actualSum)
+					return ValidationStatus.error("Sum of addends is "
+							+ actualSum + ", expecting " + sum);
+				return ValidationStatus.ok();
+			}
+		};
+		dbc.addValidationStatusProvider(validator);
+
+		addendsModelValue
+				.setContentProvider(new ObservableListContentProvider());
+		addendsModelValue.setInput(modelAddends);
+
+		dbc.bindValue(validator.observeValidatedValue(middleSum), modelSum);
+		dbc.bindList(validator.observeValidatedList(targetAddends),
+				modelAddends);
+	}
+
+	static class MultiFieldValidationWizard extends Wizard {
+		public void addPages() {
+			addPage(new Snippet021MultiFieldValidation());
+		}
+
+		public String getWindowTitle() {
+			return "Snippet 021 - Multi-field Validation";
+		}
+
+		public boolean performFinish() {
+			return true;
+		}
+	}
+
+	public static void main(String[] args) {
+		Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				IWizard wizard = new MultiFieldValidationWizard();
+				WizardDialog dialog = new WizardDialog(null, wizard);
+				dialog.open();
+
+				// The SWT event loop
+				Display display = Display.getCurrent();
+				while (dialog.getShell() != null
+						&& !dialog.getShell().isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+
+		display.dispose();
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet022ComputedListCombo.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet022ComputedListCombo.java
new file mode 100644
index 0000000..08f6325
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet022ComputedListCombo.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.ComputedList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet022ComputedListCombo {
+	private static WritableList model;
+
+	public static void main(String[] args) {
+		Display display = new Display();
+		final Shell shell = new Shell(display);
+		shell.setLayout(new GridLayout(1, false));
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Snippet022ComputedListCombo snippet = new Snippet022ComputedListCombo();
+				snippet.createModel();
+				snippet.createControls(shell);
+			}
+		});
+
+		shell.pack();
+		shell.open();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+		display.dispose();
+	}
+
+	/**
+	 * 
+	 */
+	protected void createModel() {
+		model = new WritableList();
+		model.add(new Thing("Alice", true, false));
+		model.add(new Thing("Beth", true, false));
+		model.add(new Thing("Cathy", true, false));
+		model.add(new Thing("Arthur", false, true));
+		model.add(new Thing("Bob", false, true));
+		model.add(new Thing("Curtis", false, true));
+		model.add(new Thing("Snail", true, true));
+		model.add(new Thing("Nail", false, false));
+	}
+
+	/**
+	 * @param shell
+	 */
+	protected void createControls(Shell shell) {
+		Composite composite = new Composite(shell, SWT.NONE);
+		Group group = new Group(composite, SWT.NONE);
+		group.setText("Filter");
+		Button male = new Button(group, SWT.CHECK);
+		male.setText("Male");
+		Button female = new Button(group, SWT.CHECK);
+		female.setText("Female");
+		final IObservableValue femaleObservable = SWTObservables
+				.observeSelection(female);
+		final IObservableValue maleObservable = SWTObservables
+				.observeSelection(male);
+		Combo combo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
+		GridDataFactory.defaultsFor(combo).align(SWT.BEGINNING, SWT.BEGINNING)
+				.applyTo(combo);
+		ComboViewer viewer = new ComboViewer(combo);
+		viewer.setContentProvider(new ObservableListContentProvider());
+		// We should really have an out-of-the box filtered list...
+		IObservableList filteredList = new ComputedList() {
+			protected List calculate() {
+				ArrayList result = new ArrayList();
+				for (Iterator it = model.iterator(); it.hasNext();) {
+					Thing thing = (Thing) it.next();
+					if (((Boolean) femaleObservable.getValue()).booleanValue()
+							&& !thing.female)
+						continue;
+					if (((Boolean) maleObservable.getValue()).booleanValue()
+							&& !thing.male)
+						continue;
+					result.add(thing);
+				}
+				return result;
+			}
+		};
+		viewer.setInput(filteredList);
+		GridLayoutFactory.swtDefaults().applyTo(group);
+		GridLayoutFactory.swtDefaults().applyTo(composite);
+	}
+
+	static class Thing {
+		String name;
+		boolean male;
+		boolean female;
+
+		public Thing(String name, boolean female, boolean male) {
+			this.name = name;
+			this.female = female;
+			this.male = male;
+		}
+
+		public String toString() {
+			return name;
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet023ConditionalVisibility.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet023ConditionalVisibility.java
new file mode 100644
index 0000000..3b3f77d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet023ConditionalVisibility.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet023ConditionalVisibility {
+	public static void main(String[] args) {
+		Display display = new Display();
+		final Shell shell = new Shell(display);
+		shell.setLayout(new GridLayout(1, false));
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				new Snippet023ConditionalVisibility().createControls(shell);
+			}
+		});
+
+		shell.pack();
+		shell.open();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+		display.dispose();
+	}
+
+	Text text;
+	Text toText;
+	Text fromText;
+
+	/**
+	 * @param shell
+	 */
+	private void createControls(Shell shell) {
+		Composite composite = new Composite(shell, SWT.NONE);
+		Group radioGroup = new Group(composite, SWT.NONE);
+		radioGroup.setText("Type");
+		Button textButton = new Button(radioGroup, SWT.RADIO);
+		textButton.setText("Text");
+		Button rangeButton = new Button(radioGroup, SWT.RADIO);
+		rangeButton.setText("Range");
+		GridLayoutFactory.swtDefaults().generateLayout(radioGroup);
+
+		final Composite oneOfTwo = new Composite(composite, SWT.NONE);
+		final StackLayout stackLayout = new StackLayout();
+		oneOfTwo.setLayout(stackLayout);
+
+		final Group rangeGroup = new Group(oneOfTwo, SWT.NONE);
+		rangeGroup.setText("Range");
+		Label fromLabel = new Label(rangeGroup, SWT.NONE);
+		fromLabel.setText("From:");
+		fromText = new Text(rangeGroup, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+
+		Label toLabel = new Label(rangeGroup, SWT.NONE);
+		toLabel.setText("To:");
+		toText = new Text(rangeGroup, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+		GridLayoutFactory.swtDefaults().numColumns(2)
+				.generateLayout(rangeGroup);
+
+		final Group textGroup = new Group(oneOfTwo, SWT.NONE);
+		textGroup.setText("Text");
+		Label label = new Label(textGroup, SWT.NONE);
+		label.setText("Text:");
+		text = new Text(textGroup, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
+		GridLayoutFactory.swtDefaults().numColumns(2).generateLayout(textGroup);
+
+		GridLayoutFactory.swtDefaults().numColumns(2).generateLayout(composite);
+
+		final ISWTObservableValue rangeSelected = SWTObservables
+				.observeSelection(rangeButton);
+		final ISWTObservableValue textSelected = SWTObservables
+				.observeSelection(textButton);
+
+		// Note that ControlUpdater is not API.
+		new ControlUpdater(oneOfTwo) {
+			protected void updateControl() {
+				if (((Boolean) rangeSelected.getValue()).booleanValue()) {
+					stackLayout.topControl = rangeGroup;
+					oneOfTwo.layout();
+				} else if (((Boolean) textSelected.getValue()).booleanValue()) {
+					stackLayout.topControl = textGroup;
+					oneOfTwo.layout();
+				}
+			}
+		};
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet024SelectObservableValue.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet024SelectObservableValue.java
new file mode 100644
index 0000000..46a0c62
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet024SelectObservableValue.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 249992)
+ *     Matthew Hall - bug 260329
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.SelectObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Demonstrate usage of SelectObservableValue
+ * 
+ * @since 3.2
+ */
+public class Snippet024SelectObservableValue {
+	protected Shell shell;
+
+	public static void main(String[] args) {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				try {
+					Snippet024SelectObservableValue window = new Snippet024SelectObservableValue();
+					window.open();
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		});
+	}
+
+	public void open() {
+		final Display display = Display.getDefault();
+		createContents();
+		shell.open();
+		shell.layout();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+	}
+
+	protected void createContents() {
+		shell = new Shell();
+		shell.setSize(400, 300);
+		shell.setLayout(new GridLayout(2, true));
+		shell.setText("Snippet024SelectObservableValue");
+
+		final ListViewer listViewer = new ListViewer(shell, SWT.BORDER);
+		listViewer.setContentProvider(new ArrayContentProvider());
+		listViewer.getList().setLayoutData(
+				new GridData(SWT.FILL, SWT.FILL, true, true));
+
+		final Group group = new Group(shell, SWT.NONE);
+		group.setText("Radio Group");
+		group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		group.setLayout(new GridLayout());
+
+		// Data Binding
+		Color[] colors = Color.values();
+
+		listViewer.setInput(colors);
+		IViewerObservableValue listViewerSelection = ViewersObservables
+				.observeSingleSelection(listViewer);
+
+		SelectObservableValue radioGroup = new SelectObservableValue();
+		for (int i = 0; i < colors.length; i++) {
+			Button button = new Button(group, SWT.RADIO);
+			button.setText(colors[i].toString());
+			radioGroup.addOption(colors[i], SWTObservables
+					.observeSelection(button));
+		}
+
+		DataBindingContext dbc = new DataBindingContext();
+		dbc.bindValue(radioGroup, listViewerSelection);
+	}
+
+	public static class Color {
+		public static final Color RED = new Color("Red");
+		public static final Color ORANGE = new Color("Orange");
+		public static final Color YELLOW = new Color("Yellow");
+		public static final Color GREEN = new Color("Green");
+		public static final Color BLUE = new Color("Blue");
+		public static final Color INDIGO = new Color("Indigo");
+		public static final Color VIOLET = new Color("Violet");
+
+		private final String name;
+
+		private Color(String name) {
+			this.name = name;
+		}
+
+		public String toString() {
+			return name;
+		}
+
+		public static Color[] values() {
+			return new Color[] { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO,
+					VIOLET };
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java
new file mode 100644
index 0000000..65772ea
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java
@@ -0,0 +1,272 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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:
+ *     Coconut Palm Software, Inc. - Initial API and implementation
+ *     Matthew Hall - bug 195222, 261843, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+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.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Demonstrates binding a TableViewer to a collection.
+ */
+public class Snippet025TableViewerWithPropertyDerivedColumns {
+	public static void main(String[] args) {
+		final Display display = new Display();
+
+		// Set up data binding. In an RCP application, the threading Realm
+		// will be set for you automatically by the Workbench. In an SWT
+		// application, you can do this once, wrapping your binding
+		// method call.
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				ViewModel viewModel = new ViewModel();
+				Shell shell = new View(viewModel).createShell();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	private static Person UNKNOWN = new Person("unknown", null, null);
+
+	// The data model class. This is normally a persistent class of some sort.
+	static class Person extends AbstractModelObject {
+		// A property...
+		String name = "Donald Duck";
+		Person mother;
+		Person father;
+
+		public Person(String name, Person mother, Person father) {
+			this.name = name;
+			this.mother = mother;
+			this.father = father;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = name);
+		}
+
+		public Person getMother() {
+			return mother;
+		}
+
+		public void setMother(Person mother) {
+			firePropertyChange("mother", this.mother, this.mother = mother);
+		}
+
+		public Person getFather() {
+			return father;
+		}
+
+		public void setFather(Person father) {
+			firePropertyChange("father", this.father, this.father = father);
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// ro retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private IObservableList people = new WritableList();
+		{
+			Person fergus = new Person("Fergus McDuck", UNKNOWN, UNKNOWN);
+			Person downy = new Person("Downy O'Drake", UNKNOWN, UNKNOWN);
+			Person scrooge = new Person("Scrooge McDuck", downy, fergus);
+			Person hortense = new Person("Hortense McDuck", downy, fergus);
+			Person quackmore = new Person("Quackmore Duck", UNKNOWN, UNKNOWN);
+			Person della = new Person("Della Duck", hortense, quackmore);
+			Person donald = new Person("Donald Duck", hortense, quackmore);
+			people.add(UNKNOWN);
+			people.add(downy);
+			people.add(fergus);
+			people.add(scrooge);
+			people.add(quackmore);
+			people.add(hortense);
+			people.add(della);
+			people.add(donald);
+		}
+
+		public IObservableList getPeople() {
+			return people;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Table duckFamily;
+		private Text nameText;
+		private Combo motherCombo;
+		private Combo fatherCombo;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			duckFamily = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
+			duckFamily.setHeaderVisible(true);
+			GridDataFactory.defaultsFor(duckFamily).span(2, 1).applyTo(
+					duckFamily);
+			createColumn("Name");
+			createColumn("Mother");
+			createColumn("Father");
+			createColumn("Maternal Grandmother");
+			createColumn("Maternal Grandfather");
+			createColumn("Paternal Grandmother");
+			createColumn("Paternal Grandfather");
+
+			duckFamily.setLinesVisible(true);
+
+			new Label(shell, SWT.NONE).setText("Name:");
+			nameText = new Text(shell, SWT.BORDER);
+			GridDataFactory.defaultsFor(nameText).grab(true, false).applyTo(
+					nameText);
+
+			new Label(shell, SWT.NONE).setText("Mother:");
+			motherCombo = new Combo(shell, SWT.READ_ONLY);
+
+			new Label(shell, SWT.NONE).setText("Father:");
+			fatherCombo = new Combo(shell, SWT.READ_ONLY);
+
+			DataBindingContext bindingContext = new DataBindingContext();
+			bindGUI(bindingContext);
+
+			GridLayoutFactory.swtDefaults().numColumns(2).applyTo(shell);
+			// Open and return the Shell
+			shell.setSize(800, 300);
+			shell.open();
+			return shell;
+		}
+
+		private void createColumn(String string) {
+			final TableColumn column = new TableColumn(duckFamily, SWT.NONE);
+			column.setText(string);
+			column.pack();
+			if (column.getWidth() < 100)
+				column.setWidth(100);
+		}
+
+		protected void bindGUI(DataBindingContext dbc) {
+			// Since we're using a JFace Viewer, we do first wrap our Table...
+			TableViewer peopleViewer = new TableViewer(duckFamily);
+			peopleViewer.addFilter(new ViewerFilter() {
+				public boolean select(Viewer viewer, Object parentElement,
+						Object element) {
+					return element != UNKNOWN;
+				}
+			});
+
+			ViewerSupport.bind(peopleViewer, viewModel.getPeople(),
+					BeanProperties.values(Person.class, new String[] { "name",
+							"mother.name", "father.name", "mother.mother.name",
+							"mother.father.name", "father.mother.name",
+							"father.father.name" }));
+
+			IObservableValue masterSelection = ViewerProperties
+					.singleSelection().observe(peopleViewer);
+
+			dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(nameText),
+					BeanProperties.value(Person.class, "name").observeDetail(
+							masterSelection));
+
+			ComboViewer mothercomboViewer = new ComboViewer(motherCombo);
+			ViewerSupport.bind(mothercomboViewer, viewModel.getPeople(),
+					BeanProperties.value(Person.class, "name"));
+
+			dbc.bindValue(ViewerProperties.singleSelection().observe(
+					mothercomboViewer), BeanProperties.value(Person.class,
+					"mother").observeDetail(masterSelection));
+
+			ComboViewer fatherComboViewer = new ComboViewer(fatherCombo);
+			ViewerSupport.bind(fatherComboViewer, viewModel.getPeople(),
+					BeanProperties.value(Person.class, "name"));
+
+			dbc.bindValue(ViewerProperties.singleSelection().observe(
+					fatherComboViewer), BeanProperties.value(Person.class,
+					"father").observeDetail(masterSelection));
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet026AnonymousBeanProperties.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet026AnonymousBeanProperties.java
new file mode 100644
index 0000000..3fa6a97
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet026AnonymousBeanProperties.java
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 247997)
+ *     Matthew Hall - bugs 261843, 260337, 265561
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.INativePropertyListener;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.NativePropertyListener;
+import org.eclipse.core.databinding.property.set.DelegatingSetProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.set.SimpleSetProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet026AnonymousBeanProperties {
+	private ComboViewer statusViewer;
+	private Combo combo;
+	private Text nameText;
+	private TreeViewer contactViewer;
+
+	public static void main(String[] args) {
+		Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				try {
+					Snippet026AnonymousBeanProperties window = new Snippet026AnonymousBeanProperties();
+					window.open();
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		});
+	}
+
+	private ApplicationModel model;
+	private Shell shell;
+	private Tree tree;
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	public static class ContactGroup extends AbstractModelObject implements
+			Comparable {
+		private String name;
+		private Set contacts = new TreeSet();
+
+		ContactGroup(String name) {
+			this.name = checkNull(name);
+		}
+
+		private String checkNull(String string) {
+			if (string == null)
+				throw new NullPointerException();
+			return string;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = checkNull(name));
+		}
+
+		public Set getContacts() {
+			return new TreeSet(contacts);
+		}
+
+		public void addContact(Contact contact) {
+			Set oldValue = getContacts();
+			contacts.add(contact);
+			Set newValue = getContacts();
+			firePropertyChange("contacts", oldValue, newValue);
+		}
+
+		public void removeContact(Contact contact) {
+			Set oldValue = getContacts();
+			contacts.remove(contact);
+			Set newValue = getContacts();
+			firePropertyChange("contacts", oldValue, newValue);
+		}
+
+		public int compareTo(Object o) {
+			ContactGroup that = (ContactGroup) o;
+			return this.name.compareTo(that.name);
+		}
+	}
+
+	public static class Contact extends AbstractModelObject implements
+			Comparable {
+		private String name;
+		private String status;
+
+		private String checkNull(String string) {
+			if (string == null)
+				throw new NullPointerException();
+			return string;
+		}
+
+		public Contact(String name, String status) {
+			this.name = checkNull(name);
+			this.status = checkNull(status);
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = checkNull(name));
+		}
+
+		public String getStatus() {
+			return status;
+		}
+
+		public void setStatus(String status) {
+			firePropertyChange("status", this.status,
+					this.status = checkNull(status));
+		}
+
+		public int compareTo(Object o) {
+			Contact that = (Contact) o;
+			int result = this.name.compareTo(that.name);
+			if (result == 0)
+				result = this.status.compareTo(that.status);
+			return result;
+		}
+	}
+
+	public static class ApplicationModel extends AbstractModelObject {
+		private Set groups = new TreeSet();
+
+		public Set getGroups() {
+			return new TreeSet(groups);
+		}
+
+		public void setGroups(Set groups) {
+			Set oldValue = getGroups();
+			this.groups = new TreeSet(groups);
+			Set newValue = getGroups();
+			firePropertyChange("groups", oldValue, newValue);
+		}
+	}
+
+	/**
+	 * Set property for the "contacts" property of a ContactGroup. Since
+	 * ContactGroup does not have a setContacts() method we have to write our
+	 * own property to apply set changes incrementally through the addContact
+	 * and removeContact methods.
+	 */
+	public static class ContactGroupContactsProperty extends SimpleSetProperty {
+		public Object getElementType() {
+			return Contact.class;
+		}
+
+		protected Set doGetSet(Object source) {
+			if (source == null)
+				return Collections.EMPTY_SET;
+			return ((ContactGroup) source).getContacts();
+		}
+
+		protected void doSetSet(Object source, Set set, SetDiff diff) {
+			doUpdateSet(source, diff);
+		}
+
+		@Override
+		protected void doUpdateSet(Object source, SetDiff diff) {
+			ContactGroup group = (ContactGroup) source;
+			for (Iterator it = diff.getRemovals().iterator(); it.hasNext();) {
+				Contact contact = (Contact) it.next();
+				group.removeContact(contact);
+			}
+			for (Iterator it = diff.getAdditions().iterator(); it.hasNext();) {
+				Contact contact = (Contact) it.next();
+				group.addContact(contact);
+			}
+		}
+
+		public INativePropertyListener adaptListener(
+				final ISimplePropertyListener listener) {
+			return new Listener(this, listener);
+		}
+
+		private class Listener extends NativePropertyListener implements
+				PropertyChangeListener {
+			Listener(IProperty property, ISimplePropertyListener listener) {
+				super(property, listener);
+			}
+
+			public void propertyChange(PropertyChangeEvent evt) {
+				fireChange(evt.getSource(), null);
+			}
+
+			protected void doAddTo(Object source) {
+				((ContactGroup) source).addPropertyChangeListener("contacts",
+						this);
+			}
+
+			protected void doRemoveFrom(Object source) {
+				((ContactGroup) source).removePropertyChangeListener(
+						"contacts", this);
+			}
+		}
+	}
+
+	public void open() {
+		model = createDefaultModel();
+
+		final Display display = Display.getDefault();
+		createContents();
+		bindUI();
+		shell.open();
+		shell.layout();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch())
+				display.sleep();
+		}
+	}
+
+	private static final String[] statuses = new String[] { "Online", "Idle",
+			"Busy", "Offline" };
+
+	/**
+	 * @return
+	 */
+	private ApplicationModel createDefaultModel() {
+		ContactGroup swtGroup = new ContactGroup("SWT");
+		swtGroup.addContact(new Contact("Steve Northover", "Busy"));
+		swtGroup.addContact(new Contact("Grant Gayed", "Online"));
+		swtGroup.addContact(new Contact("Veronika Irvine", "Offline"));
+		swtGroup.addContact(new Contact("Mike Wilson", "Online"));
+		swtGroup.addContact(new Contact("Christophe Cornu", "Idle"));
+		swtGroup.addContact(new Contact("Lynne Kues", "Online"));
+		swtGroup.addContact(new Contact("Silenio Quarti", "Idle"));
+
+		ContactGroup jdbGroup = new ContactGroup("JFace Data Binding");
+		jdbGroup.addContact(new Contact("Boris Bokowski", "Online"));
+		jdbGroup.addContact(new Contact("Matthew Hall", "Idle"));
+
+		Set groups = new TreeSet();
+		groups.add(swtGroup);
+		groups.add(jdbGroup);
+		ApplicationModel model = new ApplicationModel();
+		model.setGroups(groups);
+
+		return model;
+	}
+
+	/**
+	 * Create contents of the window
+	 */
+	protected void createContents() {
+		shell = new Shell();
+		shell.setSize(379, 393);
+		shell.setText("Snippet026AnonymousBeanProperties");
+		final GridLayout gridLayout = new GridLayout();
+		gridLayout.numColumns = 4;
+		shell.setLayout(gridLayout);
+
+		contactViewer = new TreeViewer(shell, SWT.BORDER);
+		tree = contactViewer.getTree();
+		tree.setHeaderVisible(true);
+		tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
+
+		final TreeColumn nameColumn = new TreeColumn(tree, SWT.NONE);
+		nameColumn.setWidth(163);
+		nameColumn.setText("Name");
+
+		final TreeColumn newColumnTreeColumn = new TreeColumn(tree, SWT.NONE);
+		newColumnTreeColumn.setWidth(100);
+		newColumnTreeColumn.setText("Status");
+
+		final Label nameLabel = new Label(shell, SWT.NONE);
+		nameLabel.setText("Name");
+
+		nameText = new Text(shell, SWT.BORDER);
+		final GridData gd_nameText = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		nameText.setLayoutData(gd_nameText);
+
+		final Label statusLabel = new Label(shell, SWT.NONE);
+		statusLabel.setLayoutData(new GridData());
+		statusLabel.setText("Status");
+
+		statusViewer = new ComboViewer(shell, SWT.READ_ONLY);
+		combo = statusViewer.getCombo();
+		combo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+	}
+
+	private void bindUI() {
+		ISetProperty treeChildrenProperty = new DelegatingSetProperty() {
+			ISetProperty modelGroups = BeanProperties.set(
+					ApplicationModel.class, "groups");
+			ISetProperty groupContacts = BeanProperties.set(ContactGroup.class,
+					"contacts");
+
+			protected ISetProperty doGetDelegate(Object source) {
+				if (source instanceof ApplicationModel)
+					return modelGroups;
+				if (source instanceof ContactGroup)
+					return groupContacts;
+				return null;
+			}
+		};
+
+		ViewerSupport.bind(contactViewer, model, treeChildrenProperty,
+				BeanProperties.values(new String[] { "name", "status" }));
+
+		contactViewer.expandAll();
+
+		final IObservableValue selection = ViewerProperties.singleSelection()
+				.observe(contactViewer);
+
+		DataBindingContext dbc = new DataBindingContext();
+
+		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(nameText),
+				BeanProperties.value("name").observeDetail(selection));
+
+		statusViewer.setContentProvider(new ArrayContentProvider());
+		statusViewer.setInput(statuses);
+
+		dbc.bindValue(ViewerProperties.singleSelection().observe(statusViewer),
+				BeanProperties.value("status").observeDetail(selection));
+
+		dbc.bindValue(WidgetProperties.enabled().observe(
+				statusViewer.getControl()), new ComputedValue() {
+			protected Object calculate() {
+				return Boolean.valueOf(selection.getValue() instanceof Contact);
+			}
+		});
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet027ExternalValidator.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet027ExternalValidator.java
new file mode 100644
index 0000000..dae6dd6
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet027ExternalValidator.java
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Code 9 Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 Aniszczyk <zx@code9.com> - initial API and implementation
+ *     Boris Bokowski, IBM - minor changes
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.validation.MultiValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * This snippet demonstrates how to integrate an external validator
+ * 
+ * @since 3.5
+ */
+public class Snippet027ExternalValidator extends WizardPage {
+
+	private Text nameValue;
+	private Text emailValue;
+	private Text phoneNumberValue;
+
+	private Contact contact;
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	static class Contact extends AbstractModelObject {
+		String name;
+		String email;
+		String phoneNumber;
+
+		public Contact(String name, String email, String number) {
+			this.name = name;
+			this.email = email;
+			this.phoneNumber = number;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			String oldValue = this.name;
+			this.name = name;
+			firePropertyChange("name", oldValue, name);
+		}
+
+		public String getEmail() {
+			return email;
+		}
+
+		public void setEmail(String email) {
+			String oldValue = this.email;
+			this.email = email;
+			firePropertyChange("email", oldValue, email);
+		}
+
+		public String getPhoneNumber() {
+			return phoneNumber;
+		}
+
+		public void setPhoneNumber(String number) {
+			String oldValue = this.phoneNumber;
+			this.phoneNumber = number;
+			firePropertyChange("phoneNumber", oldValue, number);
+		}
+
+		public IStatus validate() {
+			if (name.indexOf(' ') == -1) {
+				return ValidationStatus
+						.error("Please enter both first and last name separated by a space.");
+			}
+			if (email.indexOf('@') == -1) {
+				return ValidationStatus
+				.error("Please enter a valid email address containing '@'.");
+			}
+			if (!phoneNumber.startsWith("+")) {
+				return ValidationStatus
+				.error("Please enter the phone number in international format starting with '+'.");
+			}
+			return Status.OK_STATUS;
+		}
+
+	}
+
+	/**
+	 * Create the wizard
+	 */
+	public Snippet027ExternalValidator() {
+		super("snippet024");
+		setTitle("Snippet 024 - External Validation");
+		setDescription("Please enter contact details.");
+	}
+
+	/**
+	 * Create contents of the wizard
+	 * 
+	 * @param parent
+	 */
+	public void createControl(Composite parent) {
+		Composite container = new Composite(parent, SWT.NULL);
+		final GridLayout gridLayout = new GridLayout();
+		gridLayout.numColumns = 2;
+		container.setLayout(gridLayout);
+		setControl(container);
+
+		final Label nameLabel = new Label(container, SWT.NONE);
+		nameLabel.setText("Name");
+
+		nameValue = new Text(container, SWT.BORDER);
+		final GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
+		nameValue.setLayoutData(gd);
+
+		final Label emailLabel = new Label(container, SWT.NONE);
+		emailLabel.setText("Email");
+
+		emailValue = new Text(container, SWT.BORDER);
+		emailValue.setLayoutData(gd);
+
+		final Label phoneLabel = new Label(container, SWT.NONE);
+		phoneLabel.setText("Phone");
+
+		phoneNumberValue = new Text(container, SWT.BORDER);
+		phoneNumberValue.setLayoutData(gd);
+
+		contact = new Contact("BorisBokowski", "boris.at.somecompany.com",
+				"1-123-456-7890");
+
+		bindUI();
+	}
+
+	private void bindUI() {
+		DataBindingContext dbc = new DataBindingContext();
+
+		final IObservableValue name = BeansObservables.observeValue(contact,
+				"name");
+		dbc.bindValue(SWTObservables.observeText(nameValue, SWT.Modify), name,
+				null, null);
+
+		final IObservableValue email = BeansObservables.observeValue(contact,
+				"email");
+		dbc.bindValue(SWTObservables.observeText(emailValue, SWT.Modify),
+				email, null, null);
+
+		final IObservableValue phone = BeansObservables.observeValue(contact,
+				"phoneNumber");
+		dbc.bindValue(SWTObservables.observeText(phoneNumberValue, SWT.Modify),
+				phone, null, null);
+
+		MultiValidator validator = new MultiValidator() {
+			protected IStatus validate() {
+
+				// Everything accessed here will trigger re-validation.
+				name.getValue();
+				email.getValue();
+				phone.getValue();
+
+				System.out.println("Validating...");
+
+				return contact.validate();
+			}
+		};
+		dbc.addValidationStatusProvider(validator);
+
+		WizardPageSupport.create(this, dbc);
+	}
+
+	static class ExternalValidationWizard extends Wizard {
+		public void addPages() {
+			addPage(new Snippet027ExternalValidator());
+		}
+
+		public String getWindowTitle() {
+			return "Snippet 024 - External Validation";
+		}
+
+		public boolean performFinish() {
+			return true;
+		}
+	}
+
+	public static void main(String[] args) {
+		Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				IWizard wizard = new ExternalValidationWizard();
+				WizardDialog dialog = new WizardDialog(null, wizard);
+				dialog.open();
+
+				// The SWT event loop
+				Display display = Display.getCurrent();
+				while (dialog.getShell() != null
+						&& !dialog.getShell().isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+
+		display.dispose();
+	}
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet028DuplexingObservableValue.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet028DuplexingObservableValue.java
new file mode 100644
index 0000000..15583fe
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet028DuplexingObservableValue.java
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 175735)
+ *     Matthew Hall - bugs 262407, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+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.value.DuplexingObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet028DuplexingObservableValue {
+	protected Shell shell;
+	private TableViewer viewer;
+	private Table table;
+	private Text releaseDate;
+	private Text title;
+	private Text director;
+	private Text writer;
+
+	/**
+	 * Launch the application
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		try {
+			Snippet028DuplexingObservableValue window = new Snippet028DuplexingObservableValue();
+			window.open();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Open the window
+	 */
+	public void open() {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				createContents();
+				shell.open();
+				shell.layout();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+	}
+
+	protected void createContents() {
+		shell = new Shell();
+		shell.setSize(509, 375);
+		shell.setText("Snippet028DuplexingObservableValue.java");
+		final GridLayout gridLayout = new GridLayout();
+		gridLayout.makeColumnsEqualWidth = true;
+		gridLayout.numColumns = 4;
+		shell.setLayout(gridLayout);
+
+		viewer = new TableViewer(shell, SWT.FULL_SELECTION | SWT.MULTI
+				| SWT.BORDER);
+		table = viewer.getTable();
+		table.setLinesVisible(true);
+		table.setHeaderVisible(true);
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 4, 1));
+
+		final TableColumn newColumnTableColumn_1 = new TableColumn(table,
+				SWT.NONE);
+		newColumnTableColumn_1.setWidth(120);
+		newColumnTableColumn_1.setText("Movie");
+
+		final TableColumn newColumnTableColumn = new TableColumn(table,
+				SWT.NONE);
+
+		newColumnTableColumn.setWidth(120);
+		newColumnTableColumn.setText("Release Date");
+
+		final TableColumn newColumnTableColumn_2 = new TableColumn(table,
+				SWT.NONE);
+		newColumnTableColumn_2.setWidth(120);
+		newColumnTableColumn_2.setText("Director");
+
+		final TableColumn newColumnTableColumn_3 = new TableColumn(table,
+				SWT.NONE);
+		newColumnTableColumn_3.setWidth(120);
+		newColumnTableColumn_3.setText("Writer");
+
+		final Label movieLabel = new Label(shell, SWT.NONE);
+		movieLabel.setText("Movie");
+
+		final Label directorLabel = new Label(shell, SWT.NONE);
+		directorLabel.setLayoutData(new GridData());
+		directorLabel.setText("Release Date");
+
+		final Label producerLabel = new Label(shell, SWT.NONE);
+		producerLabel.setText("Director");
+
+		final Label scoreLabel = new Label(shell, SWT.NONE);
+		scoreLabel.setText("Writer");
+
+		title = new Text(shell, SWT.BORDER);
+		final GridData gd_title = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		title.setLayoutData(gd_title);
+
+		releaseDate = new Text(shell, SWT.BORDER);
+		final GridData gd_releaseDate = new GridData(SWT.FILL, SWT.CENTER,
+				true, false);
+		releaseDate.setLayoutData(gd_releaseDate);
+
+		director = new Text(shell, SWT.BORDER);
+		final GridData gd_director = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		director.setLayoutData(gd_director);
+
+		writer = new Text(shell, SWT.BORDER);
+		final GridData gd_writer = new GridData(SWT.FILL, SWT.CENTER, true,
+				false);
+		writer.setLayoutData(gd_writer);
+
+		bindUI();
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	public static class MovieInfo extends AbstractModelObject {
+		private String title;
+		private String releaseDate;
+		private String director;
+		private String writer;
+
+		public MovieInfo(String title, String releaseDate, String director,
+				String writer) {
+			this.title = title;
+			this.releaseDate = releaseDate;
+			this.director = director;
+			this.writer = writer;
+		}
+
+		public String getTitle() {
+			return title;
+		}
+
+		public void setTitle(String title) {
+			firePropertyChange("title", this.title, this.title = title);
+		}
+
+		public String getReleaseDate() {
+			return releaseDate;
+		}
+
+		public void setReleaseDate(String releaseDate) {
+			firePropertyChange("releaseDate", this.releaseDate,
+					this.releaseDate = releaseDate);
+		}
+
+		public String getDirector() {
+			return director;
+		}
+
+		public void setDirector(String director) {
+			firePropertyChange("director", this.director,
+					this.director = director);
+		}
+
+		public String getWriter() {
+			return writer;
+		}
+
+		public void setWriter(String writer) {
+			firePropertyChange("writer", this.writer, this.writer = writer);
+		}
+	}
+
+	private void bindUI() {
+		IObservableList movies = new WritableList();
+		movies.add(new MovieInfo("007: Quantum of Solace", "October 31, 2008",
+				"Marc Forster", "Robert Wade"));
+		movies.add(new MovieInfo("Batman Begins", "June 15, 2005",
+				"Christopher Nolan", "David S. Goyer"));
+		movies.add(new MovieInfo("Cloverfield", "January 18, 2008",
+				"Matt Reeves", "Drew Goddard"));
+		movies.add(new MovieInfo("The Dark Knight", "July 18, 2008",
+				"Christopher Nolan", "David S. Goyer"));
+		movies.add(new MovieInfo("Finding Nemo", "May 30, 2003",
+				"Andrew Stanton", "Andrew Stanton"));
+		movies.add(new MovieInfo("Get Smart", "June 20, 2008", "Peter Segal",
+				"Tom J. Astle"));
+		movies.add(new MovieInfo(
+				"Indiana Jones and the Kingdom of the Crystal Skull",
+				"May 22, 2008", "Steven Spielberg", "Drunken Lemurs"));
+		movies.add(new MovieInfo("Iron Man", "May 2, 2008", "Jon Favreau",
+				"Mark Fergus"));
+		movies.add(new MovieInfo("Raiders of the Lost Ark", "June 12, 1981",
+				"Steven Spielberg", "George Lucas"));
+		movies.add(new MovieInfo("Valkyrie", "December 25, 2008",
+				"Bryan Singer", "Christopher McQuarrie"));
+		movies.add(new MovieInfo("Wall-E", "June 27, 2008", "Andrew Stanton",
+				"Andrew Stanton"));
+		movies.add(new MovieInfo("Wanted", "June 27, 2008",
+				"Timur Bekmambetov", "Michael Brandt"));
+
+		ViewerSupport.bind(viewer, movies, BeanProperties.values(
+				MovieInfo.class, new String[] { "title", "releaseDate",
+						"director", "writer" }));
+
+		// Select Batman Begins and The Dark Knight, which have the same
+		// director and writer
+		viewer.setSelection(new StructuredSelection(new Object[] {
+				movies.get(1), movies.get(3) }));
+
+		DataBindingContext dbc = new DataBindingContext();
+
+		IObservableList selections = ViewerProperties.multipleSelection()
+				.observe(viewer);
+		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(title),
+				DuplexingObservableValue.withDefaults(BeanProperties.value(
+						"title").observeDetail(selections), "",
+						"<Multiple titles>"));
+		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(releaseDate),
+				DuplexingObservableValue.withDefaults(BeanProperties.value(
+						"releaseDate").observeDetail(selections), "",
+						"<Multiple dates>"));
+		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(director),
+				DuplexingObservableValue.withDefaults(BeanProperties.value(
+						"director").observeDetail(selections), "",
+						"<Multiple directors>"));
+		dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(writer),
+				DuplexingObservableValue.withDefaults(BeanProperties.value(
+						"writer").observeDetail(selections), "",
+						"<Multiple writers>"));
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet029TreeViewerMultiListProperty.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet029TreeViewerMultiListProperty.java
new file mode 100644
index 0000000..b539af9
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet029TreeViewerMultiListProperty.java
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 175735)
+ *     Matthew Hall - bugs 262407, 260337
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.list.MultiListProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet029TreeViewerMultiListProperty {
+	protected Shell shell;
+	private TreeViewer viewer;
+
+	/**
+	 * Launch the application
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		try {
+			Snippet029TreeViewerMultiListProperty window = new Snippet029TreeViewerMultiListProperty();
+			window.open();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Open the window
+	 */
+	public void open() {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				createContents();
+				shell.open();
+				shell.layout();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+	}
+
+	protected void createContents() {
+		shell = new Shell();
+		shell.setSize(509, 375);
+		shell.setText("Snippet028DuplexingObservableValue.java");
+		final GridLayout gridLayout = new GridLayout();
+		gridLayout.makeColumnsEqualWidth = true;
+		gridLayout.numColumns = 4;
+		shell.setLayout(new FillLayout());
+
+		viewer = new TreeViewer(shell, SWT.FULL_SELECTION | SWT.MULTI
+				| SWT.BORDER);
+
+		bindUI();
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	public static class Catalog extends AbstractModelObject {
+		private String name;
+		private List catalogs = new ArrayList();
+		private List items = new ArrayList();
+
+		public Catalog(String name) {
+			this.name = name;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = name);
+		}
+
+		public List getCatalogs() {
+			return catalogs;
+		}
+
+		public void setCatalogs(List catalogs) {
+			firePropertyChange("catalogs", this.catalogs,
+					this.catalogs = catalogs);
+		}
+
+		public List getItems() {
+			return items;
+		}
+
+		public void setItems(List items) {
+			firePropertyChange("items", this.items, this.items = items);
+		}
+	}
+
+	public static class CatalogItem extends AbstractModelObject {
+		private String name;
+
+		public CatalogItem(String name) {
+			this.name = name;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = name);
+		}
+	}
+
+	private void bindUI() {
+		List items;
+
+		Catalog fruits = new Catalog("Fruits");
+		items = new ArrayList();
+		items.add(new CatalogItem("Apple"));
+		items.add(new CatalogItem("Orange"));
+		fruits.setCatalogs(items);
+
+		Catalog vegetables = new Catalog("Vegetables");
+		items = new ArrayList();
+		items.add(new CatalogItem("Peas"));
+		items.add(new CatalogItem("Carrots"));
+		items.add(new CatalogItem("Potatoes"));
+		vegetables.setItems(items);
+
+		Catalog foods = new Catalog("Foods");
+		items = new ArrayList();
+		items.add(fruits);
+		items.add(vegetables);
+		foods.setCatalogs(items);
+
+		items = new ArrayList();
+		items.add(new CatalogItem("Own Hand"));
+		foods.setItems(items);
+
+		Catalog catalog = new Catalog("Main Catalog");
+		items = new ArrayList();
+		items.add(foods);
+		catalog.setCatalogs(items);
+
+		IListProperty childrenProperty = new MultiListProperty(
+				new IListProperty[] { BeanProperties.list("catalogs"),
+						BeanProperties.list("items") });
+
+		ObservableListTreeContentProvider contentProvider = new ObservableListTreeContentProvider(
+				childrenProperty.listFactory(), null);
+		viewer.setContentProvider(contentProvider);
+
+		ObservableMapLabelProvider labelProvider = new ObservableMapLabelProvider(
+				BeanProperties.value("name").observeDetail(
+						contentProvider.getKnownElements())) {
+			Image catalogImage = createCatalogImage();
+			Image catalogItemImage = createCatalogItemImage();
+
+			public Image getImage(Object element) {
+				if (element instanceof Catalog)
+					return catalogImage;
+				if (element instanceof CatalogItem)
+					return catalogItemImage;
+				return super.getImage(element);
+			}
+
+			public void dispose() {
+				catalogImage.dispose();
+				catalogItemImage.dispose();
+				super.dispose();
+			}
+
+			private Image createCatalogImage() {
+				Display display = Display.getCurrent();
+				Image catalogImage = new Image(display, 12, 12);
+				GC gc = new GC(catalogImage);
+				gc.setBackground(display.getSystemColor(SWT.COLOR_GREEN));
+				gc.fillArc(1, 1, 10, 10, 0, 360);
+				gc.dispose();
+				return catalogImage;
+			}
+
+			private Image createCatalogItemImage() {
+				Display display = Display.getCurrent();
+				Image catalogImage = new Image(display, 12, 12);
+				GC gc = new GC(catalogImage);
+				gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
+				gc.fillPolygon(new int[] { 1, 10, 5, 1, 6, 1, 10, 10, 1, 10 });
+				gc.dispose();
+				return catalogImage;
+			}
+		};
+		viewer.setLabelProvider(labelProvider);
+
+		viewer.setInput(catalog);
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet030DateAndTimeObservableValue.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet030DateAndTimeObservableValue.java
new file mode 100644
index 0000000..baffa2d
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet030DateAndTimeObservableValue.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 169876)
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Date;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.DateAndTimeObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet030DateAndTimeObservableValue {
+	protected Shell shell;
+	private Text modelText;
+	private DateTime date;
+	private DateTime calendar;
+	private DateTime time;
+	private Button syncTime;
+
+	/**
+	 * Launch the application
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		try {
+			Snippet030DateAndTimeObservableValue window = new Snippet030DateAndTimeObservableValue();
+			window.open();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Open the window
+	 */
+	public void open() {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				createContents();
+				shell.pack();
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+	}
+
+	protected void createContents() {
+		shell = new Shell();
+		GridLayout layout = new GridLayout();
+		layout.numColumns = 3;
+		shell.setLayout(layout);
+		shell.setText("Snippet030DateAndTimeObservableValue.java");
+
+		new Label(shell, SWT.NONE).setText("Model date + time");
+		modelText = new Text(shell, SWT.BORDER);
+		modelText.setEditable(false);
+		modelText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
+				2, 1));
+
+		new Label(shell, SWT.NONE).setText("Target date (SWT.DATE)");
+		date = new DateTime(shell, SWT.DATE | SWT.BORDER);
+		date.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2,
+				1));
+
+		new Label(shell, SWT.NONE).setText("Target date (SWT.CALENDAR)");
+		calendar = new DateTime(shell, SWT.CALENDAR);
+		calendar.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
+				2, 1));
+
+		new Label(shell, SWT.NONE).setText("Target time");
+		time = new DateTime(shell, SWT.TIME | SWT.BORDER);
+
+		syncTime = new Button(shell, SWT.CHECK);
+		syncTime.setLayoutData(new GridData());
+		syncTime.setText("Sync with system time");
+
+		bindUI();
+	}
+
+	private void bindUI() {
+		DataBindingContext dbc = new DataBindingContext();
+
+		IObservableValue model = WritableValue.withValueType(Date.class);
+		model.setValue(new Date());
+
+		dbc.bindValue(WidgetProperties.text().observe(modelText), model);
+
+		final IObservableValue timeSelection = WidgetProperties.selection()
+				.observe(time);
+
+		dbc.bindValue(new DateAndTimeObservableValue(WidgetProperties
+				.selection().observe(date), timeSelection), model);
+		dbc.bindValue(new DateAndTimeObservableValue(WidgetProperties
+				.selection().observe(calendar), timeSelection), model);
+
+		syncTime.addListener(SWT.Selection, new Listener() {
+			Runnable runnable = new Runnable() {
+				public void run() {
+					if (syncTime.getSelection()) {
+						timeSelection.setValue(new Date());
+						Display.getCurrent().timerExec(100, this);
+					}
+				}
+			};
+
+			public void handleEvent(Event event) {
+				time.setEnabled(!syncTime.getSelection());
+				if (syncTime.getSelection()) {
+					runnable.run();
+				}
+			}
+		});
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet031JFaceObservable.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet031JFaceObservable.java
new file mode 100644
index 0000000..263e990
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet031JFaceObservable.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.util.JFaceProperties;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class Snippet031JFaceObservable {
+
+	public static final String NAME_PROPERTY = "name_property";
+
+	public static void main(String[] args) {
+		Display display = new Display();
+		final ViewModel viewModel = new ViewModel();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				final Shell shell = new View(viewModel).createShell();
+				// The SWT event loop
+				Display display = Display.getCurrent();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+		// Print the results
+		System.out.println("person.getName() = "
+				+ viewModel.getPerson().getName());
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	// 
+	// In this example, we extend the EventManager class
+	// to manage our listeners and we fire a property change
+	// event when the object state changes.
+	public static class Person extends EventManager {
+		// A property...
+		String name = "HelloWorld";
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			fireChange(new PropertyChangeEvent(this, NAME_PROPERTY, this.name,
+					this.name = name));
+		}
+
+		public void addPropertyChangeListener(IPropertyChangeListener listener) {
+			addListenerObject(listener);
+		}
+
+		public void removePropertyChangeListener(
+				IPropertyChangeListener listener) {
+			removeListenerObject(listener);
+		}
+
+		private void fireChange(PropertyChangeEvent event) {
+			final Object[] list = getListeners();
+			for (int i = 0; i < list.length; ++i) {
+				((IPropertyChangeListener) list[i]).propertyChange(event);
+			}
+		}
+
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// DAO. Since this snippet doesn't have any persistent objects to
+	// retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private Person person = new Person();
+
+		public Person getPerson() {
+			return person;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Text name;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			shell.setLayout(new RowLayout(SWT.VERTICAL));
+			name = new Text(shell, SWT.BORDER);
+
+			// Bind it
+			DataBindingContext bindingContext = new DataBindingContext();
+			Person person = viewModel.getPerson();
+
+			IValueProperty nameProperty = JFaceProperties.value(Person.class,
+					"name", NAME_PROPERTY);
+
+			bindingContext.bindValue(SWTObservables.observeText(name,
+					SWT.Modify), nameProperty.observe(person), null, null);
+
+			Label label = new Label(shell, SWT.NONE);
+			bindingContext.bindValue(SWTObservables.observeText(label),
+					nameProperty.observe(person), null, null);
+
+			// Open and return the Shell
+			shell.pack();
+			shell.open();
+			return shell;
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet032TableViewerColumnEditing.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet032TableViewerColumnEditing.java
new file mode 100644
index 0000000..d5607cc
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet032TableViewerColumnEditing.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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
+ *     Tom Schindl - cell editing
+ *     Matthew Hall - bugs 260329, 260337
+ *     Heiko Ahlig - bug 267712
+ *******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanValueProperty;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.Properties;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.databinding.viewers.CellEditorProperties;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ObservableMapCellLabelProvider;
+import org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+/**
+ * Demonstrates binding a TableViewer with multiple columns to a collection.
+ */
+public class Snippet032TableViewerColumnEditing {
+	public static void main(String[] args) {
+		final Display display = new Display();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				ViewModel viewModel = new ViewModel();
+				Shell shell = new View(viewModel).createShell();
+
+				// The SWT event loop
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	static class Person extends AbstractModelObject {
+		// A property...
+		String name;
+		String firstName;
+
+		public Person(String firstName, String name) {
+			this.name = name;
+			this.firstName = firstName;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			firePropertyChange("name", this.name, this.name = name);
+		}
+
+		public String getFirstName() {
+			return firstName;
+		}
+
+		public void setFirstName(String firstName) {
+			firePropertyChange("firstName", this.firstName,
+					this.firstName = firstName);
+		}
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// ro retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private List people = new LinkedList();
+		{
+			people.add(new Person("Dave", "Orme"));
+			people.add(new Person("Gili", "Mendel"));
+			people.add(new Person("Joe", "Winchester"));
+			people.add(new Person("Boris", "Bokowski"));
+			people.add(new Person("Brad", "Reynolds"));
+			people.add(new Person("Matthew", "Hall"));
+		}
+
+		public List getPeople() {
+			return people;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+		private Table committers;
+		private Label selectedCommitterName;
+		private Label selectedCommitterFirstName;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+			shell.setLayout(new GridLayout(2, true));
+			committers = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
+			committers.setLinesVisible(true);
+			committers.setHeaderVisible(true);
+			GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+			layoutData.horizontalSpan = 2;
+			committers.setLayoutData(layoutData);
+
+			GridData fieldLayoutData = new GridData(SWT.FILL, SWT.BEGINNING,
+					true, false);
+			selectedCommitterName = new Label(shell, SWT.NONE);
+			selectedCommitterName.setLayoutData(fieldLayoutData);
+
+			selectedCommitterFirstName = new Label(shell, SWT.NONE);
+			selectedCommitterFirstName.setLayoutData(fieldLayoutData);
+
+			DataBindingContext bindingContext = new DataBindingContext();
+			bindGUI(bindingContext);
+
+			// Open and return the Shell
+			shell.setSize(250, 300);
+			shell.open();
+			return shell;
+		}
+
+		protected void bindGUI(DataBindingContext bindingContext) {
+			// Since we're using a JFace Viewer, we do first wrap our Table...
+			TableViewer peopleViewer = new TableViewer(committers);
+
+			TableViewerColumn columnName = new TableViewerColumn(peopleViewer,
+					SWT.NONE);
+			columnName.getColumn().setText("Name");
+			columnName.getColumn().setWidth(100);
+
+			TableViewerColumn columnFirstName = new TableViewerColumn(
+					peopleViewer, SWT.NONE);
+			columnFirstName.getColumn().setText("FirstName");
+			columnFirstName.getColumn().setWidth(100);
+
+			// Bind viewer to model
+			IBeanValueProperty propName = BeanProperties.value(Person.class,
+					"name");
+			IBeanValueProperty propFirstname = BeanProperties.value(
+					Person.class, "firstName");
+
+			IValueProperty cellEditorControlText = CellEditorProperties
+					.control().value(WidgetProperties.text());
+
+			columnName.setEditingSupport(ObservableValueEditingSupport.create(
+					peopleViewer, bindingContext,
+					new TextCellEditor(committers), cellEditorControlText,
+					propName));
+			columnFirstName.setEditingSupport(ObservableValueEditingSupport
+					.create(peopleViewer, bindingContext, new TextCellEditor(
+							committers), cellEditorControlText, propFirstname));
+
+			ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+			peopleViewer.setContentProvider(contentProvider);
+
+			// Bind the LabelProviders to the model and columns
+			IObservableMap[] result = Properties.observeEach(contentProvider
+					.getKnownElements(), new IBeanValueProperty[] { propName,
+					propFirstname });
+
+			columnName.setLabelProvider(new ObservableMapCellLabelProvider(
+					result[0]));
+			columnFirstName
+					.setLabelProvider(new ObservableMapCellLabelProvider(
+							result[1]));
+
+			peopleViewer.setInput(new WritableList(viewModel.getPeople(),
+					Person.class));
+
+			// bind selectedCommitter labels to the name and forname of the
+			// current selection
+			IObservableValue selection = ViewersObservables
+					.observeSingleSelection(peopleViewer);
+			bindingContext.bindValue(SWTObservables
+					.observeText(selectedCommitterName), BeansObservables
+					.observeDetailValue(selection, "name", String.class));
+			bindingContext.bindValue(SWTObservables
+					.observeText(selectedCommitterFirstName), BeansObservables
+					.observeDetailValue(selection, "firstName", String.class));
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet033CrossValidationControlDecoration.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet033CrossValidationControlDecoration.java
new file mode 100644
index 0000000..9b92853
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet033CrossValidationControlDecoration.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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 268472)
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Date;
+
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.validation.MultiValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
+import org.eclipse.jface.databinding.fieldassist.ControlDecorationUpdater;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class Snippet033CrossValidationControlDecoration {
+	protected Shell shell;
+	private DateTime startDate;
+	private DateTime endDate;
+
+	/**
+	 * Launch the application
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		try {
+			Snippet033CrossValidationControlDecoration window = new Snippet033CrossValidationControlDecoration();
+			window.open();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Open the window
+	 */
+	public void open() {
+		final Display display = Display.getDefault();
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				createContents();
+				shell.pack();
+				shell.open();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch())
+						display.sleep();
+				}
+			}
+		});
+	}
+
+	protected void createContents() {
+		shell = new Shell();
+		GridLayout layout = new GridLayout();
+		layout.numColumns = 4;
+		shell.setLayout(layout);
+		shell.setText("Snippet033CrossValidationControlDecoration.java");
+
+		final Label label = new Label(shell, SWT.NONE);
+		label.setLayoutData(new GridData());
+		label.setText("Start date");
+		startDate = new DateTime(shell, SWT.CALENDAR);
+		final GridData gd_startDate = new GridData();
+		gd_startDate.horizontalIndent = 10;
+		startDate.setLayoutData(gd_startDate);
+
+		final Label startDateLabel = new Label(shell, SWT.NONE);
+		startDateLabel.setLayoutData(new GridData());
+		startDateLabel.setText("End date");
+		endDate = new DateTime(shell, SWT.CALENDAR);
+		final GridData gd_endDate = new GridData();
+		gd_endDate.horizontalIndent = 10;
+		endDate.setLayoutData(gd_endDate);
+
+		bindUI();
+	}
+
+	private void bindUI() {
+		IObservableValue startDateObservable = WidgetProperties.selection()
+				.observe(startDate);
+		IObservableValue endDateObservable = WidgetProperties.selection()
+				.observe(endDate);
+
+		ControlDecorationSupport.create(new DateRangeValidator(
+				startDateObservable, endDateObservable,
+				"Start date must be on or before end date"), SWT.LEFT
+				| SWT.CENTER);
+
+		// Customize the decoration's description text and image
+		ControlDecorationUpdater decorationUpdater = new ControlDecorationUpdater() {
+			protected String getDescriptionText(IStatus status) {
+				return "ERROR: " + super.getDescriptionText(status);
+			}
+
+			protected Image getImage(IStatus status) {
+				return status.isOK() ? null : Display.getCurrent()
+						.getSystemImage(SWT.ICON_ERROR);
+			}
+		};
+		ControlDecorationSupport.create(new DateRangeValidator(Observables
+				.constantObservableValue(new Date()), startDateObservable,
+				"Choose a starting date later than today"), SWT.LEFT | SWT.TOP,
+				(Composite) null, decorationUpdater);
+	}
+
+	private static class DateRangeValidator extends MultiValidator {
+		private final IObservableValue start;
+		private final IObservableValue end;
+		private final String errorMessage;
+
+		public DateRangeValidator(IObservableValue start, IObservableValue end,
+				String errorMessage) {
+			this.start = start;
+			this.end = end;
+			this.errorMessage = errorMessage;
+		}
+
+		protected IStatus validate() {
+			Date startDate = (Date) start.getValue();
+			Date endDate = (Date) end.getValue();
+			if (startDate.compareTo(endDate) > 0)
+				return ValidationStatus.error(errorMessage);
+			return ValidationStatus.ok();
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet034ComboViewerAndEnum.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet034ComboViewerAndEnum.java
new file mode 100644
index 0000000..e442149
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet034ComboViewerAndEnum.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Eric Rizzo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Eric Rizzo - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class Snippet034ComboViewerAndEnum {
+
+	public static void main(String[] args) {
+		Display display = new Display();
+		final Person model = new Person("Pat", Gender.Unknown);
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				final Shell shell = new View(model).createShell();
+				// The SWT event loop
+				Display display = Display.getCurrent();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+		// Print the results
+		System.out.println("person.getName() = " + model.getName());
+		System.out.println("person.getGender() = " + model.getGender());
+	}
+
+	static enum Gender {
+		Male, Female, Unknown;
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	// 
+	// In this example, we only push changes from the GUI to the model, so we
+	// don't worry about implementing JavaBeans bound properties. If we need
+	// our GUI to automatically reflect changes in the Person object, the
+	// Person object would need to implement the JavaBeans property change
+	// listener methods.
+	static class Person {
+		// A property...
+		String name;
+		Gender gender;
+
+		public Person(String name, Gender gender) {
+			this.name = name;
+			this.gender = gender;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+
+		public Gender getGender() {
+			return gender;
+		}
+
+		public void setGender(Gender newGender) {
+			this.gender = newGender;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private Person viewModel;
+		private Text name;
+		private ComboViewer gender;
+
+		public View(Person viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			Display display = Display.getDefault();
+			Shell shell = new Shell(display);
+
+			RowLayout layout = new RowLayout(SWT.VERTICAL);
+			layout.fill = true;
+			layout.marginWidth = layout.marginHeight = 5;
+			shell.setLayout(layout);
+
+			name = new Text(shell, SWT.BORDER);
+			gender = new ComboViewer(shell, SWT.READ_ONLY);
+
+			// Here's the first key to binding a combo to an Enum:
+			// First give it an ArrayContentProvider,
+			// then set the input to the list of values from the Enum.
+			gender.setContentProvider(ArrayContentProvider.getInstance());
+			gender.setInput(Gender.values());
+
+			// Bind the fields
+			DataBindingContext bindingContext = new DataBindingContext();
+
+			IObservableValue widgetObservable = SWTObservables.observeText(
+					name, SWT.Modify);
+			bindingContext.bindValue(widgetObservable, PojoObservables
+					.observeValue(viewModel, "name"));
+
+			// The second key to binding a combo to an Enum is to use a
+			// selection observable from the ComboViewer:
+			widgetObservable = ViewersObservables
+					.observeSingleSelection(gender);
+			bindingContext.bindValue(widgetObservable, PojoObservables
+					.observeValue(viewModel, "gender"));
+
+			// Open and return the Shell
+			shell.pack();
+			shell.open();
+			return shell;
+		}
+	}
+
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet035PostSelectionProvider.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet035PostSelectionProvider.java
new file mode 100644
index 0000000..be9784a
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet035PostSelectionProvider.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 270494)
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Simple snippet which tries to illustrate the difference between normal
+ * selection events and post selection events. You will see the difference as of
+ * when the two types of selection events are fired by changing the selection in
+ * the list either with the mouse (both selection events are fired immediately,
+ * no difference) or with the keyboard (post selection events are delayed and
+ * only fired when you stop navigating with the arrow keys).
+ */
+public class Snippet035PostSelectionProvider {
+
+	private DataBindingContext dbc;
+
+	private ListViewer listViewer;
+
+	public static void main(String[] args) {
+		Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				Shell shell = new Snippet035PostSelectionProvider()
+						.createShell();
+				Display display = Display.getCurrent();
+				while (!shell.isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	private Shell createShell() {
+		Display display = Display.getCurrent();
+		Shell shell = new Shell(display);
+		shell.setText("Post Selections");
+		shell.setLayout(new GridLayout(1, false));
+
+		dbc = new DataBindingContext();
+
+		createTableSection(shell);
+		createFieldSection(shell);
+
+		shell.pack();
+		shell.open();
+
+		return shell;
+	}
+
+	private void createTableSection(Composite parent) {
+		Group section = createSectionGroup(parent, 1);
+
+		listViewer = new ListViewer(section, SWT.SINGLE | SWT.BORDER);
+		GridDataFactory.fillDefaults().grab(true, true).hint(250, 250).applyTo(
+				listViewer.getList());
+
+		listViewer.setContentProvider(new ArrayContentProvider());
+		listViewer.setLabelProvider(new LabelProvider());
+
+		String[] names = new String[] { "John Doe", "Steve Northover",
+				"Grant Gayed", "Veronika Irvine", "Mike Wilson",
+				"Christophe Cornu", "Lynne Kues", "Silenio Quarti" };
+
+		listViewer.setInput(names);
+	}
+
+	private void createFieldSection(Composite parent) {
+		final Group section = createSectionGroup(parent, 2);
+
+		// normal selection
+		Label selectionLabel = createLabelField(section, "Selection:");
+		IViewerObservableValue selectionObservable = ViewerProperties
+				.singleSelection().observe(listViewer);
+		dbc.bindValue(SWTObservables.observeText(selectionLabel),
+				selectionObservable);
+
+		// post selection
+		Label postSelectionLabel = createLabelField(section, "Post selection:");
+		IViewerObservableValue postSelectionObservable = ViewerProperties
+				.singlePostSelection().observe(listViewer);
+		dbc.bindValue(SWTObservables.observeText(postSelectionLabel),
+				postSelectionObservable);
+	}
+
+	private Group createSectionGroup(Composite parent, int numColumns) {
+		Group section = new Group(parent, SWT.SHADOW_ETCHED_IN);
+		GridLayoutFactory.fillDefaults().numColumns(numColumns).equalWidth(
+				false).margins(5, 5).spacing(15, 5).applyTo(section);
+		GridDataFactory.fillDefaults().grab(true, true).applyTo(section);
+		return section;
+	}
+
+	private static Label createLabelField(Composite parent, String labelText) {
+		Label nameLabel = new Label(parent, SWT.LEFT);
+		nameLabel.setText(labelText);
+		GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.CENTER).applyTo(
+				nameLabel);
+
+		Label contentLabel = new Label(parent, SWT.LEFT);
+		GridDataFactory.fillDefaults().grab(true, false).hint(150, SWT.DEFAULT)
+				.applyTo(contentLabel);
+
+		return contentLabel;
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet036ValidationMessageProvider.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet036ValidationMessageProvider.java
new file mode 100644
index 0000000..378da67
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet036ValidationMessageProvider.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 248877)
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.dialog.IValidationMessageProvider;
+import org.eclipse.jface.databinding.dialog.ValidationMessageProvider;
+import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
+import org.eclipse.jface.databinding.fieldassist.ControlDecorationUpdater;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Illustrates the use of the {@link IValidationMessageProvider} API.
+ */
+public class Snippet036ValidationMessageProvider {
+
+	public static void main(String[] args) {
+		Display display = new Display();
+
+		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
+			public void run() {
+				IWizard wizard = new MessageProviderWizard();
+				WizardDialog wizardDialog = new WizardDialog(null, wizard);
+				wizardDialog.open();
+
+				Display display = Display.getCurrent();
+				while (wizardDialog.getShell() != null
+						&& !wizardDialog.getShell().isDisposed()) {
+					if (!display.readAndDispatch()) {
+						display.sleep();
+					}
+				}
+			}
+		});
+	}
+
+	private static final class MessageProviderWizard extends Wizard {
+
+		public void addPages() {
+			addPage(new MessageProviderWizardPage());
+		}
+
+		public String getWindowTitle() {
+			return "Snippet 036 - IValidationMessageProvider";
+		}
+
+		public boolean performFinish() {
+			return true;
+		}
+	}
+
+	private static final class MessageProviderWizardPage extends WizardPage {
+
+		private DataBindingContext dbc;
+		private Map bindingMapName;
+
+		protected MessageProviderWizardPage() {
+			super(MessageProviderWizardPage.class.getName());
+			setTitle("Snippet 036 - IValidationMessageProvider");
+			setDescription("Please fill in the form.");
+		}
+
+		public void createControl(Composite parent) {
+			dbc = new DataBindingContext();
+			bindingMapName = new HashMap();
+
+			// Create the container composite.
+			Composite container = new Composite(parent, SWT.NULL);
+			GridLayoutFactory.fillDefaults().numColumns(2).margins(5, 5)
+					.spacing(15, 5).applyTo(container);
+			setControl(container);
+
+			// Create the input fields.
+			createTextLine(container, "Name", WritableValue
+					.withValueType(String.class));
+			createTextLine(container, "Age", WritableValue
+					.withValueType(Integer.class));
+			createTextLine(container, "Birthday", WritableValue
+					.withValueType(Date.class));
+
+			// Attach the DBC's validation to the wizard.
+			WizardPageSupport wps = WizardPageSupport.create(this, dbc);
+
+			// Use our CustomMessageProvider.
+			wps.setValidationMessageProvider(new CustomMessageProvider(
+					bindingMapName));
+		}
+
+		private void createTextLine(Composite parent, String labelText,
+				IObservableValue modelValue) {
+			// Create the Label.
+			Label label = new Label(parent, SWT.LEFT);
+			label.setText(labelText);
+			GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.CENTER).applyTo(
+					label);
+
+			// Create the Text.
+			final Text text = new Text(parent, SWT.BORDER);
+			GridDataFactory.fillDefaults().grab(true, false).applyTo(text);
+
+			// Create the Text observable.
+			ISWTObservableValue textObservable = SWTObservables.observeText(
+					text, SWT.Modify);
+
+			// Bind the Text to the model and attach a RequiredValidator.
+			Binding binding = dbc.bindValue(textObservable, modelValue,
+					new UpdateValueStrategy()
+							.setAfterConvertValidator(new RequiredValidator()),
+					new UpdateValueStrategy());
+
+			// Custom control decoration for "required" validation.
+			ControlDecorationUpdater decorationUpdater = new ControlDecorationUpdater() {
+
+				protected Image getImage(IStatus status) {
+					// For required validations, we do not want to display an
+					// error icon since the user has not done anything wrong.
+					if (text.getText().length() == 0) {
+						// Display a "required" decoration (asterisk).
+						FieldDecoration fieldDecoration = FieldDecorationRegistry
+								.getDefault().getFieldDecoration(
+										FieldDecorationRegistry.DEC_REQUIRED);
+						return fieldDecoration.getImage();
+					}
+					return super.getImage(status);
+				}
+			};
+
+			// Attach the control decoration.
+			ControlDecorationSupport.create(binding, SWT.TOP, null,
+					decorationUpdater);
+
+			// Map the created binding to its name, i.e. the Label's text.
+			bindingMapName.put(binding, labelText);
+		}
+	}
+
+	/**
+	 * Custom {@link IValidationMessageProvider} which does the following:
+	 * <ul>
+	 * <li>Every validation message of a binding is prefixed by the binding's
+	 * name, if available.</li>
+	 * <li>Validation errors due to empty, required fields are not displayed as
+	 * errors but as simple text without any icon.</li>
+	 * </ul>
+	 */
+	private static final class CustomMessageProvider extends
+			ValidationMessageProvider {
+
+		private final Map bindingMapName;
+
+		public CustomMessageProvider(Map bindingMapName) {
+			this.bindingMapName = bindingMapName;
+		}
+
+		public String getMessage(ValidationStatusProvider statusProvider) {
+			if (statusProvider != null) {
+				String name = (String) bindingMapName.get(statusProvider);
+				if (name != null) {
+					// For named bindings, we display "<name>: <message>"
+					String message = super.getMessage(statusProvider);
+					return name + ": " + message;
+				}
+			}
+			return super.getMessage(statusProvider);
+		}
+
+		public int getMessageType(ValidationStatusProvider statusProvider) {
+			if (statusProvider instanceof Binding) {
+				Binding binding = (Binding) statusProvider;
+				IStatus status = (IStatus) binding.getValidationStatus()
+						.getValue();
+
+				// For required validations, we do not want to display an error
+				// icon since the user has not done anything wrong.
+				if (status.matches(IStatus.ERROR)) {
+					IObservableValue target = (IObservableValue) binding
+							.getTarget();
+					// If the input is empty, we do not display any error icon.
+					if ("".equals(target.getValue())) {
+						return IMessageProvider.NONE;
+					}
+				}
+			}
+			return super.getMessageType(statusProvider);
+		}
+	}
+
+	private static final class RequiredValidator implements IValidator {
+
+		public IStatus validate(Object value) {
+			if (value == null || "".equals(value)) {
+				return ValidationStatus.error("Please specify a value.");
+			}
+			return ValidationStatus.ok();
+		}
+	}
+}
diff --git a/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet0xffffffff.java b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet0xffffffff.java
new file mode 100644
index 0000000..04008ec
--- /dev/null
+++ b/examples/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet0xffffffff.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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
+ *     Brad Reynolds - bug 116920
+ ******************************************************************************/
+
+package org.eclipse.jface.examples.databinding.snippets;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Snippet -1.
+ * 
+ * Hello, no databinding. Bind changes in a GUI to a Model object but don't
+ * worry about propogating changes from the Model to the GUI -- using *manual*
+ * code. (0xffffffff is -1 in 32-bit two's complement binary arithmatic)
+ */
+public class Snippet0xffffffff {
+	public static void main(String[] args) {
+		ViewModel viewModel = new ViewModel();
+		Shell shell = new View(viewModel).createShell();
+
+		// The SWT event loop
+		Display display = Display.getCurrent();
+		while (!shell.isDisposed()) {
+			if (!display.readAndDispatch()) {
+				display.sleep();
+			}
+		}
+
+		// Print the results
+		System.out.println("person.getName() = "
+				+ viewModel.getPerson().getName());
+	}
+
+	// Minimal JavaBeans support
+	public static abstract class AbstractModelObject {
+		private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+				this);
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void addPropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.addPropertyChangeListener(propertyName,
+					listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(String propertyName,
+				PropertyChangeListener listener) {
+			propertyChangeSupport.removePropertyChangeListener(propertyName,
+					listener);
+		}
+
+		protected void firePropertyChange(String propertyName, Object oldValue,
+				Object newValue) {
+			propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+					newValue);
+		}
+	}
+
+	// The data model class. This is normally a persistent class of some sort.
+	// 
+	// In this example, we only push changes from the GUI to the model, so we
+	// don't worry about implementing JavaBeans bound properties. If we need
+	// our GUI to automatically reflect changes in the Person object, the
+	// Person object would need to implement the JavaBeans property change
+	// listener methods.
+	static class Person extends AbstractModelObject {
+		// A property...
+		String name = "John Smith";
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+
+	}
+
+	// The View's model--the root of our Model graph for this particular GUI.
+	//
+	// Typically each View class has a corresponding ViewModel class.
+	//
+	// The ViewModel is responsible for getting the objects to edit from the
+	// data access tier. Since this snippet doesn't have any persistent objects
+	// to
+	// retrieve, this ViewModel just instantiates a model object to edit.
+	static class ViewModel {
+		// The model to bind
+		private Person person = new Person();
+
+		public Person getPerson() {
+			return person;
+		}
+	}
+
+	// The GUI view
+	static class View {
+		private ViewModel viewModel;
+
+		public View(ViewModel viewModel) {
+			this.viewModel = viewModel;
+		}
+
+		public Shell createShell() {
+			// Build a UI
+			final Display display = Display.getCurrent();
+			Shell shell = new Shell(display);
+			shell.setLayout(new RowLayout(SWT.VERTICAL));
+
+			final Text name = new Text(shell, SWT.BORDER);
+			
+			// Bind it (manually)
+			name.setText(viewModel.getPerson().getName());
+			name.addModifyListener(new ModifyListener() {
+				public void modifyText(ModifyEvent e) {
+					final String text = name.getText();
+					// validation
+					// conversion
+					viewModel.getPerson().setName(text);
+				}
+			});
+			viewModel.person.addPropertyChangeListener("name",
+					new PropertyChangeListener() {
+						public void propertyChange(PropertyChangeEvent evt) {
+							display.asyncExec(new Runnable() {
+								public void run() {
+									final String newName = viewModel.person.getName();
+									// conversion
+									name.setText(newName);
+								}
+							});
+						}
+					});
+
+			// Open and return the Shell
+			shell.pack();
+			shell.open();
+			return shell;
+		}
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/.classpath b/tests/org.eclipse.jface.tests.databinding.conformance/.classpath
new file mode 100644
index 0000000..ce73933
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/.project b/tests/org.eclipse.jface.tests.databinding.conformance/.project
new file mode 100644
index 0000000..3094c2c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jface.tests.databinding.conformance</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.jdt.core.prefs b/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..d735633
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,356 @@
+#Thu Feb 05 11:37:58 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.jdt.ui.prefs b/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..b58868e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:06:09 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.2\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.pde.prefs b/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..12a7331
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:56:42 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/META-INF/MANIFEST.MF b/tests/org.eclipse.jface.tests.databinding.conformance/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9dcf5ee
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/META-INF/MANIFEST.MF
@@ -0,0 +1,24 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jface.tests.databinding.conformance
+Bundle-Version: 1.1.0.qualifier
+Require-Bundle: org.junit,
+ org.eclipse.core.databinding,
+ org.eclipse.jface.databinding,
+ org.eclipse.swt,
+ org.eclipse.core.runtime
+Bundle-Vendor: %providerName
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Bundle-Localization: plugin
+Export-Package: org.eclipse.jface.databinding.conformance;uses:="org.eclipse.core.databinding.observable,org.eclipse.jface.databinding.conformance.delegate,junit.framework",
+ org.eclipse.jface.databinding.conformance.delegate,
+ org.eclipse.jface.databinding.conformance.swt,
+ org.eclipse.jface.databinding.conformance.util;
+  uses:="org.eclipse.core.databinding.observable.map,
+   org.eclipse.jface.databinding.conformance.delegate,
+   org.eclipse.core.databinding.observable,
+   org.eclipse.core.databinding.observable.list,
+   org.eclipse.core.databinding.observable.value,
+   junit.framework,
+   org.eclipse.core.databinding.observable.set"
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/about.html b/tests/org.eclipse.jface.tests.databinding.conformance/about.html
new file mode 100644
index 0000000..d42b3ce
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/build.properties b/tests/org.eclipse.jface.tests.databinding.conformance/build.properties
new file mode 100644
index 0000000..cc15552
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# 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
+###############################################################################
+bin.includes = .,\
+               META-INF/,\
+               plugin.properties,\
+               about.html
+output.databinding.jar = bin/
+src.includes = about.html
+source.. = src/
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/plugin.properties b/tests/org.eclipse.jface.tests.databinding.conformance/plugin.properties
new file mode 100644
index 0000000..4e17d68
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# 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
+###############################################################################
+pluginName = JFace Data Binding Conformance Tests
+providerName = Eclipse.org
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableCollectionContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableCollectionContractTest.java
new file mode 100644
index 0000000..ba34da5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableCollectionContractTest.java
@@ -0,0 +1,387 @@
+/*******************************************************************************
+ * 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 - bugs 208858, 213145
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ * Mutability tests for IObservableCollection.
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class MutableObservableCollectionContractTest extends ObservableDelegateTest {
+	private IObservableCollectionContractDelegate delegate;
+
+	private IObservableCollection collection;
+
+	public MutableObservableCollectionContractTest(
+			IObservableCollectionContractDelegate delegate) {
+		super(delegate);
+		this.delegate = delegate;
+	}
+
+	public MutableObservableCollectionContractTest(String name,
+			IObservableCollectionContractDelegate delegate) {
+		super(name, delegate);
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		collection = (IObservableCollection) super.getObservable();
+	}
+
+	public void testAdd_ChangeEvent() throws Exception {
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				collection.add(delegate.createElement(collection));
+			}
+		}, "Collection.add(Object)", collection);
+	}
+	
+	public void testAdd_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.add(delegate.createElement(collection));
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testAdd_ChangeEventFiredAfterElementIsAdded() throws Exception {
+		final Object element = delegate.createElement(collection);
+
+		assertContainsDuringChangeEvent(new Runnable() {
+			public void run() {
+				collection.add(element);
+			}
+		}, "Collection.add(Object)", collection, element);
+	}
+
+	public void testAddAll_ChangeEvent() throws Exception {
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				collection.addAll(Arrays.asList(new Object[] { delegate
+						.createElement(collection) }));
+			}
+		}, "Collection.addAll(Collection)", collection);
+	}
+	
+	public void testAddAll_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.addAll(Arrays.asList(new Object[] { delegate
+						.createElement(collection) }));
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testAddAll_ChangeEventFiredAfterElementsAreAdded()
+			throws Exception {
+		final Object element = delegate.createElement(collection);
+
+		assertContainsDuringChangeEvent(new Runnable() {
+			public void run() {
+				collection.addAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "Collection.addAll(Collection)", collection, element);
+	}
+
+	public void testRemove_ChangeEvent() throws Exception {
+		final Object element = delegate.createElement(collection);
+		collection.add(element);
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				collection.remove(element);
+			}
+		}, "Collection.remove(Object)", collection);
+	}
+	
+	public void testRemove_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.remove(delegate.createElement(collection));
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testRemove_ChangeEventFiredAfterElementIsRemoved()
+			throws Exception {
+		final Object element = delegate.createElement(collection);
+		collection.add(element);
+
+		assertDoesNotContainDuringChangeEvent(new Runnable() {
+			public void run() {
+				collection.remove(element);
+			}
+		}, "Collection.remove(Object)", collection, element);
+	}
+
+	public void testRemoveAll_ChangeEvent() throws Exception {
+		final Object element = delegate.createElement(collection);
+		collection.add(element);
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				collection.removeAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "Collection.removeAll(Collection)", collection);
+	}
+	
+	public void testRemoveAll_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.removeAll(Arrays.asList(new Object[] { delegate.createElement(collection) }));
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testRemoveAll_ChangeEventFiredAfterElementsAreRemoved()
+			throws Exception {
+		final Object element = delegate.createElement(collection);
+		collection.add(element);
+
+		assertDoesNotContainDuringChangeEvent(new Runnable() {
+			public void run() {
+				collection.removeAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "Collection.removeAll(Collection)", collection, element);
+	}
+
+	public void testRemoveAll_NoChange() throws Exception {
+		ChangeEventTracker tracker = ChangeEventTracker.observe(collection);
+		collection.removeAll(Collections.EMPTY_LIST);
+		assertEquals(
+				"List.removeAll on an empty list should not fire a list change event",
+				0, tracker.count);
+	}
+
+	public void testRetainAll_ChangeEvent() throws Exception {
+		final Object element1 = delegate.createElement(collection);
+		collection.add(element1);
+		Object element2 = delegate.createElement(collection);
+		collection.add(element2);
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				collection.retainAll(Arrays.asList(new Object[] { element1 }));
+			}
+
+		}, "Collection.retainAll(Collection)", collection);
+	}
+	
+	public void testRetainAll_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.retainAll(Collections.EMPTY_LIST);
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testRetainAll_ChangeEventFiredAfterElementsAreRetained()
+			throws Exception {
+		Object element1 = delegate.createElement(collection);
+		collection.add(element1);
+		Object element2 = delegate.createElement(collection);
+		collection.add(element2);
+
+		// precondition
+		assertTrue(collection.contains(element1));
+		assertTrue(collection.contains(element2));
+
+		ContainsListener listener1 = new ContainsListener(collection, element1)
+				.init();
+		ContainsListener listener2 = new ContainsListener(collection, element2)
+				.init();
+
+		// set contains the the opposite of the expected outcome to ensure they
+		// get set
+		listener1.contains = false;
+		listener2.contains = true;
+
+		collection.retainAll(Arrays.asList(new Object[] { element1 }));
+		assertTrue(
+				formatFail("When Collection.retainAll(...) fires the change event the element should have been retained in the Collection."),
+				listener1.contains);
+		assertFalse(
+				formatFail("When Collection.retainAll(...) fires the change event the element should have been removed from the Collection."),
+				listener2.contains);
+	}
+	
+	public void testRetainAll_NoChangeFiresNoChangeEvent() throws Exception {
+		ChangeEventTracker tracker = ChangeEventTracker.observe(collection);
+		collection.retainAll(Collections.EMPTY_LIST);
+		assertEquals("List.retainAll should not have fired a change event:",
+				0, tracker.count);
+	}
+	
+	public void testClear_ChangeEvent() throws Exception {
+		collection.add(delegate.createElement(collection));
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				collection.clear();
+			}
+		}, "List.clear()", collection);
+	}
+
+	public void testClear_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.clear();
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testClear_ChangeEventFiredAfterElementIsRemoved()
+			throws Exception {
+		Object element = delegate.createElement(collection);
+		collection.add(element);
+
+		assertDoesNotContainDuringChangeEvent(new Runnable() {
+			public void run() {
+				collection.clear();
+			}
+		}, "List.clear()", collection, element);
+	}
+
+	/**
+	 * Asserts that a ChangeEvent is fired once when the provided
+	 * <code>runnable</code> is invoked and the source is the provided
+	 * <code>collection</code>.
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 * @param collection
+	 */
+	/* package */void assertChangeEventFired(Runnable runnable,
+			String methodName, IObservableCollection collection) {
+		
+		ChangeEventTracker listener = ChangeEventTracker.observe(collection);
+		runnable.run();
+
+		assertEquals(formatFail(methodName + " should fire one ChangeEvent."), 1,
+				listener.count);
+		assertEquals(
+				formatFail(methodName
+						+ "'s change event observable should be the created Collection."),
+				collection, listener.event.getObservable());
+	}
+
+	/**
+	 * Asserts that when the change event is fired for the action contained in
+	 * the <code>runnable</code> the change will have been applied to the
+	 * <code>collection</code>.
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 * @param collection
+	 * @param elementNotContained
+	 */
+	/* package */void assertDoesNotContainDuringChangeEvent(Runnable runnable,
+			String methodName, IObservableCollection collection,
+			Object elementNotContained) {
+
+		// precondition
+		assertTrue(collection.contains(elementNotContained));
+
+		ContainsListener listener = new ContainsListener(collection,
+				elementNotContained).init();
+		listener.contains = true;
+		collection.remove(elementNotContained);
+		assertFalse(
+				formatFail(new StringBuffer("When ")
+						.append(methodName)
+						.append(
+								" fires a change event the element should have been removed from the Collection.")
+						.toString()), listener.contains);
+	}
+
+	/**
+	 * Asserts that when the change event is fired for the action contained in
+	 * the <code>runnable</code> the change will have been applied to the
+	 * <code>collection</code>.
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 * @param collection
+	 * @param elementContained
+	 */
+	/* package */void assertContainsDuringChangeEvent(Runnable runnable,
+			String methodName, IObservableCollection collection,
+			Object elementContained) {
+		ContainsListener listener = new ContainsListener(collection,
+				elementContained).init();
+
+		// precondition
+		assertFalse(collection.contains(elementContained));
+		runnable.run();
+
+		assertTrue(
+				formatFail(new StringBuffer("When ")
+						.append(methodName)
+						.append(
+								" fires a change event the element should have been added to the Collection.")
+						.toString()), listener.contains);
+	}
+
+	/* package */static class ContainsListener implements IChangeListener {
+		boolean contains;
+
+		final private Object element;
+
+		final private IObservableCollection collection;
+
+		ContainsListener(IObservableCollection collection, Object element) {
+			this.element = element;
+			this.collection = collection;
+		}
+
+		ContainsListener init() {
+			collection.addChangeListener(this);
+			return this;
+		}
+
+		public void handleChange(ChangeEvent event) {
+			contains = collection.contains(element);
+		}
+	}
+
+	public static Test suite(IObservableCollectionContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				MutableObservableCollectionContractTest.class, delegate)
+				.addObservableContractTest(
+						ObservableCollectionContractTest.class, delegate)
+				.build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableListContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableListContractTest.java
new file mode 100644
index 0000000..3d34e9a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableListContractTest.java
@@ -0,0 +1,534 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 208858, 221351, 213145, 244098
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ * Mutability tests for IObservableList.
+ * 
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class MutableObservableListContractTest extends
+		MutableObservableCollectionContractTest {
+	private IObservableCollectionContractDelegate delegate;
+
+	private IObservableList list;
+
+	/**
+	 * @param delegate
+	 */
+	public MutableObservableListContractTest(
+			IObservableCollectionContractDelegate delegate) {
+		super(delegate);
+		this.delegate = delegate;
+	}
+
+	public MutableObservableListContractTest(String testName,
+			IObservableCollectionContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		list = (IObservableList) getObservable();
+	}
+
+	public void testAdd_ListChangeEvent() throws Exception {
+		final Object element = delegate.createElement(list);
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.add(element);
+			}
+		}, "List.add(Object)", list, Collections.singletonList(element));
+	}
+
+	public void testAdd_ListDiffEntry() throws Exception {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.add(element1);
+			}
+		}, "List.add(Object)", list, Arrays.asList(new Object[] { element0,
+				element1 }));
+	}
+
+	public void testAddAtIndex_ChangeEvent() throws Exception {
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				list.add(0, delegate.createElement(list));
+			}
+		}, "List.add(int, Object)", list);
+	}
+
+	public void testAddAtIndex_ListChangeEvent() throws Exception {
+		final Object element = delegate.createElement(list);
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.add(0, element);
+			}
+		}, "List.add(int, Object)", list, Collections.singletonList(element));
+	}
+
+	public void testAddAtIndex_ChangeEventFiredAfterElementIsAdded()
+			throws Exception {
+		final Object element = delegate.createElement(list);
+
+		assertContainsDuringChangeEvent(new Runnable() {
+			public void run() {
+				list.add(0, element);
+			}
+		}, "List.add(int, Collection)", list, element);
+	}
+
+	public void testAddAtIndex_ListDiffEntry() throws Exception {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.add(1, element1);
+			}
+		}, "List.add(int, Object)", list, Arrays.asList(new Object[] {
+				element0, element1 }));
+	}
+
+	public void testAddAll_ListChangeEvent() throws Exception {
+		final Object element = delegate.createElement(list);
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.addAll(Collections.singletonList(element));
+			}
+		}, "List.addAll(Collection", list, Collections.singletonList(element));
+	}
+
+	public void testAddAll_ListDiffEntry() throws Exception {
+		final Object element = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.addAll(Collections.singletonList(element));
+			}
+		}, "List.addAll(Collection)", list, Collections.singletonList(element));
+	}
+
+	public void testAddAll_ListDiffEntry2() throws Exception {
+		final Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.addAll(Collections.singletonList(element1));
+			}
+		}, "List.addAll(Collection)", list, Arrays.asList(new Object[] {
+				element0, element1 }));
+	}
+
+	public void testAddAllAtIndex_ChangeEvent() throws Exception {
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				list.addAll(0, Arrays.asList(new Object[] { delegate
+						.createElement(list) }));
+			}
+		}, "List.addAll(int, Collection)", list);
+	}
+
+	public void testAddAllAtIndex_ListChangeEvent() throws Exception {
+		final Object element = delegate.createElement(list);
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.addAll(0, Collections.singletonList(element));
+			}
+		}, "List.addAll(int, Collection)", list, Collections
+				.singletonList(element));
+	}
+
+	public void testAddAllAtIndex_ChangeEventFiredAfterElementIsAdded()
+			throws Exception {
+		final Object element = delegate.createElement(list);
+
+		assertContainsDuringChangeEvent(new Runnable() {
+			public void run() {
+				list.addAll(0, Arrays.asList(new Object[] { element }));
+			}
+		}, "List.addAll(int, Collection)", list, element);
+	}
+
+	public void testAddAllAtIndex_ListDiffEntry() throws Exception {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.addAll(1, Collections.singletonList(element1));
+			}
+		}, "List.addAll(int, Collection)", list, Arrays.asList(new Object[] {
+				element0, element1 }));
+	}
+
+	public void testSet_ChangeEvent() throws Exception {
+		list.add(delegate.createElement(list));
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				list.set(0, delegate.createElement(list));
+			}
+		}, "List.set(int, Object)", list);
+	}
+
+	public void testSet_ListChangeEvent() throws Exception {
+		final Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				assertSame(element0, list.set(0, element1));
+			}
+		}, "List.set(int, Object)", list, Arrays
+				.asList(new Object[] { element1 }));
+	}
+
+	public void testSet_ChangeEventFiredAfterElementIsSet() throws Exception {
+		final Object element1 = delegate.createElement(list);
+		list.add(element1);
+		final Object element2 = delegate.createElement(list);
+
+		assertContainsDuringChangeEvent(new Runnable() {
+			public void run() {
+				assertSame(element1, list.set(0, element2));
+			}
+		}, "List.set(int, Object)", list, element2);
+	}
+
+	public void testSet_ListChangeEvent2() throws Exception {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		Object oldElement1 = delegate.createElement(list);
+		list.add(oldElement1);
+		final Object newElement1 = delegate.createElement(list);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.set(1, newElement1);
+			}
+		}, "List.set(int, Object)", list, Arrays.asList(new Object[] {
+				element0, newElement1 }));
+	}
+
+	public void testMove_ChangeEvent() throws Exception {
+		list.add(delegate.createElement(list));
+		list.add(delegate.createElement(list));
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				list.move(0, 1);
+			}
+		}, "IObservableList.move(int, int)", list);
+	}
+
+	public void testMove_NoChangeEventAtSameIndex() throws Exception {
+		Object element = delegate.createElement(list);
+		list.add(element);
+
+		ListChangeEventTracker tracker = ListChangeEventTracker.observe(list);
+
+		final Object movedElement = list.move(0, 0);
+
+		assertEquals(
+				formatFail("IObservableList.move(int,int) should return the moved element"),
+				element, movedElement);
+		assertEquals(
+				formatFail("IObservableLIst.move(int,int) should not fire a change event"
+						+ "when the old and new indices are the same"), 0,
+				tracker.count);
+	}
+
+	public void testMove_ListChangeEvent() throws Exception {
+		final Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+		list.add(element1);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				assertSame(element0, list.move(0, 1));
+			}
+		}, "IObservableList.move(int, int)", list, Arrays.asList(new Object[] {
+				element1, element0 }));
+	}
+
+	public void testMove_ChangeEventFiredAfterElementIsMoved() throws Exception {
+		Object element0 = delegate.createElement(list);
+		Object element1 = delegate.createElement(list);
+		list.add(element0);
+		list.add(element1);
+
+		assertSame(element0, list.get(0));
+		assertSame(element1, list.get(1));
+
+		list.move(0, 1);
+
+		assertSame(element1, list.get(0));
+		assertSame(element0, list.get(1));
+	}
+
+	public void testMove_ListChangeEvent2() {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		Object element1 = delegate.createElement(list);
+		list.add(element1);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.move(0, 1);
+			}
+		}, "IObservableList.move(int, int)", list, Arrays.asList(new Object[] {
+				element1, element0 }));
+	}
+
+	public void testRemove_ListChangeEvent() throws Exception {
+		final Object element = delegate.createElement(list);
+		list.add(element);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.remove(element);
+			}
+		}, "List.remove(Object)", list, Collections.EMPTY_LIST);
+	}
+
+	public void testRemove_ListDiffEntry() throws Exception {
+		final Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+		list.add(element1);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.remove(element1);
+			}
+		}, "List.remove(Object)", list, Collections.singletonList(element0));
+	}
+
+	public void testRemoveAtIndex_ChangeEvent() throws Exception {
+		list.add(delegate.createElement(list));
+
+		assertChangeEventFired(new Runnable() {
+			public void run() {
+				list.remove(0);
+			}
+		}, "List.remove(int)", list);
+	}
+
+	public void testRemoveAtIndex_ListChangeEvent() throws Exception {
+		list.add(delegate.createElement(list));
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.remove(0);
+			}
+		}, "List.remove(int)", list, Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAtIndex_ChangeEventFiredAfterElementIsRemoved()
+			throws Exception {
+		final Object element = delegate.createElement(list);
+		list.add(element);
+
+		assertDoesNotContainDuringChangeEvent(new Runnable() {
+			public void run() {
+				list.remove(0);
+			}
+		}, "List.remove(int)", list, element);
+	}
+
+	public void testRemoveAtIndex_ListDiffEntry() throws Exception {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		Object element1 = delegate.createElement(list);
+		list.add(element1);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.remove(1);
+			}
+		}, "List.remove(int)", list, Collections.singletonList(element0));
+	}
+
+	public void testRemoveAll_ListChangeEvent() throws Exception {
+		final Object element = delegate.createElement(list);
+		list.add(element);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.removeAll(Collections.singletonList(element));
+			}
+		}, "List.removeAll(Collection)", list, Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAll_ListDiffEntry() throws Exception {
+		final Object element = delegate.createElement(list);
+		list.add(element);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.removeAll(Collections.singletonList(element));
+			}
+		}, "List.removeAll(Collection)", list, Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAll_ListDiffEntry2() throws Exception {
+		Object element0 = delegate.createElement(list);
+		list.add(element0);
+		final Object element1 = delegate.createElement(list);
+		list.add(element1);
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.removeAll(Arrays.asList(new Object[] { element1 }));
+			}
+		}, "List.removeAll(Collection)", list, Collections
+				.singletonList(element0));
+	}
+
+	public void testRetainAll_ListChangeEvent() throws Exception {
+		final Object element0 = delegate.createElement(list);
+		list.add(element0);
+		list.add(delegate.createElement(list));
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.retainAll(Arrays.asList(new Object[] { element0 }));
+			}
+		}, "List.retainAll(Collection", list, Collections
+				.singletonList(element0));
+	}
+
+	public void testRetainAll_ListDiffEntry() throws Exception {
+		final Object element = delegate.createElement(list);
+		list.add(element);
+		list.add(delegate.createElement(list));
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.retainAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "List.retainAll(Collection)", list, Collections
+				.singletonList(element));
+	}
+
+	public void testClear_ListChangeEvent() throws Exception {
+		list.add(delegate.createElement(list));
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.clear();
+			}
+		}, "List.clear()", list, Collections.EMPTY_LIST);
+	}
+
+	public void testClear_ListDiffEntry() throws Exception {
+		list.add(delegate.createElement(list));
+
+		assertListChangeEventFired(new Runnable() {
+			public void run() {
+				list.clear();
+			}
+		}, "List.clear()", list, Collections.EMPTY_LIST);
+	}
+
+	public void testClear_ClearsList() {
+		Object element = delegate.createElement(list);
+		list.add(element);
+		Assert.assertEquals(Collections.singletonList(element), list);
+		list.clear();
+		Assert.assertEquals(Collections.EMPTY_LIST, list);
+	}
+
+	private void assertListChangeEventFired(Runnable runnable,
+			String methodName, IObservableList list, List newList) {
+		List oldList = new ArrayList(list);
+
+		List queue = new ArrayList();
+		ListChangeEventTracker listListener = new ListChangeEventTracker(queue);
+		ChangeEventTracker changeListener = new ChangeEventTracker(queue);
+
+		list.addListChangeListener(listListener);
+		list.addChangeListener(changeListener);
+
+		runnable.run();
+
+		assertEquals(formatFail(methodName + " should fire one ListChangeEvent."), 1,
+				listListener.count);
+		assertEquals(formatFail(methodName
+				+ "'s change event observable should be the created List."),
+				list, listListener.event.getObservable());
+
+		assertEquals(formatFail("Two notifications should have been received."), 2, queue
+				.size());
+		assertEquals("ChangeEvent of " + methodName
+				+ " should have fired before the ListChangeEvent.",
+				changeListener, queue.get(0));
+		assertEquals("ListChangeEvent of " + methodName
+				+ " should have fired after the ChangeEvent.", listListener,
+				queue.get(1));
+
+		assertEquals(formatFail(methodName
+				+ " did not leave observable list with the expected contents"),
+				newList, list);
+
+		ListDiff diff = listListener.event.diff;
+		diff.applyTo(oldList);
+		assertEquals(
+				formatFail(methodName
+						+ " fired a diff which does not represent the expected list change"),
+				newList, oldList);
+
+	}
+
+	public static Test suite(IObservableCollectionContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				MutableObservableListContractTest.class, delegate)
+				.addObservableContractTest(ObservableListContractTest.class,
+						delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableSetContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableSetContractTest.java
new file mode 100644
index 0000000..8ff8cdb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableSetContractTest.java
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 2007-2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 215531, 221351, 213145
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ */
+public class MutableObservableSetContractTest extends
+		MutableObservableCollectionContractTest {
+	private IObservableCollectionContractDelegate delegate;
+
+	private IObservableSet set;
+
+	public MutableObservableSetContractTest(String testName,
+			IObservableCollectionContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	/**
+	 * @param delegate
+	 */
+	public MutableObservableSetContractTest(
+			IObservableCollectionContractDelegate delegate) {
+		super(delegate);
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		set = (IObservableSet) getObservable();
+	}
+
+	public void testAdd_SetChangeEvent() throws Exception {
+		assertSetChangeEventFired(new Runnable() {
+			public void run() {
+				set.add(delegate.createElement(set));
+			}
+		}, "Set.add(Object)", set);
+	}
+
+	public void testAdd_SetDiffEntry() throws Exception {
+		set.add(delegate.createElement(set));
+		final Object element = delegate.createElement(set);
+
+		assertAddDiffEntry(new Runnable() {
+			public void run() {
+				set.add(element);
+			}
+		}, "Set.add(Object)", set, element);
+	}
+
+	public void testAdd_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				set.add(delegate.createElement(set));
+			}
+		}, "Set.add(Object)", set);
+	}
+
+	public void testAddAll_SetChangeEvent() throws Exception {
+		assertSetChangeEventFired(new Runnable() {
+			public void run() {
+				set.addAll(Arrays.asList(new Object[] { delegate
+						.createElement(set) }));
+			}
+		}, "Set.addAll(Collection", set);
+	}
+
+	public void testAddAll_SetDiffEntry() throws Exception {
+		final Object element = delegate.createElement(set);
+
+		assertAddDiffEntry(new Runnable() {
+			public void run() {
+				set.addAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "Set.addAll(Collection)", set, element);
+	}
+
+	public void testAddAll_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				set.addAll(Collections.singleton(delegate.createElement(set)));
+			}
+		}, "Set.addAll(Collection)", set);
+	}
+
+	public void testRemove_SetChangeEvent() throws Exception {
+		final Object element = delegate.createElement(set);
+		set.add(element);
+
+		assertSetChangeEventFired(new Runnable() {
+			public void run() {
+				set.remove(element);
+			}
+		}, "Set.remove(Object)", set);
+	}
+
+	public void testRemove_SetDiffEntry() throws Exception {
+		set.add(delegate.createElement(set));
+		final Object element = delegate.createElement(set);
+		set.add(element);
+
+		assertRemoveDiffEntry(new Runnable() {
+			public void run() {
+				set.remove(element);
+			}
+		}, "Set.remove(Object)", set, element);
+	}
+
+	public void testRemove_GetterCalled() throws Exception {
+		final Object element = delegate.createElement(set);
+		set.add(element);
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				set.remove(element);
+			}
+		}, "Set.remove(Object)", set);
+	}
+
+	public void testRemoveAll_SetChangeEvent() throws Exception {
+		final Object element = delegate.createElement(set);
+		set.add(element);
+
+		assertSetChangeEventFired(new Runnable() {
+			public void run() {
+				set.removeAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "Set.removeAll(Collection)", set);
+	}
+
+	public void testRemoveAll_SetDiffEntry() throws Exception {
+		final Object element = delegate.createElement(set);
+		set.add(element);
+
+		assertRemoveDiffEntry(new Runnable() {
+			public void run() {
+				set.removeAll(Arrays.asList(new Object[] { element }));
+			}
+		}, "Set.removeAll(Collection)", set, element);
+	}
+
+	public void testRemoveAll_GetterCalled() throws Exception {
+		final Object element = delegate.createElement(set);
+		set.add(element);
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				set.removeAll(Collections.singleton(element));
+			}
+		}, "Set.removeAll(Collection)", set);
+	}
+
+	public void testRetainAll_SetChangeEvent() throws Exception {
+		final Object element1 = delegate.createElement(set);
+		set.add(element1);
+		set.add(delegate.createElement(set));
+
+		assertSetChangeEventFired(new Runnable() {
+			public void run() {
+				set.retainAll(Arrays.asList(new Object[] { element1 }));
+			}
+		}, "Set.retainAll(Collection", set);
+	}
+
+	public void testRetainAll_SetDiffEntry() throws Exception {
+		final Object element1 = delegate.createElement(set);
+		set.add(element1);
+		Object element2 = delegate.createElement(set);
+		set.add(element2);
+
+		assertRemoveDiffEntry(new Runnable() {
+			public void run() {
+				set.retainAll(Arrays.asList(new Object[] { element1 }));
+			}
+		}, "Set.retainAll(Collection)", set, element2);
+	}
+
+	public void testRetainAll_GetterCalled() throws Exception {
+		set.add(delegate.createElement(set));
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				set.retainAll(Collections.EMPTY_SET);
+			}
+		}, "Set.retainAll(Collection)", set);
+	}
+
+	public void testClear_SetChangeEvent() throws Exception {
+		set.add(delegate.createElement(set));
+
+		assertSetChangeEventFired(new Runnable() {
+			public void run() {
+				set.clear();
+			}
+		}, "Set.clear()", set);
+	}
+
+	public void testClear_SetDiffEntry() throws Exception {
+		Object element = delegate.createElement(set);
+		set.add(element);
+
+		assertRemoveDiffEntry(new Runnable() {
+			public void run() {
+				set.clear();
+			}
+		}, "Set.clear()", set, element);
+	}
+
+	public void testClear_GetterCalled() throws Exception {
+		set.add(delegate.createElement(set));
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				set.clear();
+			}
+		}, "Set.clear()", set);
+	}
+
+	/**
+	 * Asserts standard behaviors of firing set change events.
+	 * <ul>
+	 * <li>Event fires once.</li>
+	 * <li>Source of the event is the provided <code>set</code>.
+	 * <li>The set change event is fired after the change event.</li>
+	 * </ul>
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 * @param set
+	 */
+	private void assertSetChangeEventFired(Runnable runnable,
+			String methodName, IObservableSet set) {
+		List queue = new ArrayList();
+		SetChangeEventTracker setListener = new SetChangeEventTracker(queue);
+		ChangeEventTracker changeListener = new ChangeEventTracker(queue);
+
+		set.addSetChangeListener(setListener);
+		set.addChangeListener(changeListener);
+
+		runnable.run();
+
+		assertEquals(formatFail(methodName + " should fire one SetChangeEvent."), 1,
+				setListener.count);
+		assertEquals(formatFail(methodName
+				+ "'s change event observable should be the created Set."), set,
+				setListener.event.getObservable());
+
+		assertEquals(formatFail("Two notifications should have been received."), 2, queue
+				.size());
+		assertEquals(formatFail("ChangeEvent of " + methodName
+				+ " should have fired before the SetChangeEvent."),
+				changeListener, queue.get(0));
+		assertEquals(formatFail("SetChangeEvent of " + methodName
+				+ " should have fired after the ChangeEvent."), setListener,
+				queue.get(1));
+	}
+
+	/**
+	 * Asserts the set diff entry for an add operation.
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 * @param set
+	 * @param element
+	 */
+	private void assertAddDiffEntry(Runnable runnable, String methodName,
+			IObservableSet set, Object element) {
+		SetChangeEventTracker listener = new SetChangeEventTracker();
+		set.addSetChangeListener(listener);
+
+		runnable.run();
+
+		Set entries = listener.event.diff.getAdditions();
+		assertEquals(formatFail(methodName + " should result in one diff entry."), 1,
+				entries.size());
+
+		assertTrue(formatFail(methodName
+				+ " should result in a diff entry that is an addition."),
+				entries.contains(element));
+	}
+
+	/**
+	 * Asserts the set diff entry for a remove operation.
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 * @param set
+	 * @param element
+	 */
+	private void assertRemoveDiffEntry(Runnable runnable, String methodName,
+			IObservableSet set, Object element) {
+		SetChangeEventTracker listener = new SetChangeEventTracker();
+		set.addSetChangeListener(listener);
+
+		runnable.run();
+
+		Set entries = listener.event.diff.getRemovals();
+		assertEquals(formatFail(methodName + " should result in one diff entry."), 1,
+				entries.size());
+
+		assertTrue(formatFail(methodName
+				+ " should result in a diff entry that is a removal."),
+				entries.contains(element));
+	}
+
+	public static Test suite(IObservableCollectionContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				MutableObservableSetContractTest.class, delegate)
+				.addObservableContractTest(
+						ObservableCollectionContractTest.class, delegate)
+				.build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableValueContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableValueContractTest.java
new file mode 100644
index 0000000..e03b0aa
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/MutableObservableValueContractTest.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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 213145
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+
+/**
+ * Mutability tests for IObservableValue.
+ * 
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class MutableObservableValueContractTest extends
+		ObservableDelegateTest {
+	private IObservableValueContractDelegate delegate;
+
+	private IObservableValue observable;
+
+	/**
+	 * @param delegate
+	 */
+	public MutableObservableValueContractTest(
+			IObservableValueContractDelegate delegate) {
+		this(null, delegate);
+	}
+
+	public MutableObservableValueContractTest(String testName,
+			IObservableValueContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		this.observable = (IObservableValue) getObservable();
+	}
+
+	public void testSetValue_SetsValue() throws Exception {
+		Object value = delegate.createValue(observable);
+		
+		observable.setValue(value);
+		assertEquals(formatFail("IObservableValue.setValue(Object) should set the value of the observable."), value, observable.getValue());
+	}
+	
+	public void testSetValue_ChangeEvent() throws Exception {
+		ChangeEventTracker listener = ChangeEventTracker.observe(observable);
+		
+		observable.setValue(delegate.createValue(observable));
+		
+		assertEquals(formatFail("Change event listeners were not notified"), 1, listener.count);
+		assertEquals(formatFail("IObservableValue.setValue(Object) should fire one ChangeEvent."), 1,
+				listener.count);
+		assertEquals(
+				formatFail("IObservableValue.setValue(Object)'s change event observable should be the created observable."),
+				observable, listener.event.getObservable());
+	}
+	
+	public void testSetValue_SameValue() throws Exception {
+		// invoke change to ensure observable has a value
+		delegate.change(observable);
+
+		ValueChangeEventTracker valueChangeListener = ValueChangeEventTracker.observe(observable);
+		ChangeEventTracker changeListener = ChangeEventTracker.observe(observable);
+		Object value = observable.getValue();
+		observable.setValue(value);
+
+		assertEquals(
+				formatFail("IObservableValue.setValue() should not fire a value change event when the value has not change."),
+				0, valueChangeListener.count);
+		assertEquals(
+				formatFail("IObservableValue.setValue() should not fire a change event when the value has not change."),
+				0, changeListener.count);
+	}
+	
+	public void testSetValue_RealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				observable.setValue(delegate.createValue(observable));
+			}
+		}, (CurrentRealm) observable.getRealm());
+	}
+
+	public static Test suite(IObservableValueContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				MutableObservableValueContractTest.class, delegate)
+				.addObservableContractTest(ObservableValueContractTest.class,
+						delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableCollectionContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableCollectionContractTest.java
new file mode 100644
index 0000000..e744a72
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableCollectionContractTest.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 213145, 274450
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import java.util.Arrays;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ * Tests for IObservableCollection that don't mutate the collection.
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class ObservableCollectionContractTest extends ObservableContractTest {
+	private IObservableCollectionContractDelegate delegate;
+
+	private IObservableCollection collection;
+
+	public ObservableCollectionContractTest(
+			IObservableCollectionContractDelegate delegate) {
+		super(delegate);
+		this.delegate = delegate;
+	}
+
+	public ObservableCollectionContractTest(String testName,
+			IObservableCollectionContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		collection = (IObservableCollection) getObservable();
+	}
+
+	public void testIterator_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.iterator();
+			}
+		}, "Collection.iterator()", collection);
+	}
+
+	public void testIterator_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.iterator();
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testSize_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.size();
+			}
+		}, "Collection.size()", collection);
+	}
+
+	public void testSize_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.size();
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testIsEmpty_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.isEmpty();
+			}
+		}, "Collection.isEmpty()", collection);
+	}
+
+	public void testIsEmpty_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.isEmpty();
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testContains_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.contains(delegate.createElement(collection));
+			}
+		}, "Collection.contains(...)", collection);
+	}
+
+	public void testContains_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.contains(delegate.createElement(collection));
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testContainsAll_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.containsAll(Arrays.asList(new Object[] { delegate
+						.createElement(collection) }));
+			}
+		}, "Collection.containsAll(Collection)", collection);
+	}
+
+	public void testContainsAll_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.containsAll(Arrays.asList(new Object[] { delegate
+						.createElement(collection) }));
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testToArray_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.toArray();
+			}
+		}, "Collection.toArray()", collection);
+	}
+
+	public void testToArray_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.toArray();
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testToArrayWithObjectArray_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.toArray(new Object[collection.size()]);
+			}
+		}, "Collection.toArray(Object[])", collection);
+	}
+
+	public void testToArrayWithObjectArray_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.toArray(new Object[collection.size()]);
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testEquals_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.equals(collection);
+			}
+		}, "Collection.equals(Object)", collection);
+	}
+
+	public void testEquals_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.equals(collection);
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testEquals_IdentityEquals() throws Exception {
+		assertTrue(collection.equals(collection));
+	}
+
+	public void testHashCode_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				collection.hashCode();
+			}
+		}, "Collection.hashCode()", collection);
+	}
+
+	public void testHashCode_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				collection.hashCode();
+			}
+		}, (CurrentRealm) collection.getRealm());
+	}
+
+	public void testGetElementType_ReturnsType() throws Exception {
+		assertEquals(
+				"Element type of the collection should be returned from IObservableCollection.getElementType()",
+				delegate.getElementType(collection), collection
+						.getElementType());
+	}
+
+	public static Test suite(IObservableCollectionContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				ObservableCollectionContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableContractTest.java
new file mode 100644
index 0000000..4e2071c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableContractTest.java
@@ -0,0 +1,234 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 208322, 221351, 208858, 146397, 249526
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ * Tests for IObservable that don't require mutating the observable.
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class ObservableContractTest extends ObservableDelegateTest {
+	private IObservable observable;
+
+	private IObservableContractDelegate delegate;
+
+	public ObservableContractTest(IObservableContractDelegate delegate) {
+		this(null, delegate);
+	}
+
+	public ObservableContractTest(String testName,
+			IObservableContractDelegate delegate) {
+		super(testName, delegate);
+		
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		observable = getObservable();
+	}
+
+	public void testConstruction_CallsObservableCreated() {
+		final IObservable[] created = new IObservable[1];
+		IObservable[] collected = ObservableTracker.runAndCollect(new Runnable() {
+			public void run() {
+				created[0] = delegate.createObservable(new CurrentRealm(true));
+			}
+		});
+		assertTrue(collected.length > 0);
+		boolean wasCollected = false;
+		for (int i = 0; i < collected.length; i++) {
+			if (collected[i] == created[0])
+				wasCollected = true;
+		}
+		assertTrue(wasCollected);
+	}
+
+	public void testGetRealm_NotNull() throws Exception {
+		assertNotNull(formatFail("The observable's realm should not be null."), observable
+				.getRealm());
+	}
+
+	public void testChange_ChangeEvent() throws Exception {
+		ChangeListener listener = new ChangeListener();
+
+		observable.addChangeListener(listener);
+		delegate.change(observable);
+
+		assertEquals(
+				formatFail("A change in the observable should notify change listeners."),
+				1, listener.count);
+	}
+
+	public void testChange_EventObservable() throws Exception {
+		ChangeListener listener = new ChangeListener();
+
+		observable.addChangeListener(listener);
+		delegate.change(observable);
+
+		ChangeEvent event = listener.event;
+		assertNotNull(formatFail("change event was null"), event);
+
+		assertSame(
+				formatFail("In the change event the source of the change should be the observable."),
+				observable, event.getObservable());
+	}
+
+	public void testChange_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				delegate.change(observable);
+			}			
+		}, (CurrentRealm) observable.getRealm());
+	}
+	
+	public void testChange_ObservableRealmIsTheCurrentRealm() throws Exception {
+		ChangeListener listener = new ChangeListener();
+		observable.addChangeListener(listener);
+
+		delegate.change(observable);
+		assertTrue(
+				formatFail("On change the current realm should be the realm of the observable."),
+				listener.isCurrentRealm);
+	}
+
+	public void testRemoveChangeListener_RemovesListener() throws Exception {
+		ChangeListener listener = new ChangeListener();
+
+		observable.addChangeListener(listener);
+		delegate.change(observable);
+
+		// precondition check
+		assertEquals(formatFail("change did not notify listeners"), 1, listener.count);
+
+		observable.removeChangeListener(listener);
+		delegate.change(observable);
+
+		assertEquals(
+				formatFail("When a change listener is removed it should not still receive change events."),
+				1, listener.count);
+	}
+
+	public void testIsStale_NotStale() throws Exception {
+		delegate.setStale(observable, false);
+		assertFalse(
+				formatFail("When an observable is not stale isStale() should return false."),
+				observable.isStale());
+	}
+	
+	public void testIsStale_RealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				observable.isStale();
+			}
+		}, (CurrentRealm) observable.getRealm());
+	}
+
+	public void testIsStale_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				observable.isStale();
+			}
+		}, "isStale", observable);
+	}
+
+	public void testIsDisposed() throws Exception {
+		assertFalse(observable.isDisposed());
+		observable.dispose();
+		assertTrue(observable.isDisposed());
+	}
+
+	public void testAddDisposeListener_HandleDisposeInvoked() {
+		DisposeEventTracker tracker = DisposeEventTracker.observe(observable);
+		assertEquals(0, tracker.count);
+		observable.dispose();
+		assertEquals(1, tracker.count);
+		assertSame(observable, tracker.event.getSource());
+	}
+
+	public void testHandleDispose_IsDisposedTrue() {
+		// Ensures observable.isDisposed() == true before
+		// the dispose listeners are called
+		observable.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+				assertTrue(observable.isDisposed());
+			}
+		});
+		observable.dispose();
+	}
+
+	public void testDispose_RemovesListeners() throws Exception {
+		ChangeListener disposedObservableListener = new ChangeListener();
+		Realm realm = observable.getRealm();
+		
+		observable.addChangeListener(disposedObservableListener);
+		observable.dispose();
+		
+		//create a new observable to fire a change from
+		observable = delegate.createObservable(realm);
+		delegate.change(observable);
+		
+		assertEquals(
+				formatFail("After being disposed listeners should not receive change events."),
+				0, disposedObservableListener.count);
+	}
+
+	public void testDispose_PreservesRealm() throws Exception {
+		Realm realm = observable.getRealm();
+
+		observable.dispose();
+
+		assertSame(realm, observable.getRealm());
+	}
+
+	/* package */static class ChangeListener implements IChangeListener {
+		int count;
+
+		ChangeEvent event;
+
+		boolean isCurrentRealm;
+
+		public void handleChange(ChangeEvent event) {
+			count++;
+			this.event = event;
+			this.isCurrentRealm = event.getObservable().getRealm().isCurrent();
+		}
+	}
+
+	public static Test suite(IObservableContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				ObservableContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableDelegateTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableDelegateTest.java
new file mode 100644
index 0000000..f740539
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableDelegateTest.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * 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 221351
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * TestCase that provides the standard behavior expected for delegating test cases.
+ * 
+ * @since 3.2
+ */
+public class ObservableDelegateTest extends TestCase {
+	private IObservableContractDelegate delegate;
+
+	private Realm previousRealm;
+
+	private IObservable observable;
+	private String debugInfo;
+
+	public ObservableDelegateTest(IObservableContractDelegate delegate) {
+		this(null, delegate);
+	}
+	
+	public ObservableDelegateTest(String testName, IObservableContractDelegate delegate) {
+		super(testName);
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		previousRealm = Realm.getDefault();
+
+		delegate.setUp();
+		observable = doCreateObservable();
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		delegate.tearDown();
+		observable.dispose();
+		observable = null;
+		
+		RealmTester.setDefault(previousRealm);
+
+		observable = null;
+		previousRealm = null;
+	}
+	
+	/**
+	 * Creates a new observable with a default realm. Invoked from
+	 * {@link #setUp()}. Override to customize the creation of observables
+	 * (e.g. specifying a different Realm).
+	 * 
+	 * @return observable
+	 */
+	protected IObservable doCreateObservable() {
+		return delegate.createObservable(new CurrentRealm(true));
+	}
+
+	/**
+	 * Returns the created observable. The observable is created in
+	 * {@link #setUp()}. If invoked before {@link #setUp()} will be
+	 * <code>null</code>.
+	 * 
+	 * @return observable
+	 */
+	protected IObservable getObservable() {
+		return observable;
+	}	
+	
+	/**
+	 * Returns the delegate in use.
+	 * 
+	 * @return delegate
+	 */
+	protected IObservableContractDelegate getObservableContractDelegate() {
+		return delegate;
+	}
+	
+	protected String formatFail(String message) {
+		return message + getDebugString();
+	}
+	
+	private String getDebugString() {
+		if (debugInfo == null) {
+			debugInfo = "(Test: " + this.getClass().getName() + ", Delegate: " + delegate.getClass().getName() + ")";
+		}
+		
+		return debugInfo;
+	}
+
+	/**
+	 * Asserts that ObservableTracker.getterCalled(...) is invoked when the
+	 * provided <code>runnable</code> is invoked.
+	 * 
+	 * @param runnable
+	 * @param methodName
+	 *            method name to display when displaying a message
+	 * @param observable
+	 *            observable that should be collected by ObservableTracker
+	 */
+	protected void assertGetterCalled(Runnable runnable, String methodName, IObservable observable) {
+		IObservable[] observables = ObservableTracker.runAndMonitor(runnable,
+				null, null);
+	
+		int count = 0;
+		for (int i = 0; i < observables.length; i++) {
+			if (observables[i] == observable) {
+				count++;
+			}
+		}
+		
+		assertEquals(formatFail(methodName
+				+ " should invoke ObservableTracker.getterCalled() once."), 1,
+				count);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableListContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableListContractTest.java
new file mode 100644
index 0000000..a0e48c1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableListContractTest.java
@@ -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
+ *     Matthew Hall - bug 213145
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ * Tests for IObservableList that don't require mutating the collection.
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class ObservableListContractTest extends
+		ObservableCollectionContractTest {
+	private IObservableList list;
+
+	private IObservableCollectionContractDelegate delegate;
+
+	/**
+	 * @param delegate
+	 */
+	public ObservableListContractTest(
+			IObservableCollectionContractDelegate delegate) {
+		super(delegate);
+		this.delegate = delegate;
+	}
+
+	public ObservableListContractTest(String testName,
+			IObservableCollectionContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		list = (IObservableList) getObservable();
+	}
+
+	public void testListIterator_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				list.listIterator();
+			}
+		}, "List.listIterator()", list);
+	}
+
+	public void testGet_GetterCalled() throws Exception {
+		list = (IObservableList) delegate.createObservableCollection(new CurrentRealm(true), 1);
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				list.get(0);
+			}
+		}, "List.get(int)", list);
+	}
+
+	public void testIndexOf_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				list.indexOf(delegate.createElement(list));
+			}
+		}, "List.indexOf(int)", list);
+	}
+
+	public void testLastIndexOf_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				list.lastIndexOf(delegate.createElement(list));
+			}
+		}, "List.lastIndexOf(Object)", list);
+	}
+
+	public void testListIteratorAtIndex_GetterCalled() throws Exception {
+		// Create a new list instead of adding an item because the list might
+		// not be mutable
+		list = (IObservableList) delegate.createObservableCollection(new CurrentRealm(true), 1);
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				list.listIterator(0);
+			}
+		}, "List.listIterator(int)", list);
+	}
+
+	public void testSubList_GetterCalled() throws Exception {
+		list = (IObservableList) delegate.createObservableCollection(new CurrentRealm(true), 1);
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				list.subList(0, 1);
+			}
+		}, "List.subList(int, int)", list);
+	}
+
+	public static Test suite(IObservableCollectionContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				ObservableListContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableStaleContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableStaleContractTest.java
new file mode 100644
index 0000000..482ed77
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableStaleContractTest.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IStaleListener;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+/**
+ * @since 3.3
+ */
+public class ObservableStaleContractTest extends ObservableDelegateTest {
+	private IObservableContractDelegate delegate;
+	private IObservable observable;
+	
+	public ObservableStaleContractTest(IObservableContractDelegate delegate) {
+		this(null, delegate);
+	}
+	
+	public ObservableStaleContractTest(String testName, IObservableContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+	
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		observable = getObservable();
+	}
+	
+	public void testIsStale_TrueWhenStale() throws Exception {
+		delegate.setStale(observable, true);
+		assertTrue(formatFail("When stale isStale() should return true."), observable.isStale());
+	}
+	
+	public void testIsStale_FalseWhenNotStale() throws Exception {
+		delegate.setStale(observable, false);
+		assertFalse(formatFail("When not stale isStale() should return false."), observable.isStale());
+	}
+
+	public void testBecomingStaleFiresStaleEvent() throws Exception {
+		StaleListener listener = new StaleListener();
+
+		// precondition
+		ensureStale(observable, false);
+
+		observable.addStaleListener(listener);
+		delegate.setStale(observable, true);
+
+		assertEquals(formatFail("When becoming stale listeners should be notified."), 1, listener.count);
+	}
+
+	public void testStaleEventObservable() throws Exception {
+		StaleListener listener = new StaleListener();
+
+		// precondition
+		ensureStale(observable, false);
+
+		observable.addStaleListener(listener);
+		delegate.setStale(observable, true);
+
+		StaleEvent event = listener.event;
+		assertNotNull(formatFail("stale event was null"), event);
+		assertEquals(formatFail("When notifying listeners of becoming stale the observable should be the source of the event."), observable,
+				event.getObservable());
+	}
+
+	public void testRemoveStaleListener_RemovesListener() throws Exception {
+		StaleListener listener = new StaleListener();
+
+		observable.addStaleListener(listener);
+		ensureStale(observable, false);
+		delegate.setStale(observable, true);
+
+		// precondition check
+		assertEquals(formatFail("set stale did not notify listeners"), 1, listener.count);
+
+		observable.removeStaleListener(listener);
+		ensureStale(observable, false);
+		delegate.setStale(observable, true);
+
+		assertEquals(formatFail("Once removed stale listeners should not be notified of becoming stale."), 1,
+				listener.count);
+	}
+
+	public void testStaleListenersAreNotNotifiedWhenObservableIsNoLongerStale()
+			throws Exception {
+		ensureStale(observable, true);
+
+		StaleListener listener = new StaleListener();
+		observable.addStaleListener(listener);
+		delegate.setStale(observable, false);
+
+		assertEquals(formatFail("Stale listeners should not be notified when the stale state changes from true to false."), 0,
+				listener.count);
+	}
+
+	public void testObservableRealmIsCurrentOnStale() throws Exception {
+		ensureStale(observable, false);
+
+		StaleListener listener = new StaleListener();
+		observable.addStaleListener(listener);
+		delegate.setStale(observable, true);
+
+		assertTrue(formatFail("When notifying listeners of becoming stale the observable's realm should be the current realm."),
+				listener.isCurrentRealm);
+	}
+	
+	/**
+	 * Ensures that stale is set to the provided state. Will throw an
+	 * AssertionFailedError if setting of the state is unsuccessful.
+	 * 
+	 * @param observable
+	 * @param stale
+	 */
+	private void ensureStale(IObservable observable, boolean stale) {
+		if (observable.isStale() != stale) {
+			delegate.setStale(observable, stale);
+		}
+
+		assertEquals(stale, observable.isStale());
+	}
+	
+	/* package */static class StaleListener implements IStaleListener {
+		int count;
+
+		StaleEvent event;
+
+		boolean isCurrentRealm;
+
+		public void handleStale(StaleEvent staleEvent) {
+			count++;
+			this.event = staleEvent;
+			this.isCurrentRealm = staleEvent.getObservable().getRealm()
+					.isCurrent();
+		}
+	}
+
+	public static Test suite(IObservableContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				ObservableStaleContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableValueContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableValueContractTest.java
new file mode 100644
index 0000000..d55c12e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/ObservableValueContractTest.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+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.jface.databinding.conformance.delegate.IObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+
+/**
+ * @since 3.2
+ */
+public class ObservableValueContractTest extends ObservableContractTest {
+	private IObservableValueContractDelegate delegate;
+	private IObservableValue observable;
+
+	public ObservableValueContractTest(IObservableValueContractDelegate delegate) {
+		super(delegate);
+		this.delegate = delegate;			
+	}
+
+	/**
+	 * @param testName
+	 * @param delegate
+	 */
+	public ObservableValueContractTest(String testName,
+			IObservableValueContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.databinding.conformance.ObservableContractTest#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		observable = (IObservableValue) getObservable();
+	}
+
+	public void testChange_ValueChangeEvent() throws Exception {
+		ValueChangeEventTracker listener = ValueChangeEventTracker.observe(observable);
+
+		delegate.change(observable);
+		assertEquals(formatFail("On change value change listeners should be notified."), 1,
+				listener.count);
+	}
+
+	public void testGetValueType_ExpectedType() throws Exception {
+		assertEquals(formatFail("Type of the value should be returned from getType()."),
+				delegate.getValueType(observable), observable.getValueType());
+	}
+
+	public void testChange_OrderOfNotifications() throws Exception {
+		final List listeners = new ArrayList();
+		IChangeListener changeListener = new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				listeners.add(this);
+			}
+		};
+
+		IValueChangeListener valueChangeListener = new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				listeners.add(this);
+			}
+		};
+
+		observable.addChangeListener(changeListener);
+		observable.addValueChangeListener(valueChangeListener);
+
+		delegate.change(observable);
+		
+		assertTrue(formatFail("Change Listeners were not notified on change."), listeners.size() > 0);
+		
+		// not asserting the fact that both are notified as this is asserted in
+		// other tests
+		assertEquals(
+				formatFail("Change listeners should be notified before value change listeners."),
+				changeListener, listeners.get(0));
+		assertEquals(
+				formatFail("Value change listeners should be notified after change listeners."),
+				valueChangeListener, listeners.get(1));
+	}
+
+	public void testChange_ValueChangeEventDiff() throws Exception {
+		ValueChangeEventTracker listener = ValueChangeEventTracker.observe(observable);
+		Object oldValue = observable.getValue();
+
+		delegate.change(observable);
+
+		ValueChangeEvent event = listener.event;
+		
+		assertTrue(formatFail("Change Listeners were not notified on change."), listener.count > 0);
+		
+		assertEquals(
+				formatFail("When a value change event is fired the old value should be the previous value of the observable value."),
+				oldValue, event.diff.getOldValue());
+		assertEquals(
+				formatFail("When a value change event is fired the new value should be the same as the current value of the observable value."),
+				observable.getValue(), event.diff.getNewValue());
+	}
+
+	public void testChange_ValueChangeEventFiredAfterValueIsSet()
+			throws Exception {
+		class ValueChangeListener implements IValueChangeListener {
+			Object value;
+
+			public void handleValueChange(ValueChangeEvent event) {
+				this.value = event.getObservableValue().getValue();
+			}
+		}
+		
+		ValueChangeListener listener = new ValueChangeListener();
+		observable.addValueChangeListener(listener);
+		delegate.change(observable);
+		assertEquals(
+				formatFail("When a value change event is fired the new value should be applied before firing the change event."),
+				listener.value, observable.getValue());
+	}
+
+	public void testRemoveValueChangeListener_RemovesListener() throws Exception {
+		ValueChangeEventTracker listener = ValueChangeEventTracker.observe(observable);
+		delegate.change(observable);
+
+		// precondition
+		assertEquals(formatFail("Value change listeners should be notified on change."), 1,
+				listener.count);
+
+		observable.removeValueChangeListener(listener);
+		delegate.change(observable);
+
+		assertEquals(
+				formatFail("Value change listeners should not be notified after they've been removed from the observable."),
+				1, listener.count);
+	}
+
+	public void testGetValue_GetterCalled() throws Exception {
+		assertGetterCalled(new Runnable() {
+			public void run() {
+				observable.getValue();
+			}
+		}, formatFail("IObservableValue.getValue()"), observable);
+	}
+
+	public void testGetValue_RealmCheck() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				observable.getValue();
+			}
+		}, (CurrentRealm) observable.getRealm());
+	}
+
+	public static Test suite(IObservableValueContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				ObservableValueContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableCollectionContractDelegate.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableCollectionContractDelegate.java
new file mode 100644
index 0000000..5455d7f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableCollectionContractDelegate.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.delegate;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+
+/**
+ * Abstract implementation of {@link IObservableCollectionContractDelegate}.
+ * 
+ * @since 3.2
+ */
+public abstract class AbstractObservableCollectionContractDelegate extends
+		AbstractObservableContractDelegate implements
+		IObservableCollectionContractDelegate {
+
+	/**
+	 * Invokes {@link IObservableCollectionContractDelegate#createObservableCollection(Realm, int)}.
+	 * @param realm 
+	 * @return observable
+	 */
+	public final IObservable createObservable(Realm realm) {
+		return createObservableCollection(realm, 0);
+	}
+	
+	public Object createElement(IObservableCollection collection) {
+		//no op
+		return null;
+	}
+
+	public Object getElementType(IObservableCollection collection) {
+		//no op
+		return null;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableContractDelegate.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableContractDelegate.java
new file mode 100644
index 0000000..8146760
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableContractDelegate.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.delegate;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * Abstract implementation of {@link IObservableContractDelegate}.
+ * 
+ * @since 1.1
+ */
+public abstract class AbstractObservableContractDelegate implements
+		IObservableContractDelegate {
+
+	public void setUp() {
+		// no op
+	}
+
+	public void tearDown() {
+		// no op
+	}
+
+	public void change(IObservable observable) {
+		// no op
+	}
+
+	public void setStale(IObservable observable, boolean stale) {
+		// no op
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableValueContractDelegate.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableValueContractDelegate.java
new file mode 100644
index 0000000..6de2c2c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/AbstractObservableValueContractDelegate.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.delegate;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * Abstract implementation of {@link IObservableValueContractDelegate}.
+ * 
+ * @since 1.1
+ */
+public abstract class AbstractObservableValueContractDelegate extends
+		AbstractObservableContractDelegate implements
+		IObservableValueContractDelegate {
+
+	/**
+	 * Invokes {@link #createObservableValue(Realm)}.
+	 * @param realm 
+	 * @return observable
+	 */
+	public final IObservable createObservable(Realm realm) {
+		return createObservableValue(realm);
+	}
+
+	/**
+	 * Default implementation returns <code>null</code>.
+	 * @param observable 
+	 * @return value type
+	 */
+	public Object getValueType(IObservableValue observable) {
+		// no op
+		return null;
+	}
+	
+	/**
+	 * Default implementation returns <code>null</code>.
+	 * @param observable 
+	 * @return value
+	 */
+	public Object createValue(IObservableValue observable) {
+		//no op
+		return null;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableCollectionContractDelegate.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableCollectionContractDelegate.java
new file mode 100644
index 0000000..5fb412c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableCollectionContractDelegate.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.delegate;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+
+/**
+ * Delegate interface for an IObservableCollection.
+ * 
+ * <p>
+ * 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.1
+ */
+public interface IObservableCollectionContractDelegate extends
+		IObservableContractDelegate {
+	/**
+	 * Creates a new observable collection with the provided
+	 * <code>elementCount</code>.
+	 * 
+	 * @param realm realm of the collection
+	 * @param elementCount
+	 *            number of elements to initialize the collection with
+	 * 
+	 * @return new observable collection
+	 */
+	public IObservableCollection createObservableCollection(Realm realm, int elementCount);
+
+	/**
+	 * Creates a new element of the appropriate type for the provided
+	 * <code>collection</code>. This element will be employed to assert the
+	 * addition and removal of elements in the collection.
+	 * 
+	 * @param collection
+	 * @return valid element for the collection
+	 */
+	public Object createElement(IObservableCollection collection);
+
+	/**
+	 * Returns the expected type of the elements in the collection.
+	 * 
+	 * @param collection
+	 * @return element type
+	 */
+	public Object getElementType(IObservableCollection collection);
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableContractDelegate.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableContractDelegate.java
new file mode 100644
index 0000000..bcd59bb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableContractDelegate.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.delegate;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+
+/**
+ * Delegate interface for observables.
+ * 
+ * <p>
+ * 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.1
+ */
+public interface IObservableContractDelegate {
+	/**
+	 * Notifies the delegate of the start of a test.
+	 */
+	public void setUp();
+
+	/**
+	 * Notifies the delegate of the end of a test.
+	 */
+	public void tearDown();
+
+	/**
+	 * Invokes an operation to set the stale state of the provided
+	 * <code>observable</code>.
+	 * 
+	 * @param observable
+	 * @param stale
+	 */
+	public void setStale(IObservable observable, boolean stale);
+
+	/**
+	 * Creates a new observable.
+	 * 
+	 * @param realm realm of the observable
+	 * @return observable
+	 */
+	public IObservable createObservable(Realm realm);
+
+	/**
+	 * Invokes a change operation resulting in a change event being fired from
+	 * the observable. The preferred approach is to change the observed object
+	 * resulting in an observable event. There is no guaranteed as to the state
+	 * of the observable when invoked. The observable could be disposed to
+	 * assert the proper behavior of dispose.
+	 * 
+	 * @param observable
+	 */
+	public void change(IObservable observable);
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableValueContractDelegate.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableValueContractDelegate.java
new file mode 100644
index 0000000..873b005
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/delegate/IObservableValueContractDelegate.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.delegate;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * Delegate interface for an observable value.
+ * 
+ * <p>
+ * 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.1
+ */
+public interface IObservableValueContractDelegate extends
+		IObservableContractDelegate {
+
+	/**
+	 * Creates a new observable value.
+	 * 
+	 * @param realm
+	 *            realm of the observable
+	 * @return observable value
+	 */
+	public IObservableValue createObservableValue(Realm realm);
+
+	/**
+	 * Returns the expected type of the observable.
+	 * 
+	 * @param observable
+	 * @return type
+	 */
+	public Object getValueType(IObservableValue observable);
+	
+	/**
+	 * Returns a valid value that is not the current value of the observable.
+	 * 
+	 * @param observable
+	 * @return value
+	 */
+	public Object createValue(IObservableValue observable);
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/swt/SWTMutableObservableValueContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/swt/SWTMutableObservableValueContractTest.java
new file mode 100644
index 0000000..b16fb6e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/swt/SWTMutableObservableValueContractTest.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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 213145
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.swt;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.DelegatingRealm;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Mutability tests for IObservableValue for a SWT widget.
+ * 
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class SWTMutableObservableValueContractTest extends
+		MutableObservableValueContractTest {
+	private IObservableValueContractDelegate delegate;
+
+	public SWTMutableObservableValueContractTest(
+			IObservableValueContractDelegate delegate) {
+		this(null, delegate);
+	}
+
+	/**
+	 * @param testName
+	 * @param delegate
+	 */
+	public SWTMutableObservableValueContractTest(String testName,
+			IObservableValueContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	/**
+	 * Creates a new observable passing the realm for the current display.
+	 * @return observable
+	 */
+	protected IObservable doCreateObservable() {
+		Display display = Display.getCurrent();
+		if (display == null) {
+			display = new Display();
+		}
+		DelegatingRealm delegateRealm = new DelegatingRealm(SWTObservables
+				.getRealm(display));
+		delegateRealm.setCurrent(true);
+
+		return delegate.createObservable(delegateRealm);
+	}
+
+	public static Test suite(IObservableValueContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(
+				SWTMutableObservableValueContractTest.class, delegate)
+				.addObservableContractTest(
+						SWTObservableValueContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/swt/SWTObservableValueContractTest.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/swt/SWTObservableValueContractTest.java
new file mode 100644
index 0000000..ef21669
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/swt/SWTObservableValueContractTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 213145
+ *******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.swt;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.jface.databinding.conformance.ObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.DelegatingRealm;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Tests for IObservableValue that don't mutate the value.
+ * <p>
+ * This class is experimental and can change at any time. It is recommended to
+ * not subclass or assume the test names will not change. The only API that is
+ * guaranteed to not change are the constructors. The tests will remain public
+ * and not final in order to allow for consumers to turn off a test if needed by
+ * subclassing.
+ * </p>
+ * 
+ * @since 3.2
+ */
+public class SWTObservableValueContractTest extends ObservableValueContractTest {
+	private IObservableValueContractDelegate delegate;
+
+	/**
+	 * @param delegate
+	 */
+	public SWTObservableValueContractTest(
+			IObservableValueContractDelegate delegate) {
+		this(null, delegate);
+	}
+
+	public SWTObservableValueContractTest(String testName,
+			IObservableValueContractDelegate delegate) {
+		super(testName, delegate);
+		this.delegate = delegate;
+	}
+
+	/**
+	 * Creates a new observable passing the realm for the current display.
+	 * @return observable
+	 */
+	protected IObservable doCreateObservable() {
+		Display display = Display.getCurrent();
+		if (display == null) {
+			display = new Display();
+		}
+		DelegatingRealm delegateRealm = new DelegatingRealm(SWTObservables
+				.getRealm(display));
+		delegateRealm.setCurrent(true);
+
+		return delegate.createObservable(delegateRealm);
+	}
+
+	public static Test suite(IObservableValueContractDelegate delegate) {
+		return new SuiteBuilder().addObservableContractTest(SWTObservableValueContractTest.class, delegate).build();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ChangeEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ChangeEventTracker.java
new file mode 100644
index 0000000..1e38756
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ChangeEventTracker.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * Listener for tracking the firing of ChangeEvents.
+ */
+public class ChangeEventTracker implements IChangeListener {
+	public int count;
+	public ChangeEvent event;
+
+	/**
+	 * Queue that the listener will add itself too when it is notified of an
+	 * event. Used to determine order of notifications of listeners.  Can be null.
+	 */
+	public final List queue;
+
+	public ChangeEventTracker() {
+		queue = null;
+	}
+
+	public ChangeEventTracker(List notificationQueue) {
+		this.queue = notificationQueue;
+	}
+
+	public void handleChange(ChangeEvent event) {
+		count++;
+		this.event = event;
+		if (queue != null) {
+			queue.add(this);
+		}
+	}
+
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static ChangeEventTracker observe(IObservable observable) {
+		ChangeEventTracker tracker = new ChangeEventTracker();
+		observable.addChangeListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/CurrentRealm.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/CurrentRealm.java
new file mode 100644
index 0000000..7cddbed
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/CurrentRealm.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 268688
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Realm;
+
+/**
+ * Allows for the toggling of the current status of the realm. The
+ * asyncExec(...) implementations do nothing.
+ * 
+ * @since 3.2
+ */
+public class CurrentRealm extends Realm {
+	private boolean current;
+	private List queue = new LinkedList();
+
+	public CurrentRealm() {
+		this(false);
+	}
+
+	public CurrentRealm(boolean current) {
+		this.current = current;
+	}
+
+	public boolean isCurrent() {
+		return current;
+	}
+
+	public void setCurrent(boolean current) {
+		this.current = current;
+		processTasks();
+	}
+
+	protected void syncExec(Runnable runnable) {
+		super.syncExec(runnable);
+	}
+
+	private void processTasks() {
+		if (isCurrent()) {
+			for (Iterator it = queue.iterator(); it.hasNext();) {
+				Runnable task = (Runnable) it.next();
+				it.remove();
+				safeRun(task);
+			}
+		}
+	}
+
+	public void asyncExec(Runnable runnable) {
+		queue.add(runnable);
+	}
+
+	protected static Realm setDefault(Realm realm) {
+		return Realm.setDefault(realm);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/DelegatingRealm.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/DelegatingRealm.java
new file mode 100644
index 0000000..229d5fc
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/DelegatingRealm.java
@@ -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
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import org.eclipse.core.databinding.observable.Realm;
+
+/**
+ * Realm that will delegate to another for all operations except calls to
+ * {@link #isCurrent()}. The current status can be set by the consumer to
+ * enable testing of realm checks.
+ * 
+ * @since 3.2
+ */
+public class DelegatingRealm extends CurrentRealm {
+	private Realm realm;
+
+	public DelegatingRealm(Realm realm) {
+		this.realm = realm;
+	}
+
+	protected void syncExec(Runnable runnable) {
+		realm.exec(runnable);
+	}
+
+	public void asyncExec(Runnable runnable) {
+		realm.asyncExec(runnable);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/DisposeEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/DisposeEventTracker.java
new file mode 100644
index 0000000..130acef
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/DisposeEventTracker.java
@@ -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 146397)
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * Listener for tracking the firing of DisposeEvents.
+ */
+public class DisposeEventTracker implements IDisposeListener {
+	public int count;
+	public DisposeEvent event;
+
+	/**
+	 * Queue that the listener will add itself too when it is notified of an
+	 * event. Used to determine order of notifications of listeners. Can be
+	 * null.
+	 */
+	public final List queue;
+
+	public DisposeEventTracker() {
+		queue = null;
+	}
+
+	public DisposeEventTracker(List notificationQueue) {
+		this.queue = notificationQueue;
+	}
+
+	public void handleDispose(DisposeEvent event) {
+		count++;
+		this.event = event;
+		if (queue != null) {
+			queue.add(this);
+		}
+	}
+
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static DisposeEventTracker observe(IObservable observable) {
+		DisposeEventTracker tracker = new DisposeEventTracker();
+		observable.addDisposeListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ListChangeEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ListChangeEventTracker.java
new file mode 100644
index 0000000..da2a263
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ListChangeEventTracker.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.list.IListChangeListener;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListChangeEvent;
+
+/**
+ * Listener for tracking the firing of ListChangeEvents.
+ */
+public class ListChangeEventTracker implements IListChangeListener {
+	public int count;
+
+	public ListChangeEvent event;
+
+	/**
+	 * Queue that the listener will add itself too when it is notified of an
+	 * event. Used to determine order of notifications of listeners.
+	 */
+	public final List listenerQueue;
+
+	public ListChangeEventTracker() {
+		this(null);
+	}
+
+	public ListChangeEventTracker(List listenerQueue) {
+		this.listenerQueue = listenerQueue;
+	}
+
+	public void handleListChange(ListChangeEvent event) {
+		count++;
+		this.event = event;
+		if (listenerQueue != null) {
+			listenerQueue.add(this);
+		}
+	}
+	
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static ListChangeEventTracker observe(IObservableList observable) {
+		ListChangeEventTracker tracker = new ListChangeEventTracker();
+		observable.addListChangeListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/MapChangeEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/MapChangeEventTracker.java
new file mode 100644
index 0000000..52e87e9
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/MapChangeEventTracker.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.map.IMapChangeListener;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapChangeEvent;
+
+/**
+ * Listener for tracking the firing of ChangeEvents.
+ */
+public class MapChangeEventTracker implements IMapChangeListener {
+	public int count;
+
+	public MapChangeEvent event;
+
+	public List queue;
+	
+	public MapChangeEventTracker() {
+		this(null);
+	}
+	
+	public MapChangeEventTracker(List queue) {
+		this.queue = queue;
+	}
+
+	public void handleMapChange(MapChangeEvent event) {
+		count++;
+		this.event = event;
+		
+		if (queue != null) {
+			queue.add(this);
+		}
+	}
+	
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static MapChangeEventTracker observe(IObservableMap observable) {
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		observable.addMapChangeListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/RealmTester.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/RealmTester.java
new file mode 100644
index 0000000..1bdc799
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/RealmTester.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.util;
+
+import junit.framework.Assert;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.runtime.AssertionFailedException;
+
+/**
+ * Aids in the testing of Realms.
+ * 
+ * @since 3.2
+ */
+public class RealmTester {
+
+	/**
+	 * Sets the default realm without using Realm.runWithDefault() for testing
+	 * purposes.
+	 * 
+	 * @param realm
+	 */
+	public static void setDefault(Realm realm) {
+		CurrentRealm.setDefault(realm);
+	}
+
+	/**
+	 * Runs the provided <code>runnable</code> when the realm is both current
+	 * and not current. It checks for AssertionFailedExceptions and if an
+	 * exception occurs or doesn't occur as expected the test fails. The realm
+	 * of an observable created before the method was invoked must be of type
+	 * {@link CurrentRealm}. The default realm during the runnable invocation
+	 * is set to an instance of {@link CurrentRealm} when the runnable is
+	 * invoked.
+	 * 
+	 * @param runnable
+	 */
+	public static void exerciseCurrent(Runnable runnable) {
+		CurrentRealm previousRealm = (CurrentRealm) Realm.getDefault();
+		CurrentRealm realm = new CurrentRealm();
+		setDefault(realm);
+
+		try {
+			realm.setCurrent(true);
+			if (previousRealm != null) {
+				previousRealm.setCurrent(true);
+			}
+
+			try {
+				runnable.run();
+			} catch (AssertionFailedException e) {
+				Assert
+						.fail("Correct realm, exception should not have been thrown");
+			}
+
+			realm.setCurrent(false);
+			if (previousRealm != null) {
+				previousRealm.setCurrent(false);
+			}
+
+			try {
+				runnable.run();
+				Assert
+						.fail("Incorrect realm, exception should have been thrown");
+			} catch (AssertionFailedException e) {
+			}
+		} finally {
+			setDefault(previousRealm);
+		}
+	}
+
+	/**
+	 * Runs the provided <code>runnable</code> when the realm is both current
+	 * and not current. It checks for AssertionFailedExceptions and if an
+	 * exception occurs or doesn't occur as expected the test fails.
+	 * 
+	 * @param runnable
+	 * @param realm
+	 */
+	public static void exerciseCurrent(Runnable runnable, CurrentRealm realm) {
+		realm.setCurrent(true);
+
+		try {
+			runnable.run();
+		} catch (AssertionFailedException e) {
+			Assert.fail("Correct realm, exception should not have been thrown");
+		}
+
+		realm.setCurrent(false);
+
+		try {
+			runnable.run();
+			Assert.fail("Incorrect realm, exception should have been thrown");
+		} catch (AssertionFailedException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/SetChangeEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/SetChangeEventTracker.java
new file mode 100644
index 0000000..9de2f88
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/SetChangeEventTracker.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ISetChangeListener;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+
+public class SetChangeEventTracker implements ISetChangeListener {
+	public int count;
+
+	public SetChangeEvent event;
+
+	/**
+	 * Queue that the listener will add itself too when it is notified of an
+	 * event. Used to determine order of notifications of listeners.
+	 */
+	public final List listenerQueue;
+
+	public SetChangeEventTracker() {
+		this(null);
+	}
+
+	public SetChangeEventTracker(List notificationQueue) {
+		this.listenerQueue = notificationQueue;
+	}
+
+	public void handleSetChange(SetChangeEvent event) {
+		count++;
+		this.event = event;
+		if (listenerQueue != null) {
+			listenerQueue.add(this);
+		}
+	}
+	
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static SetChangeEventTracker observe(IObservableSet observable) {
+		SetChangeEventTracker tracker = new SetChangeEventTracker();
+		observable.addSetChangeListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/StaleEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/StaleEventTracker.java
new file mode 100644
index 0000000..b10c1e2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/StaleEventTracker.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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
+ *     Ovidio Mallo - bug 242166
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IStaleListener;
+import org.eclipse.core.databinding.observable.StaleEvent;
+
+/**
+ * Listener for tracking the firing of StaleEvents.
+ * @since 1.1
+ */
+public class StaleEventTracker implements IStaleListener {
+	public int count;
+
+	public StaleEvent event;
+
+	/**
+	 * Queue that the listener will add itself too when it is notified of an
+	 * event. Used to determine order of notifications of listeners. Can be
+	 * null.
+	 */
+	public final List queue;
+
+	public StaleEventTracker() {
+		this(null);
+	}
+
+	public StaleEventTracker(List notificationQueue) {
+		this.queue = notificationQueue;
+	}
+
+	public void handleStale(StaleEvent event) {
+		count++;
+		this.event = event;
+		if (queue != null) {
+			queue.add(this);
+		}
+	}
+
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static StaleEventTracker observe(IObservable observable) {
+		StaleEventTracker tracker = new StaleEventTracker();
+		observable.addStaleListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/SuiteBuilder.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/SuiteBuilder.java
new file mode 100644
index 0000000..46491e1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/SuiteBuilder.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 208322
+ ******************************************************************************/
+
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+
+import org.eclipse.jface.databinding.conformance.delegate.IObservableContractDelegate;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Builds a test suite.
+ * 
+ * @since 1.1
+ */
+public class SuiteBuilder {
+	private LinkedHashSet content;
+
+	public SuiteBuilder() {
+		content = new LinkedHashSet();
+	}
+
+	/**
+	 * Adds all test methods from the provided <code>testCase</code> to the
+	 * suite.
+	 * 
+	 * @param testCase
+	 * @return builder
+	 */
+	public SuiteBuilder addTests(Class testCase) {
+		content.add(testCase);
+		return this;
+	}
+
+	/**
+	 * Adds all test methods from the provided <code>testCase</code> with the
+	 * provided <code>parameters</code>. A constructor must exist in the
+	 * testCase that accepts a String as the first parameter followed by
+	 * parameters matching the provided parameters. If an appropriate
+	 * constructor is not found an exception will be thrown.
+	 * 
+	 * @param testCase
+	 * @param parameters
+	 * @return builder
+	 */
+	public SuiteBuilder addParameterizedTests(Class testCase,
+			Object[] parameters) {
+
+		Constructor constructor = findConstructor(testCase, parameters);
+		if (constructor == null) {
+			throw new IllegalArgumentException(
+					"The parameters provided don't match a constructor found in ["
+							+ testCase.getName() + "]");
+		}
+
+		content.add(new ParameterizedTest(testCase, constructor, parameters));
+
+		return this;
+	}
+
+	/**
+	 * Convenience method for invoking
+	 * {@link #addParameterizedTests(Class, Object[])} with a delegate.
+	 * 
+	 * @param testCase
+	 * @param delegate
+	 * @return builder
+	 */
+	public SuiteBuilder addObservableContractTest(Class testCase,
+			IObservableContractDelegate delegate) {
+
+		addParameterizedTests(testCase, new Object[] {delegate});
+		return this;
+	}
+
+	/**
+	 * Builds a new TestSuite out of the tests.
+	 * 
+	 * @return suite
+	 */
+	public TestSuite build() {
+		TestSuite suite = new TestSuite();
+
+		for (Iterator it = content.iterator(); it.hasNext();) {
+			Object o = it.next();
+			if (o instanceof Class) {
+				suite.addTestSuite((Class) o);
+			} else if (o instanceof ParameterizedTest) {
+				ParameterizedTest test = (ParameterizedTest) o;
+
+				// Outer test named for parameterized test class
+				TestSuite testClassSuite = new TestSuite();
+				testClassSuite.setName(test.testClass.getName());
+
+				// Inner test named for parameter
+				TestSuite parameterSuite = new TestSuite();
+				parameterSuite.setName(test.parameters[0].getClass().getName());
+				testClassSuite.addTest(parameterSuite);
+
+				Method[] methods = test.testClass.getMethods();
+				for (int i = 0; i < methods.length; i++) {
+					String name = methods[i].getName();
+					if (name.startsWith("test")) {
+						try {
+							parameterSuite.addTest((Test) test.constructor
+									.newInstance(toParamArray(name,
+											test.parameters)));
+						} catch (Exception e) {
+							throw new RuntimeException(e);
+						}
+					}
+				}
+
+				if (testClassSuite.countTestCases() > 0)
+					suite.addTest(testClassSuite);
+			}
+		}
+
+		return suite;
+	}
+
+	private Object[] toParamArray(String testName, Object[] parameters) {
+		Object[] result = new Object[parameters.length + 1];
+		result[0] = testName;
+		System.arraycopy(parameters, 0, result, 1, parameters.length);
+		return result;
+	}
+
+	/**
+	 * Returns the constructor that has a String as the first parameters and
+	 * then matches the type of the parameters.
+	 * @param clazz 
+	 * @param parameters
+	 * @return constructor
+	 */
+	private static Constructor findConstructor(Class clazz, Object[] parameters) {
+		Constructor[] constructors = clazz.getConstructors();
+		int expectedParametersLength = parameters.length + 1;
+
+		for (int i = 0; i < constructors.length; i++) {
+			Constructor constructor = constructors[i];
+			Class[] types = constructor.getParameterTypes();
+
+			if (types.length != expectedParametersLength
+					|| !String.class.equals(types[0])) {
+				continue;
+			}
+
+			boolean skip = false;
+			for (int j = 1; j < types.length; j++) {
+				Class type = types[j];
+				if (!type.isInstance(parameters[j - 1])) {
+					skip = true;
+					break;
+				}
+			}
+
+			if (!skip) {
+				return constructor;
+			}
+		}
+
+		return null;
+	}
+
+	/* package */static class ParameterizedTest {
+		final Constructor constructor;
+
+		final Object[] parameters;
+
+		private Class testClass;
+
+		ParameterizedTest(Class testClass, Constructor constructor,
+				Object[] parameterss) {
+			this.testClass = testClass;
+			this.constructor = constructor;
+			this.parameters = parameterss;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ValueChangeEventTracker.java b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ValueChangeEventTracker.java
new file mode 100644
index 0000000..b54bd14
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding.conformance/src/org/eclipse/jface/databinding/conformance/util/ValueChangeEventTracker.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.databinding.conformance.util;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+
+/**
+ * Listener for tracking the firing of ValueChangeEvents.
+ */
+public class ValueChangeEventTracker implements IValueChangeListener {
+	public int count;
+
+	public ValueChangeEvent event;
+
+	public final List queue;
+
+	public ValueChangeEventTracker() {
+		this(null);
+	}
+
+	public ValueChangeEventTracker(List queue) {
+		this.queue = queue;
+	}
+
+	public void handleValueChange(ValueChangeEvent event) {
+		count++;
+		this.event = event;
+
+		if (queue != null) {
+			queue.add(this);
+		}
+	}
+
+	/**
+	 * Convenience method to register a new listener.
+	 * 
+	 * @param observable
+	 * @return tracker
+	 */
+	public static ValueChangeEventTracker observe(IObservableValue observable) {
+		ValueChangeEventTracker tracker = new ValueChangeEventTracker();
+		observable.addValueChangeListener(tracker);
+		return tracker;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/.classpath b/tests/org.eclipse.jface.tests.databinding/.classpath
new file mode 100644
index 0000000..ce73933
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tests/org.eclipse.jface.tests.databinding/.cvsignore b/tests/org.eclipse.jface.tests.databinding/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/tests/org.eclipse.jface.tests.databinding/.project b/tests/org.eclipse.jface.tests.databinding/.project
new file mode 100644
index 0000000..815de41
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.jface.tests.databinding</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.core.prefs b/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..a45dc96
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,355 @@
+#Thu Feb 05 11:37:47 MST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.builder.cleanOutputFolder=clean
+org.eclipse.jdt.core.builder.duplicateResourceTask=warning
+org.eclipse.jdt.core.builder.invalidClasspath=abort
+org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore
+org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
+org.eclipse.jdt.core.circularClasspath=error
+org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
+org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.maxProblemPerUnit=100
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=ignore
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.incompatibleJDKLevel=ignore
+org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.ui.prefs b/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..0ec2b7f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,117 @@
+#Tue Feb 10 16:06:05 MST 2009
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=false
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile
+formatter_settings_version=11
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return Returns the ${bare_field_name}.\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} The ${bare_field_name} to set.\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*******************************************************************************\r\n * Copyright (c) ${year} IBM Corporation and others.\r\n * All rights reserved. This program and the accompanying materials\r\n * are made available under the terms of the Eclipse Public License v1.0\r\n * which accompanies this distribution, and is available at\r\n * http\://www.eclipse.org/legal/epl-v10.html\r\n *\r\n * Contributors\:\r\n *     IBM Corporation - initial API and implementation\r\n ******************************************************************************/\r\n</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @since 3.2\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template></templates>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=true
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.pde.prefs b/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.pde.prefs
new file mode 100644
index 0000000..12a7331
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/.settings/org.eclipse.pde.prefs
@@ -0,0 +1,18 @@
+#Mon Dec 03 13:56:42 EST 2007
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.deprecated=0
+compilers.p.illegal-att-value=0
+compilers.p.missing-bundle-classpath-entries=1
+compilers.p.missing-packages=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=0
+compilers.p.unknown-attribute=0
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.p.unused-element-or-attribute=1
+compilers.use-project=true
+eclipse.preferences.version=1
diff --git a/tests/org.eclipse.jface.tests.databinding/JFace-Data Binding Test Suite.launch b/tests/org.eclipse.jface.tests.databinding/JFace-Data Binding Test Suite.launch
new file mode 100644
index 0000000..a0f323a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/JFace-Data Binding Test Suite.launch
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?><launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
+<stringAttribute key="application" value="org.eclipse.pde.junit.runtime.coretestapplication"/>
+<booleanAttribute key="askclear" value="false"/>
+<booleanAttribute key="automaticAdd" value="true"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="[NONE]"/>
+<booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="true"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<booleanAttribute key="com.mountainminds.eclemma.core.INPLACE_INSTRUMENTATION" value="true"/>
+<listAttribute key="com.mountainminds.eclemma.core.INSTRUMENTATION_PATHS">
+<listEntry value="/org.eclipse.jface.databinding/bin"/>
+<listEntry value="/org.eclipse.core.databinding.beans/bin"/>
+<listEntry value="/org.eclipse.core.databinding.observable/bin"/>
+<listEntry value="/org.eclipse.core.databinding.property/bin"/>
+<listEntry value="/org.eclipse.core.databinding/bin"/>
+</listAttribute>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/pde-junit"/>
+<booleanAttribute key="default" value="true"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../junit-workspace"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSuite.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.jface.tests.databinding.BindingTestSuite"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jface.tests.databinding"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.sdk.ide"/>
+<booleanAttribute key="run_in_ui_thread" value="true"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<stringAttribute key="templateConfig" value=""/>
+<booleanAttribute key="tracing" value="false"/>
+<booleanAttribute key="useCustomFeatures" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="false"/>
+<booleanAttribute key="useProduct" value="false"/>
+</launchConfiguration>
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF b/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..66ddd56
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.jface.tests.databinding
+Bundle-Version: 1.0.100.qualifier
+Bundle-ClassPath: jfacebindingtests.jar
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.databinding;bundle-version="[1.3.0,2.0.0)",
+ org.junit,
+ org.eclipse.swt,
+ org.eclipse.jface,
+ org.eclipse.core.runtime,
+ org.eclipse.jface.examples.databinding,
+ org.eclipse.core.databinding.beans,
+ org.eclipse.jface.databinding,
+ org.eclipse.jface.tests.databinding.conformance,
+ org.eclipse.core.databinding.property
+Import-Package: com.ibm.icu.math,
+ com.ibm.icu.text
+Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/tests/org.eclipse.jface.tests.databinding/about.html b/tests/org.eclipse.jface.tests.databinding/about.html
new file mode 100644
index 0000000..4602330
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+ 
+<p>June 2, 2006</p>	
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/build.properties b/tests/org.eclipse.jface.tests.databinding/build.properties
new file mode 100644
index 0000000..e671f5a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/build.properties
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (c) 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+source.jfacebindingtests.jar = src/
+               
+src.includes = about.html
+output.jfacebindingtests.jar = bin/
+bin.includes = META-INF/,\
+               about.html,\
+               test.xml,\
+               *.html,\
+               jfacebindingtests.jar,\
+               plugin.properties
diff --git a/tests/org.eclipse.jface.tests.databinding/plugin.properties b/tests/org.eclipse.jface.tests.databinding/plugin.properties
new file mode 100644
index 0000000..6f8acfc
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/plugin.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2000, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+pluginName = JFace Data Binding Tests
+providerName = Eclipse.org
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/AggregateValidationStatusTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/AggregateValidationStatusTest.java
new file mode 100644
index 0000000..28d5b06
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/AggregateValidationStatusTest.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import org.eclipse.core.databinding.AggregateValidationStatus;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+
+/**
+ * @since 1.1
+ */
+public class AggregateValidationStatusTest extends AbstractSWTTestCase {
+	public void testAggregateValidationStatusValueType() throws Exception {
+		DataBindingContext dbc = new DataBindingContext();
+		AggregateValidationStatus status = new AggregateValidationStatus(dbc
+				.getBindings(), AggregateValidationStatus.MAX_SEVERITY);
+		assertEquals(IStatus.class, status.getValueType());
+	}
+
+	public void testConstructor_DefaultRealm() throws Exception {
+		DataBindingContext dbc = new DataBindingContext();
+		AggregateValidationStatus status = new AggregateValidationStatus(dbc
+				.getBindings(), AggregateValidationStatus.MAX_SEVERITY);
+		assertEquals(Realm.getDefault(), status.getRealm());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/BindingTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/BindingTest.java
new file mode 100644
index 0000000..b175bfb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/BindingTest.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * 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 254524)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class BindingTest extends AbstractDefaultRealmTestCase {
+	private IObservable target;
+
+	private IObservable model;
+
+	private DataBindingContext dbc;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		target = WritableValue.withValueType(String.class);
+		model = WritableValue.withValueType(String.class);
+		dbc = new DataBindingContext();
+	}
+
+	public void testDisposeTargetDisposesBinding() {
+		Binding binding = createBinding();
+		assertFalse(binding.isDisposed());
+		target.dispose();
+		assertTrue(binding.isDisposed());
+	}
+
+	public void testDisposeModelDisposesBinding() {
+		Binding binding = createBinding();
+		assertFalse(binding.isDisposed());
+		model.dispose();
+		assertTrue(binding.isDisposed());
+	}
+
+	public void testPreDisposedTarget_FiresIllegalArgumentException() {
+		try {
+			target.dispose();
+			createBinding();
+			fail("Expected IllegalArgumentException");
+		} catch (IllegalArgumentException expected) {
+		}
+	}
+
+	public void testPreDisposedModel_FiresIllegalArgumentException() {
+		try {
+			model.dispose();
+			createBinding();
+			fail("Expected IllegalArgumentException");
+		} catch (IllegalArgumentException expected) {
+		}
+	}
+
+	public void testDisposeModelThenBinding() {
+		Binding binding = createBinding();
+		model.dispose();
+		binding.dispose();
+	}
+
+	public void testDisposeTargetThenBinding() {
+		Binding binding = createBinding();
+		target.dispose();
+		binding.dispose();
+	}
+
+	public void testDisposeObservablesThenBinding() {
+		Binding binding = createBinding();
+		model.dispose();
+		target.dispose();
+		binding.dispose();
+	}
+
+	public void testDisposeBindingThenModel() {
+		Binding binding = createBinding();
+		binding.dispose();
+		model.dispose();
+	}
+
+	public void testDisposeBindingThenTarget() {
+		Binding binding = createBinding();
+		binding.dispose();
+		target.dispose();
+	}
+
+	public void testDisposeBindingThenObservables() {
+		Binding binding = createBinding();
+		binding.dispose();
+		model.dispose();
+		target.dispose();
+	}
+
+	private Binding createBinding() {
+		Binding binding = new BindingStub(target, model);
+		binding.init(dbc);
+		return binding;
+	}
+
+	private static class BindingStub extends Binding {
+		BindingStub(IObservable target, IObservable model) {
+			super(target, model);
+		}
+
+		public IObservableValue getValidationStatus() {
+			return null;
+		}
+
+		protected void postInit() {
+		}
+
+		protected void preInit() {
+		}
+
+		public void updateModelToTarget() {
+		}
+
+		public void updateTargetToModel() {
+		}
+
+		public void validateModelToTarget() {
+		}
+
+		public void validateTargetToModel() {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/DatabindingContextTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/DatabindingContextTest.java
new file mode 100755
index 0000000..c8bb70c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/DatabindingContextTest.java
@@ -0,0 +1,266 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 159539, 140644, 159940, 116920, 159768
+ *     Matthew Hall - bugs 213145, 260329
+ *******************************************************************************/
+package org.eclipse.core.tests.databinding;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.databinding.AggregateValidationStatus;
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateListStrategy;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+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.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class DatabindingContextTest extends AbstractDefaultRealmTestCase {
+	private DataBindingContext dbc;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		dbc = new DataBindingContext();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase#tearDown
+	 * ()
+	 */
+	protected void tearDown() throws Exception {
+		if (dbc != null) {
+			dbc.dispose();
+		}
+		super.tearDown();
+	}
+
+	public void testDisposeBindings() throws Exception {
+		Binding binding = new BindingStub();
+		binding.init(dbc);
+
+		assertFalse(binding.isDisposed());
+		dbc.dispose();
+		assertTrue("binding should be diposed when dbc is disposed", binding
+				.isDisposed());
+	}
+
+	public void testBindValue() throws Exception {
+		IObservableValue target = WritableValue.withValueType(String.class);
+		IObservableValue model = WritableValue.withValueType(String.class);
+
+		Binding binding = dbc.bindValue(target, model);
+		assertTrue("binding is of the incorrect type", binding.getClass()
+				.getName().endsWith("ValueBinding"));
+	}
+
+	public void testBindList() throws Exception {
+		IObservableList target = WritableList.withElementType(Object.class);
+		IObservableList model = WritableList.withElementType(Object.class);
+
+		Binding binding = dbc.bindList(target, model);
+		assertTrue("binding is of the incorrect type", binding.getClass()
+				.getName().endsWith("ListBinding"));
+	}
+
+	/**
+	 * Asserts that IStatus is populated and change events are fired when a
+	 * Binding that is associated with a context is in error.
+	 * 
+	 * @throws Exception
+	 */
+	public void testValidationError() throws Exception {
+		WritableValue targetObservable = WritableValue
+				.withValueType(String.class);
+		WritableValue modelObservable = WritableValue
+				.withValueType(String.class);
+
+		final String errorMessage = "error";
+		ValueChangeEventTracker errorCounter = new ValueChangeEventTracker();
+		ChangeEventTracker errorsCounter = new ChangeEventTracker();
+
+		IObservableValue error = new AggregateValidationStatus(dbc
+				.getBindings(), AggregateValidationStatus.MAX_SEVERITY);
+		error.addValueChangeListener(errorCounter);
+		assertTrue(((IStatus) error.getValue()).isOK());
+
+		IObservableMap errors = dbc.getValidationStatusMap();
+		errors.addChangeListener(errorsCounter);
+		assertEquals(0, errors.size());
+
+		IValidator validator = new IValidator() {
+			public IStatus validate(Object value) {
+				return ValidationStatus.error(errorMessage);
+			}
+		};
+
+		dbc
+				.bindValue(targetObservable, modelObservable,
+						new UpdateValueStrategy()
+								.setAfterGetValidator(validator), null);
+
+		targetObservable.setValue("");
+		assertFalse(((IStatus) error.getValue()).isOK());
+		assertEquals(errorMessage, ((IStatus) error.getValue()).getMessage());
+		assertEquals(1, errors.size());
+		assertEquals(1, errorsCounter.count);
+		assertEquals(1, errorCounter.count);
+		error.dispose();
+	}
+
+	/**
+	 * Asserts that then
+	 * {@link DataBindingContext#bindValue(IObservableValue, IObservableValue, UpdateValueStrategy, UpdateValueStrategy)}
+	 * if invoked the created binding is added to the internal list of bindings.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBindValueAddBinding() throws Exception {
+		WritableValue targetValue = WritableValue.withValueType(String.class);
+		WritableValue modelValue = WritableValue.withValueType(String.class);
+
+		assertNotNull(dbc.getBindings());
+		assertEquals(0, dbc.getBindings().size());
+
+		Binding binding = dbc.bindValue(targetValue, modelValue);
+		assertNotNull(binding);
+		assertNotNull(dbc.getBindings());
+		assertEquals(1, dbc.getBindings().size());
+		assertEquals(binding, dbc.getBindings().get(0));
+	}
+
+	/**
+	 * Asserts that when
+	 * {@link DataBindingContext#bindList(IObservableList, IObservableList, UpdateListStrategy, UpdateListStrategy)}
+	 * is invoked the created binding is added to the intenal list of bindings.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBindListAddBinding() throws Exception {
+		WritableList targetList = new WritableList(new ArrayList(),
+				Object.class);
+		WritableList modelList = new WritableList(new ArrayList(), Object.class);
+
+		assertNotNull(dbc.getBindings());
+		assertEquals(0, dbc.getBindings().size());
+
+		Binding binding = dbc.bindList(targetList, modelList);
+		assertNotNull(binding);
+		assertNotNull(dbc.getBindings());
+		assertEquals(1, dbc.getBindings().size());
+		assertEquals(binding, dbc.getBindings().get(0));
+	}
+
+	public void testGetBindingsImmutability() throws Exception {
+		BindingStub binding = new BindingStub();
+		binding.init(dbc);
+
+		try {
+			dbc.getBindings().remove(0);
+			fail("exception should have been thrown");
+		} catch (UnsupportedOperationException e) {
+		}
+	}
+
+	public void testRemoveBinding() throws Exception {
+		BindingStub binding = new BindingStub();
+		binding.init(dbc);
+
+		assertTrue("context should contain the binding", dbc.getBindings()
+				.contains(binding));
+		binding.dispose();
+		assertFalse("binding should have been removed", dbc.getBindings()
+				.contains(binding));
+	}
+
+	/**
+	 * Asserts that when a ValueBinding is created validation is ran to ensure
+	 * that the validation status of the Binding reflects the validity of the
+	 * value in the target.
+	 * 
+	 * @throws Exception
+	 */
+	public void testValidateTargetAfterValueBindingCreation() throws Exception {
+		WritableValue target = new WritableValue("", String.class);
+		WritableValue model = new WritableValue("2", String.class);
+		class Validator implements IValidator {
+			public IStatus validate(Object value) {
+				return ValidationStatus.error("error");
+			}
+		}
+
+		Binding binding = dbc.bindValue(target, model,
+				new UpdateValueStrategy()
+						.setAfterConvertValidator(new Validator()), null);
+
+		assertEquals(IStatus.ERROR, ((IStatus) binding.getValidationStatus()
+				.getValue()).getSeverity());
+	}
+
+	protected void assertNoErrorsFound() {
+		IStatus status = AggregateValidationStatus.getStatusMaxSeverity(dbc
+				.getBindings());
+		assertTrue("No errors should be found, but found " + status, status
+				.isOK());
+	}
+
+	protected void assertErrorsFound() {
+		IStatus status = AggregateValidationStatus.getStatusMaxSeverity(dbc
+				.getBindings());
+		assertFalse("Errors should be found, but found none.", status.isOK());
+	}
+
+	private static class BindingStub extends Binding {
+
+		public BindingStub() {
+			super(new WritableValue(), new WritableValue());
+		}
+
+		public IObservableValue getValidationStatus() {
+			return null;
+		}
+
+		public void updateTargetToModel() {
+		}
+
+		public void updateModelToTarget() {
+		}
+
+		protected void postInit() {
+		}
+
+		protected void preInit() {
+		}
+
+		public void validateModelToTarget() {
+		}
+
+		public void validateTargetToModel() {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ListBindingTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ListBindingTest.java
new file mode 100755
index 0000000..ff08d69
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ListBindingTest.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 159768
+ *     Matthew Hall - bug 260329
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateListStrategy;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.BindingStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class ListBindingTest extends AbstractDefaultRealmTestCase {
+	private IObservableList target;
+	private IObservableList model;
+	private DataBindingContext dbc;
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		target = new WritableList(new ArrayList(), String.class);
+		model = new WritableList(new ArrayList(), String.class);
+		dbc = new DataBindingContext();
+	}
+
+	public void testUpdateModelFromTarget() throws Exception {
+		Binding binding = dbc.bindList(target, model,
+				new UpdateListStrategy(UpdateListStrategy.POLICY_ON_REQUEST),
+				new UpdateListStrategy(UpdateListStrategy.POLICY_ON_REQUEST));
+		
+		target.add("1");
+		List targetCopy = new ArrayList(target.size());
+		targetCopy.addAll(target);
+		
+		model.add("2");
+		
+		assertFalse("target should not equal model", target.equals(model));
+		binding.updateTargetToModel();
+		assertEquals("target should not have changed", targetCopy, target);
+		assertEquals("target != model", target, model);
+	}
+
+	public void testUpdateTargetFromModel() throws Exception {
+		Binding binding = dbc.bindList(target, model,
+				new UpdateListStrategy(UpdateListStrategy.POLICY_ON_REQUEST),
+				new UpdateListStrategy(UpdateListStrategy.POLICY_ON_REQUEST));
+		
+		target.add("1");		
+		model.add("2");
+		
+		List modelCopy = new ArrayList(model.size());
+		modelCopy.addAll(model);
+		
+		assertFalse("model should not equal target", model.equals(target));
+		binding.updateModelToTarget();
+		
+		assertEquals("model should not have changed", modelCopy, model);
+		assertEquals("model != target", model, target);
+	}
+	
+	public void testGetTarget() throws Exception {
+		Binding binding = dbc.bindList(target, model);
+		assertEquals(target, binding.getTarget());
+	}
+	
+	public void testGetModel() throws Exception {
+		Binding binding = dbc.bindList(target, model);
+		assertEquals(model, binding.getModel());
+	}
+	
+	public void testStatusIsInstanceOfBindingStatus() throws Exception {
+		Binding binding = dbc.bindList(target, model);
+		assertTrue(binding.getValidationStatus().getValue() instanceof BindingStatus);
+	}
+	
+	public void testAddValidationStatusContainsMultipleStatuses() throws Exception {
+		UpdateListStrategy strategy = new UpdateListStrategy() {
+			protected IStatus doAdd(IObservableList observableList,
+					Object element, int index) {
+				super.doAdd(observableList, element, index);
+				
+				switch (index) {
+				case 0:
+					return ValidationStatus.error("");
+				case 1:
+					return ValidationStatus.info("");
+				}
+				
+				return null;
+			}
+		};
+		
+		Binding binding = dbc.bindList(target, model, strategy, null);
+		target.addAll(Arrays.asList(new String[] {"1", "2"}));
+		
+		IStatus status = (IStatus) binding.getValidationStatus().getValue();
+		assertEquals("maximum status", IStatus.ERROR, status.getSeverity());
+		assertTrue("multi status", status.isMultiStatus());
+		
+		IStatus[] children = status.getChildren();
+		assertEquals("multi status children", 2, children.length);
+		assertEquals("first status severity", IStatus.ERROR, children[0].getSeverity());
+		assertEquals("second status severity", IStatus.INFO, children[1].getSeverity());
+	}
+	
+	public void testRemoveValidationStatusContainsMultipleStatuses() throws Exception {
+		List items = Arrays.asList(new String[] {"1", "2"});
+		model.addAll(items);
+		
+		UpdateListStrategy strategy = new UpdateListStrategy() {
+			int count;
+			/* (non-Javadoc)
+			 * @see org.eclipse.core.databinding.UpdateListStrategy#doRemove(org.eclipse.core.databinding.observable.list.IObservableList, int)
+			 */
+			protected IStatus doRemove(IObservableList observableList, int index) {
+				super.doRemove(observableList, index);
+				
+				switch (count++) {
+				case 0:
+					return ValidationStatus.error("");
+				case 1:
+					return ValidationStatus.info("");
+				}
+				
+				return null; 	
+			}
+		};
+		
+		Binding binding = dbc.bindList(target, model, strategy, null);
+		target.removeAll(items);
+		
+		IStatus status = (IStatus) binding.getValidationStatus().getValue();
+		assertEquals("maximum status", IStatus.ERROR, status.getSeverity());
+		assertTrue("multi status", status.isMultiStatus());
+		
+		IStatus[] children = status.getChildren();
+		assertEquals("multi status children", 2, children.length);
+		assertEquals("first status severity", IStatus.ERROR, children[0].getSeverity());
+		assertEquals("second status severity", IStatus.INFO, children[1].getSeverity());
+	}
+	
+	public void testAddOKValidationStatus() throws Exception {
+		Binding binding = dbc.bindList(target, model);
+		target.add("1");
+		
+		IStatus status = (IStatus) binding.getValidationStatus().getValue();
+		assertTrue(status.isOK());
+		assertEquals(0, status.getChildren().length);
+	}
+	
+	public void testRemoveOKValidationStatus() throws Exception {
+		model.add("1");
+		Binding binding = dbc.bindList(target, model);
+		
+		target.remove("1");
+		IStatus status = (IStatus) binding.getValidationStatus().getValue();
+		assertTrue(status.isOK());
+		assertEquals(0, status.getChildren().length);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ObservablesManagerTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ObservablesManagerTest.java
new file mode 100644
index 0000000..920a034
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ObservablesManagerTest.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Bob Smith and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Bob Smith - initial API and implementation (bug 198880)
+ *     Matthew Hall - bugs 146397, 260329
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.ObservablesManager;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ *
+ */
+public class ObservablesManagerTest extends AbstractDefaultRealmTestCase {
+	private DataBindingContext dbc;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		dbc = new DataBindingContext();
+	}
+
+	protected void tearDown() throws Exception {
+		if (dbc != null) {
+			dbc.dispose();
+		}
+		super.tearDown();
+	}
+
+	public void testOnlyModelIsDisposed() throws Exception {
+		IObservableValue targetOv = new WritableValue();
+		IObservableValue modelOv = new WritableValue();
+		dbc.bindValue(targetOv, modelOv);
+
+		ObservablesManager observablesManager = new ObservablesManager();
+
+		observablesManager.addObservablesFromContext(dbc, false, true);
+		observablesManager.dispose();
+
+		assertFalse(targetOv.isDisposed());
+		assertTrue(modelOv.isDisposed());
+	}
+
+	public void testOnlyTargetIsDisposed() throws Exception {
+		IObservableValue targetOv = new WritableValue();
+		IObservableValue modelOv = new WritableValue();
+		dbc.bindValue(targetOv, modelOv);
+
+		ObservablesManager observablesManager = new ObservablesManager();
+
+		observablesManager.addObservablesFromContext(dbc, true, false);
+		observablesManager.dispose();
+
+		assertTrue(targetOv.isDisposed());
+		assertFalse(modelOv.isDisposed());
+	}
+
+	public void testTargetAndModelIsDisposed() throws Exception {
+		IObservableValue targetOv = new WritableValue();
+		IObservableValue modelOv = new WritableValue();
+		dbc.bindValue(targetOv, modelOv);
+
+		ObservablesManager observablesManager = new ObservablesManager();
+
+		observablesManager.addObservablesFromContext(dbc, true, true);
+		observablesManager.dispose();
+
+		assertTrue(targetOv.isDisposed());
+		assertTrue(modelOv.isDisposed());
+	}
+
+	public void testDispose_Bug277966_NPEWhenManagedObservableAlreadyDisposed() {
+		ObservablesManager manager = new ObservablesManager();
+
+		// NPE only occurs when explicitly managing (i.e. not through a
+		// DataBindingContext) observables where hashCode() is a tracked getter
+		IObservable observable = new WritableList();
+		manager.addObservable(observable);
+		observable.dispose();
+
+		manager.dispose();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateListStrategyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateListStrategyTest.java
new file mode 100644
index 0000000..f2f112e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateListStrategyTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 270461)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import org.eclipse.core.databinding.BindingException;
+import org.eclipse.core.databinding.UpdateListStrategy;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.internal.databinding.conversion.IdentityConverter;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class UpdateListStrategyTest extends AbstractDefaultRealmTestCase {
+	public void testFillDefaults_AssertSourceTypeExtendsConverterFromType() {
+		// Valid use: source type String extends converter from-type Object
+		UpdateListStrategyStub strategy = new UpdateListStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, Object.class));
+		strategy.fillDefaults(WritableList.withElementType(String.class),
+				WritableList.withElementType(Object.class));
+
+		// Invalid use: source type Object does not extend converter from-type
+		// String
+		strategy = new UpdateListStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(String.class, Object.class));
+		try {
+			strategy.fillDefaults(WritableList.withElementType(Object.class),
+					WritableList.withElementType(Object.class));
+			fail("Expected BindingException since Object does not extend String");
+		} catch (BindingException expected) {
+		}
+	}
+
+	public void testFillDefaults_AssertConverterToTypeExtendsDestinationType() {
+		// Valid use: converter to-type String extends destination type Object
+		UpdateListStrategyStub strategy = new UpdateListStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, String.class));
+		strategy.fillDefaults(WritableList.withElementType(Object.class),
+				WritableList.withElementType(Object.class));
+
+		// Invalid use: converter to-type Object does not extend destination
+		// type String
+		strategy = new UpdateListStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, Object.class));
+		try {
+			strategy.fillDefaults(WritableList.withElementType(Object.class),
+					WritableList.withElementType(String.class));
+			fail("Expected BindingException since Object does not extend String");
+		} catch (BindingException expected) {
+		}
+	}
+
+	class UpdateListStrategyStub extends UpdateListStrategy {
+		protected void fillDefaults(IObservableList source,
+				IObservableList destination) {
+			super.fillDefaults(source, destination);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateSetStrategyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateSetStrategyTest.java
new file mode 100644
index 0000000..dedd4e4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateSetStrategyTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 270461)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import org.eclipse.core.databinding.BindingException;
+import org.eclipse.core.databinding.UpdateSetStrategy;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.internal.databinding.conversion.IdentityConverter;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class UpdateSetStrategyTest extends AbstractDefaultRealmTestCase {
+	public void testFillDefaults_AssertSourceTypeExtendsConverterFromType() {
+		// Valid use: source type String extends converter from-type Object
+		UpdateSetStrategyStub strategy = new UpdateSetStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, Object.class));
+		strategy.fillDefaults(WritableSet.withElementType(String.class),
+				WritableSet.withElementType(Object.class));
+
+		// Invalid use: source type Object does not extend converter from-type
+		// String
+		strategy = new UpdateSetStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(String.class, Object.class));
+		try {
+			strategy.fillDefaults(WritableSet.withElementType(Object.class),
+					WritableSet.withElementType(Object.class));
+			fail("Expected BindingException since Object does not extend String");
+		} catch (BindingException expected) {
+		}
+	}
+
+	public void testFillDefaults_AssertConverterToTypeExtendsDestinationType() {
+		// Valid use: converter to-type String extends destination type Object
+		UpdateSetStrategyStub strategy = new UpdateSetStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, String.class));
+		strategy.fillDefaults(WritableSet.withElementType(Object.class),
+				WritableSet.withElementType(Object.class));
+
+		// Invalid use: converter to-type Object does not extend destination
+		// type String
+		strategy = new UpdateSetStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, Object.class));
+		try {
+			strategy.fillDefaults(WritableSet.withElementType(Object.class),
+					WritableSet.withElementType(String.class));
+			fail("Expected BindingException since Object does not extend String");
+		} catch (BindingException expected) {
+		}
+	}
+
+	class UpdateSetStrategyStub extends UpdateSetStrategy {
+		protected void fillDefaults(IObservableSet source,
+				IObservableSet destination) {
+			super.fillDefaults(source, destination);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateStrategyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateStrategyTest.java
new file mode 100644
index 0000000..351f668
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateStrategyTest.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+
+import org.eclipse.core.databinding.UpdateValueStrategy;
+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.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.internal.databinding.conversion.DateToStringConverter;
+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.StatusToStringConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToBooleanConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToBooleanPrimitiveConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToByteConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToDateConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToShortConverter;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class UpdateStrategyTest extends AbstractDefaultRealmTestCase {
+	public void testDefaultConverterForStringToInteger() throws Exception {
+		assertDefaultConverter(String.class, Integer.class, StringToNumberConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToIntegerPrimitive() throws Exception {
+		assertDefaultConverter(String.class, Integer.TYPE, StringToNumberConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToLong() throws Exception {
+		assertDefaultConverter(String.class, Long.class, StringToNumberConverter.class);
+	}
+
+	public void testDefaultConverterForStringToLongPrimitive() throws Exception {
+		assertDefaultConverter(String.class, Long.TYPE, StringToNumberConverter.class);
+	}
+
+	public void testDefaultConverterForStringToDouble() throws Exception {
+		assertDefaultConverter(String.class, Double.class, StringToNumberConverter.class);
+	}
+
+	public void testDefaultConverterForStringToDoublePrimitive() throws Exception {
+		assertDefaultConverter(String.class, Double.TYPE, StringToNumberConverter.class);
+	}
+
+	public void testDefaultConverterForStringToFloat() throws Exception {
+		assertDefaultConverter(String.class, Float.class, StringToNumberConverter.class);
+	}
+
+	public void testDefaultConverterForStringToFloatPrimitive() throws Exception {
+		assertDefaultConverter(String.class, Float.TYPE, StringToNumberConverter.class);
+	}
+
+	public void testDefaultConverterForStringToBigInteger() throws Exception {
+		assertDefaultConverter(String.class, BigInteger.class, StringToNumberConverter.class);
+	}
+	
+	public void testDefaultConverterForIntegerToString() throws Exception {
+		assertDefaultConverter(Integer.class, String.class, NumberToStringConverter.class);
+	}
+
+	public void testDefaultConverterForIntegerPrimitiveToString() throws Exception {
+		assertDefaultConverter(Integer.TYPE, String.class, NumberToStringConverter.class);
+	}
+
+	public void testDefaultConverterForLongToString() throws Exception {
+		assertDefaultConverter(Long.class, String.class, NumberToStringConverter.class);
+	}
+
+	public void testDefaultConverterForLongPrimitiveToString() throws Exception {
+		assertDefaultConverter(Long.TYPE, String.class, NumberToStringConverter.class);
+	}
+
+	public void testDefaultConverterForDoubleToString() throws Exception {
+		assertDefaultConverter(Double.class, String.class, NumberToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForDoublePrimitiveToString() throws Exception {
+		assertDefaultConverter(Double.TYPE, String.class, NumberToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForFloatToString() throws Exception {
+		assertDefaultConverter(Float.class, String.class, NumberToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForFloatPrimitiveToString() throws Exception {
+		assertDefaultConverter(Float.TYPE, String.class, NumberToStringConverter.class);
+	}
+
+	public void testDefaultConverterForBigIntegerToString() throws Exception {
+		assertDefaultConverter(BigInteger.class, String.class, NumberToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForDateToString() throws Exception {
+		assertDefaultConverter(Date.class, String.class, DateToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToBoolean() throws Exception {
+		assertDefaultConverter(String.class, Boolean.class, StringToBooleanConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToBooleanPrimitive() throws Exception {
+		assertDefaultConverter(String.class, Boolean.TYPE, StringToBooleanPrimitiveConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToByte() throws Exception {
+		assertDefaultConverter(String.class, Byte.class, StringToByteConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToBytePrimitive() throws Exception {
+		assertDefaultConverter(String.class, Byte.TYPE, StringToByteConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToCharacter() throws Exception {
+		assertDefaultConverter(String.class, Character.class, StringToCharacterConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToDate() throws Exception {
+		assertDefaultConverter(String.class, Date.class, StringToDateConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToShort() throws Exception {
+		assertDefaultConverter(String.class, Short.class, StringToShortConverter.class);
+	}
+	
+	public void testDefaultConverterForStringToShortPrimitive() throws Exception {
+		assertDefaultConverter(String.class, Short.TYPE, StringToShortConverter.class);
+	}
+	
+	public void testDefaultConverterForByteToString() throws Exception {
+		assertDefaultConverter(Byte.class, String.class, IntegerToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForBytePrimitiveToString() throws Exception {
+		assertDefaultConverter(Byte.TYPE, String.class, IntegerToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForShortToString() throws Exception {
+		assertDefaultConverter(Short.class, String.class, IntegerToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForShortPrimitiveToString() throws Exception {
+		assertDefaultConverter(Short.TYPE, String.class, IntegerToStringConverter.class);
+	}
+	
+	public void testDefaultConverterForStatusToString() throws Exception {
+		assertDefaultConverter(IStatus.class, String.class, StatusToStringConverter.class);
+	}
+	
+
+	public void testDefaultConverterForNumberToByte() throws Exception {
+		assertFromNumberToNumberConverter(Byte.class, Byte.TYPE,
+				NumberToByteConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToShort() throws Exception {
+		assertFromNumberToNumberConverter(Short.class, Short.TYPE,
+				NumberToShortConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToShortPrimitive()
+			throws Exception {
+		assertFromNumberToNumberConverter(Short.TYPE, Short.class,
+				NumberToShortConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToInteger() throws Exception {
+		assertFromNumberToNumberConverter(Integer.class, Integer.TYPE,
+				NumberToIntegerConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToIntegerPrimitive()
+			throws Exception {
+		assertFromNumberToNumberConverter(Integer.TYPE, Integer.class,
+				NumberToIntegerConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToLong() throws Exception {
+		assertFromNumberToNumberConverter(Long.class, Long.TYPE,
+				NumberToLongConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToLongPrimitive() throws Exception {
+		assertFromNumberToNumberConverter(Long.TYPE, Long.class,
+				NumberToLongConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToFloat() throws Exception {
+		assertFromNumberToNumberConverter(Float.class, Float.TYPE,
+				NumberToFloatConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToFloatPrimitive()
+			throws Exception {
+		assertFromNumberToNumberConverter(Float.TYPE, Float.class,
+				NumberToFloatConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToDouble() throws Exception {
+		assertFromNumberToNumberConverter(Double.class, Double.TYPE,
+				NumberToDoubleConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToDoublePrimitive()
+			throws Exception {
+		assertFromNumberToNumberConverter(Double.TYPE, Double.class,
+				NumberToDoubleConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToBigInteger() throws Exception {
+		assertFromNumberToNumberConverter(BigInteger.class, null,
+				NumberToBigIntegerConverter.class);
+	}
+
+	public void testDefaultConverterForNumberToBigDecimal() throws Exception {
+		assertFromNumberToNumberConverter(BigDecimal.class, null,
+				NumberToBigDecimalConverter.class);
+	}
+
+
+	private static Class[] primitiveNumberTypes = new Class[] { Byte.TYPE,
+			Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE };
+
+	private static Class[] boxedNumberTypes = new Class[] { Byte.class,
+			Short.class, Integer.class, Long.class, Float.class, Double.class,
+			BigInteger.class, BigDecimal.class };
+
+	private void assertFromNumberToNumberConverter(Class toType,
+			Class toCounterPrimitiveType, Class converterType) {
+
+		for (int i = 0; i < primitiveNumberTypes.length; i++) {
+			Class primitiveType = primitiveNumberTypes[i];
+
+			if (!primitiveType.equals(toType)
+					&& !primitiveType.equals(toCounterPrimitiveType)) {
+				assertDefaultConverter(primitiveType, toType, converterType);
+			} else if (!primitiveType.equals(toType)) {
+				assertDefaultConverter(primitiveType, toType,
+						IdentityConverter.class);
+			}
+		}
+
+		for (int i = 0; i < boxedNumberTypes.length; i++) {
+			Class boxedType = boxedNumberTypes[i];
+
+			if (!boxedType.equals(toType)
+					&& !boxedType.equals(toCounterPrimitiveType)) {
+				assertDefaultConverter(boxedType, toType, converterType);
+			} else if (!boxedType.equals(toType)) {
+				assertDefaultConverter(boxedType, toType,
+						IdentityConverter.class);
+			}
+		}
+	}
+	
+	private void assertDefaultConverter(Class fromType, Class toType, Class converterType) {
+		WritableValue source = WritableValue.withValueType(fromType);
+		WritableValue destination = WritableValue.withValueType(toType);
+		
+		UpdateStrategyStub strategy = new UpdateStrategyStub();
+		strategy.fillDefaults(source, destination);
+		
+		IConverter converter = strategy.converter;
+		assertNotNull("converter not null", converter);
+		assertEquals("fromType [" + fromType + "]" , fromType, converter.getFromType());
+		assertEquals("toType [" + toType + "]", toType, converter.getToType());
+		assertTrue("converter should be instanceof " + converterType
+				+ " but was instanceof " + converter.getClass(), converterType
+				.isInstance(converter));
+	}
+	
+	class UpdateStrategyStub extends UpdateValueStrategy {
+		IConverter converter;
+		
+		protected void fillDefaults(IObservableValue source,
+				IObservableValue destination) {
+			super.fillDefaults(source, destination);
+		}
+		
+		/* (non-Javadoc)
+		 * @see org.eclipse.core.databinding.UpdateValueStrategy#setConverter(org.eclipse.core.databinding.conversion.IConverter)
+		 */
+		public UpdateValueStrategy setConverter(IConverter converter) {
+			this.converter = converter;
+			return super.setConverter(converter);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateValueStrategyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateValueStrategyTest.java
new file mode 100644
index 0000000..a6eea84
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/UpdateValueStrategyTest.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 270461
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+
+import org.eclipse.core.databinding.BindingException;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.conversion.IdentityConverter;
+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.StringToByteValidator;
+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.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class UpdateValueStrategyTest extends AbstractDefaultRealmTestCase {
+	public void testDefaultValidatorForStringToInteger() throws Exception {
+		assertDefaultValidator(String.class, Integer.class,
+				StringToIntegerValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToIntegerPrimitive()
+			throws Exception {
+		assertDefaultValidator(String.class, Integer.TYPE,
+				StringToIntegerValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToLong() throws Exception {
+		assertDefaultValidator(String.class, Long.class,
+				StringToLongValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToLongPrimitive() throws Exception {
+		assertDefaultValidator(String.class, Long.TYPE,
+				StringToLongValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToFloat() throws Exception {
+		assertDefaultValidator(String.class, Float.class,
+				StringToFloatValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToFloatPrimitive()
+			throws Exception {
+		assertDefaultValidator(String.class, Float.TYPE,
+				StringToFloatValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToDouble() throws Exception {
+		assertDefaultValidator(String.class, Double.class,
+				StringToDoubleValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToDoublePrimitive()
+			throws Exception {
+		assertDefaultValidator(String.class, Double.TYPE,
+				StringToDoubleValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToByte() throws Exception {
+		assertDefaultValidator(String.class, Byte.class,
+				StringToByteValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToBytePrimitive() throws Exception {
+		assertDefaultValidator(String.class, Byte.TYPE,
+				StringToByteValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToShort() throws Exception {
+		assertDefaultValidator(String.class, Short.class,
+				StringToShortValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToShortPrimitive()
+			throws Exception {
+		assertDefaultValidator(String.class, Short.TYPE,
+				StringToShortValidator.class);
+	}
+
+	public void testDefaultValidatorForStringToDate() throws Exception {
+		assertDefaultValidator(String.class, Date.class,
+				StringToDateValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToByte() throws Exception {
+		assertDefaultValidator(Integer.class, Byte.class,
+				NumberToByteValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToShort() throws Exception {
+		assertDefaultValidator(Integer.class, Short.class,
+				NumberToShortValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToInteger() throws Exception {
+		assertDefaultValidator(Short.class, Integer.class,
+				NumberToIntegerValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToLong() throws Exception {
+		assertDefaultValidator(Short.class, Long.class,
+				NumberToLongValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToFloat() throws Exception {
+		assertDefaultValidator(Short.class, Float.class,
+				NumberToFloatValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToDouble() throws Exception {
+		assertDefaultValidator(Short.class, Double.class,
+				NumberToDoubleValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToBigInteger() throws Exception {
+		assertDefaultValidator(Short.class, BigInteger.class,
+				NumberToUnboundedNumberValidator.class);
+	}
+
+	public void testDefaultValidatorForNumberToBigDecimal() throws Exception {
+		assertDefaultValidator(Short.class, BigDecimal.class,
+				NumberToUnboundedNumberValidator.class);
+	}
+
+	public void testCachesDefaultedValidators() throws Exception {
+		WritableValue source = WritableValue.withValueType(String.class);
+		WritableValue destination = WritableValue.withValueType(Integer.class);
+
+		UpdateValueStrategyStub strategy = new UpdateValueStrategyStub();
+		strategy.fillDefaults(source, destination);
+
+		IValidator validator = strategy.validator;
+		assertNotNull(validator);
+
+		strategy = new UpdateValueStrategyStub();
+		strategy.fillDefaults(source, destination);
+
+		assertSame(validator, strategy.validator);
+	}
+
+	public void testFillDefaults_AssertSourceTypeExtendsConverterFromType() {
+		// Valid use: source type String extends converter from-type Object
+		UpdateValueStrategyStub strategy = new UpdateValueStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, Object.class));
+		strategy.fillDefaults(WritableValue.withValueType(String.class),
+				WritableValue.withValueType(Object.class));
+
+		// Invalid use: source type Object does not extend converter from-type
+		// String
+		strategy = new UpdateValueStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(String.class, Object.class));
+		try {
+			strategy.fillDefaults(WritableValue.withValueType(Object.class),
+					WritableValue.withValueType(Object.class));
+			fail("Expected BindingException since Object does not extend String");
+		} catch (BindingException expected) {
+		}
+	}
+
+	public void testFillDefaults_AssertConverterToTypeExtendsDestinationType() {
+		// Valid use: converter to-type String extends destination type Object
+		UpdateValueStrategyStub strategy = new UpdateValueStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, String.class));
+		strategy.fillDefaults(WritableValue.withValueType(Object.class),
+				WritableValue.withValueType(Object.class));
+
+		// Invalid use: converter to-type Object does not extend destination
+		// type String
+		strategy = new UpdateValueStrategyStub();
+		strategy
+				.setConverter(new IdentityConverter(Object.class, Object.class));
+		try {
+			strategy.fillDefaults(WritableValue.withValueType(Object.class),
+					WritableValue.withValueType(String.class));
+			fail("Expected BindingException since Object does not extend String");
+		} catch (BindingException expected) {
+		}
+	}
+
+	private void assertDefaultValidator(Class fromType, Class toType,
+			Class validatorType) {
+		WritableValue source = WritableValue.withValueType(fromType);
+		WritableValue destination = WritableValue.withValueType(toType);
+
+		UpdateValueStrategyStub strategy = new UpdateValueStrategyStub();
+		strategy.fillDefaults(source, destination);
+
+		IValidator validator = strategy.validator;
+		assertNotNull("validator not null", validator);
+		assertTrue("converter instanceof " + validatorType, validatorType
+				.isInstance(validator));
+	}
+
+	class UpdateValueStrategyStub extends UpdateValueStrategy {
+		IValidator validator;
+
+		protected void fillDefaults(IObservableValue source,
+				IObservableValue destination) {
+			super.fillDefaults(source, destination);
+		}
+
+		protected IValidator createValidator(Object fromType, Object toType) {
+			validator = super.createValidator(fromType, toType);
+			return validator;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ValueBindingTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ValueBindingTest.java
new file mode 100755
index 0000000..6cd6585
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/ValueBindingTest.java
@@ -0,0 +1,434 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bugs 116920, 164653, 159768
+ *     Matthew Hall - bugs 260329, 271148
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.conversion.Converter;
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.Diffs;
+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.databinding.observable.value.ValueDiff;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.BindingStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class ValueBindingTest extends AbstractDefaultRealmTestCase {
+	private WritableValue target;
+
+	private WritableValue model;
+
+	private DataBindingContext dbc;
+	private Binding binding;
+
+	private List log;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		target = WritableValue.withValueType(String.class);
+		model = WritableValue.withValueType(String.class);
+		dbc = new DataBindingContext();
+		log = new ArrayList();
+	}
+
+	/**
+	 * Bug 152543.
+	 * 
+	 * @throws Exception
+	 */
+	public void testNoUpdateTargetFromModel() throws Exception {
+		try {
+			new DataBindingContext().bindValue(new ObservableValueStub(),
+					new ObservableValueStub(), new UpdateValueStrategy(
+							UpdateValueStrategy.POLICY_NEVER),
+					new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER));
+		} catch (Exception e) {
+			fail(e.getMessage());
+		}
+	}
+
+	public void testValuePropagation() throws Exception {
+		String initialValue = "value";
+		model.setValue(initialValue);
+
+		assertFalse(model.getValue().equals(target.getValue()));
+		dbc.bindValue(target, model);
+
+		assertEquals(target.getValue(), model.getValue());
+	}
+
+	public void testGetTarget() throws Exception {
+		Binding binding = dbc.bindValue(target, model);
+
+		assertEquals(target, binding.getTarget());
+	}
+
+	public void testGetModel() throws Exception {
+		Binding binding = dbc.bindValue(target, model);
+
+		assertEquals(model, binding.getModel());
+	}
+
+	public void testOKStatusInValidationUpdatesModel() throws Exception {
+		Binding binding = dbc.bindValue(target, model);
+
+		String value = "value";
+		assertFalse(value.equals(model.getValue()));
+		target.setValue(value);
+
+		assertEquals("value copied to model", value, model.getValue());
+		assertTrue(((IStatus) binding.getValidationStatus().getValue()).isOK());
+	}
+
+	public void testWarningStatusInValidationUpdatesModel() throws Exception {
+		Binding binding = dbc.bindValue(target, model,
+				new UpdateValueStrategy()
+						.setAfterGetValidator(warningValidator()), null);
+
+		String value = "value";
+		assertFalse(value.equals(model.getValue()));
+		target.setValue(value);
+
+		assertEquals("value copied to model", value, model.getValue());
+		assertEquals("warning status", IStatus.WARNING, ((IStatus) binding
+				.getValidationStatus().getValue()).getSeverity());
+	}
+
+	public void testInfoStatusInValidationUpdatesModel() throws Exception {
+		Binding binding = dbc
+				.bindValue(target, model, new UpdateValueStrategy()
+						.setAfterGetValidator(infoValidator()), null);
+
+		String value = "value";
+		assertFalse(value.equals(model.getValue()));
+		target.setValue(value);
+
+		assertEquals("value copied to model", value, model.getValue());
+		assertEquals("info status", IStatus.INFO, ((IStatus) binding
+				.getValidationStatus().getValue()).getSeverity());
+	}
+
+	public void testErrorStatusInValidationDoesNotUpdateModel()
+			throws Exception {
+		Binding binding = dbc.bindValue(target, model,
+				new UpdateValueStrategy()
+						.setAfterGetValidator(errorValidator()), null);
+
+		String value = "value";
+		assertFalse(value.equals(model.getValue()));
+		target.setValue(value);
+
+		assertFalse("value not copied to model", value.equals(model.getValue()));
+		assertEquals("error status", IStatus.ERROR, ((IStatus) binding
+				.getValidationStatus().getValue()).getSeverity());
+	}
+
+	public void testCancelStatusInValidationDoesNotUpdateModel()
+			throws Exception {
+		Binding binding = dbc.bindValue(target, model,
+				new UpdateValueStrategy()
+						.setAfterGetValidator(cancelValidator()), null);
+
+		String value = "value";
+		assertFalse(value.equals(model.getValue()));
+		target.setValue(value);
+
+		assertFalse("value not copied to model", value.equals(model.getValue()));
+		assertEquals("cancel status", IStatus.CANCEL, ((IStatus) binding
+				.getValidationStatus().getValue()).getSeverity());
+	}
+
+	public void testStatusesFromEveryPhaseAreReturned() throws Exception {
+		UpdateValueStrategy strategy = new UpdateValueStrategy() {
+			protected IStatus doSet(IObservableValue observableValue,
+					Object value) {
+				super.doSet(observableValue, value);
+				return ValidationStatus.info("");
+			}
+		};
+
+		strategy.setAfterGetValidator(warningValidator());
+		strategy.setAfterConvertValidator(infoValidator());
+		strategy.setBeforeSetValidator(warningValidator());
+
+		Binding binding = dbc.bindValue(target, model, strategy, null);
+		String value = "value";
+		assertFalse(value.equals(model.getValue()));
+
+		target.setValue(value);
+		assertEquals(value, model.getValue());
+		IStatus status = (IStatus) binding.getValidationStatus().getValue();
+		assertTrue(status.isMultiStatus());
+		assertEquals("max status", IStatus.WARNING, status.getSeverity());
+
+		MultiStatus multiStatus = (MultiStatus) status;
+		assertEquals(4, multiStatus.getChildren().length);
+		IStatus[] children = multiStatus.getChildren();
+
+		assertEquals("after get severity", IStatus.WARNING, children[0]
+				.getSeverity());
+		assertEquals("after convert severity", IStatus.INFO, children[1]
+				.getSeverity());
+		assertEquals("before set severity", IStatus.WARNING, children[2]
+				.getSeverity());
+		assertEquals("doSet severity", IStatus.INFO, children[3].getSeverity());
+	}
+
+	public void testStatusIsInstanceOfBindingStatus() throws Exception {
+		Binding binding = dbc.bindValue(target, model);
+		assertTrue(binding.getValidationStatus().getValue() instanceof BindingStatus);
+	}
+
+	public void testDiffsAreCheckedForEqualityBeforeUpdate() throws Exception {
+		class WritableValueStub extends WritableValue {
+			public WritableValueStub() {
+				super("", String.class);
+			}
+
+			protected void fireValueChange(ValueDiff diff) {
+				super.fireValueChange(diff);
+			}
+		}
+
+		WritableValueStub target = new WritableValueStub();
+		WritableValue model = WritableValue.withValueType(String.class);
+
+		class Strategy extends UpdateValueStrategy {
+			int afterGetCount;
+
+			public IStatus validateAfterGet(Object value) {
+				afterGetCount++;
+				return super.validateAfterGet(value);
+			}
+		}
+
+		Strategy strategy = new Strategy();
+		dbc.bindValue(target, model, strategy, null);
+		int count = strategy.afterGetCount;
+
+		target.fireValueChange(Diffs.createValueDiff("", ""));
+		assertEquals("update does not occur", count, strategy.afterGetCount);
+	}
+
+	public void testPostInit_UpdatePolicy_UpdateToTarget_UpdateToModel() {
+		bindLoggingValue(
+				loggingTargetToModelStrategy(UpdateValueStrategy.POLICY_UPDATE),
+				loggingModelToTargetStrategy(UpdateValueStrategy.POLICY_UPDATE));
+		assertEquals(Arrays.asList(new String[] { "model-get", "model-convert",
+				"model-after-convert", "target-before-set", "target-set",
+				"target-get", "target-convert", "target-after-convert",
+				"model-before-set" }), log);
+	}
+
+	public void testPostInit_UpdatePolicy_UpdateToTarget_ConvertToModel() {
+		bindLoggingValue(
+				loggingTargetToModelStrategy(UpdateValueStrategy.POLICY_CONVERT),
+				loggingModelToTargetStrategy(UpdateValueStrategy.POLICY_UPDATE));
+		assertEquals(Arrays.asList(new String[] { "model-get", "model-convert",
+				"model-after-convert", "target-before-set", "target-set",
+				"target-get", "target-convert", "target-after-convert",
+				"model-before-set" }), log);
+	}
+
+	public void testPostInit_UpdatePolicy_UpdateToTarget_OnRequestToModel() {
+		bindLoggingValue(
+				loggingTargetToModelStrategy(UpdateValueStrategy.POLICY_ON_REQUEST),
+				loggingModelToTargetStrategy(UpdateValueStrategy.POLICY_UPDATE));
+		assertEquals(Arrays.asList(new String[] { "model-get", "model-convert",
+				"model-after-convert", "target-before-set", "target-set" }),
+				log);
+
+		log.clear();
+		target.setValue(new Object());
+		assertEquals(Collections.singletonList("target-set"), log);
+
+		log.clear();
+		binding.validateTargetToModel();
+		assertEquals(
+				Arrays.asList(new String[] { "target-get", "target-convert",
+						"target-after-convert", "model-before-set" }), log);
+
+		log.clear();
+		binding.updateTargetToModel();
+		assertEquals(Arrays.asList(new String[] { "target-get",
+				"target-convert", "target-after-convert", "model-before-set",
+				"model-set" }), log);
+	}
+
+	public void testPostInit_UpdatePolicy_UpdateToTarget_NeverToModel() {
+		bindLoggingValue(
+				loggingTargetToModelStrategy(UpdateValueStrategy.POLICY_NEVER),
+				loggingModelToTargetStrategy(UpdateValueStrategy.POLICY_UPDATE));
+		assertEquals(Arrays.asList(new String[] { "model-get", "model-convert",
+				"model-after-convert", "target-before-set", "target-set" }),
+				log);
+
+		log.clear();
+		target.setValue(new Object());
+		assertEquals(Collections.singletonList("target-set"), log);
+
+		log.clear();
+		binding.validateTargetToModel();
+		assertEquals(Collections.EMPTY_LIST, log);
+
+		log.clear();
+		binding.updateTargetToModel();
+		assertEquals(Collections.EMPTY_LIST, log);
+	}
+
+	public void testPostInit_UpdatePolicy_ConvertToTarget_UpdateToModel() {
+		bindLoggingValue(
+				loggingTargetToModelStrategy(UpdateValueStrategy.POLICY_UPDATE),
+				loggingModelToTargetStrategy(UpdateValueStrategy.POLICY_CONVERT));
+		assertEquals(
+				Arrays.asList(new String[] { "model-get", "model-convert",
+						"model-after-convert", "target-before-set",
+						"target-get", "target-convert", "target-after-convert",
+						"model-before-set" }), log);
+
+		log.clear();
+		target.setValue(new Object());
+		assertEquals(Arrays.asList(new String[] { "target-set", "target-get",
+				"target-convert", "target-after-convert", "model-before-set",
+				"model-set" }), log);
+
+		log.clear();
+		model.setValue(new Object());
+		assertEquals(Arrays.asList(new String[] { "model-set", "model-get",
+				"model-convert", "model-after-convert" }), log);
+	}
+
+	private void bindLoggingValue(UpdateValueStrategy targetToModel,
+			UpdateValueStrategy modelToTarget) {
+		// Set model and target to different values to ensure we get a change
+		// notification when the binding is first created.
+		model.setValue("1");
+		target.setValue("2");
+
+		target.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				log.add("target-set");
+			}
+		});
+		model.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				log.add("model-set");
+			}
+		});
+		binding = dbc.bindValue(target, model, targetToModel, modelToTarget);
+	}
+
+	private UpdateValueStrategy loggingModelToTargetStrategy(int updatePolicy) {
+		return new UpdateValueStrategy(updatePolicy).setAfterGetValidator(
+				loggingValidator(log, "model-get")).setConverter(
+				loggingConverter(log, "model-convert"))
+				.setAfterConvertValidator(
+						loggingValidator(log, "model-after-convert"))
+				.setBeforeSetValidator(
+						loggingValidator(log, "target-before-set"));
+	}
+
+	private UpdateValueStrategy loggingTargetToModelStrategy(int updatePolicy) {
+		return new UpdateValueStrategy(updatePolicy).setAfterGetValidator(
+				loggingValidator(log, "target-get")).setConverter(
+				loggingConverter(log, "target-convert"))
+				.setAfterConvertValidator(
+						loggingValidator(log, "target-after-convert"))
+				.setBeforeSetValidator(
+						loggingValidator(log, "model-before-set"));
+	}
+
+	private IValidator loggingValidator(final List log, final String message) {
+		return new IValidator() {
+			public IStatus validate(Object value) {
+				log.add(message);
+				return ValidationStatus.ok();
+			}
+		};
+	}
+
+	private IConverter loggingConverter(final List log, final String message) {
+		return new Converter(null, null) {
+			public Object convert(Object fromObject) {
+				log.add(message);
+				return fromObject;
+			}
+		};
+	}
+
+	private IValidator warningValidator() {
+		return new IValidator() {
+			public IStatus validate(Object value) {
+				return ValidationStatus.warning("");
+			}
+		};
+	}
+
+	private IValidator infoValidator() {
+		return new IValidator() {
+			public IStatus validate(Object value) {
+				return ValidationStatus.info("");
+			}
+		};
+	}
+
+	private IValidator errorValidator() {
+		return new IValidator() {
+			public IStatus validate(Object value) {
+				return ValidationStatus.error("");
+			}
+		};
+	}
+
+	private IValidator cancelValidator() {
+		return new IValidator() {
+			public IStatus validate(Object value) {
+				return ValidationStatus.cancel("");
+			}
+		};
+	}
+
+	private static class ObservableValueStub extends AbstractObservableValue {
+		protected Object doGetValue() {
+			// do nothing
+			return null;
+		}
+
+		public Object getValueType() {
+			// do nothing
+			return null;
+		}
+
+		protected void doSetValue(Object value) {
+
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/AnonymousBeanValuePropertyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/AnonymousBeanValuePropertyTest.java
new file mode 100644
index 0000000..803ba13
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/AnonymousBeanValuePropertyTest.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.beans;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class AnonymousBeanValuePropertyTest extends
+		AbstractDefaultRealmTestCase {
+	public void testObserveDetailHavingNullValueType_UseExplicitValueType() {
+		IObservableValue master = WritableValue.withValueType(null);
+		IValueProperty prop = BeanProperties.value("value", String.class);
+
+		IObservableValue detail = prop.observeDetail(master);
+
+		assertEquals(String.class, detail.getValueType());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/AnonymousPojoValuePropertyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/AnonymousPojoValuePropertyTest.java
new file mode 100644
index 0000000..ffc2432
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/AnonymousPojoValuePropertyTest.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.beans;
+
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class AnonymousPojoValuePropertyTest extends
+		AbstractDefaultRealmTestCase {
+	public void testObserveDetailHavingNullValueType_UseExplicitValueType() {
+		IObservableValue master = WritableValue.withValueType(null);
+		IValueProperty prop = PojoProperties.value("value", String.class);
+
+		IObservableValue detail = prop.observeDetail(master);
+
+		assertEquals(String.class, detail.getValueType());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/BeanPropertiesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/BeanPropertiesTest.java
new file mode 100644
index 0000000..23de654
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/BeanPropertiesTest.java
@@ -0,0 +1,41 @@
+package org.eclipse.core.tests.databinding.beans;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class BeanPropertiesTest extends AbstractDefaultRealmTestCase {
+	private Bean bean;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		bean = new Bean();
+	}
+
+	public void testValue_ValueFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.value(Bean.class, "value")
+				.valueFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testSet_SetFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.set(Bean.class, "set")
+				.setFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testList_ListFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.list(Bean.class, "list")
+				.listFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testMap_MapFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.map(Bean.class, "map")
+				.mapFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/BeansObservablesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/BeansObservablesTest.java
new file mode 100644
index 0000000..5f00ac5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/BeansObservablesTest.java
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 164268, 171616
+ *     Mike Evans - bug 217558
+ *     Matthew Hall - bugs 221351, 246625, 260329, 264619
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.beans;
+
+import java.util.Arrays;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+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.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.beans.BeanObservableListDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableSetDecorator;
+import org.eclipse.core.internal.databinding.beans.BeanObservableValueDecorator;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ */
+public class BeansObservablesTest extends AbstractDefaultRealmTestCase {
+	Bean[] elements = null;
+	Bean model = null;
+	Class elementType = null;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		elements = new Bean[] { new Bean("1"), new Bean("2"), new Bean("3") };
+		model = new Bean(elements);
+		model.setList(Arrays.asList(elements));
+		elementType = Bean.class;
+	}
+
+	public void testObserveListArrayInferredElementType() throws Exception {
+		IObservableList list = BeansObservables.observeList(Realm.getDefault(),
+				model, "list", null);
+		assertEquals("element type", Object.class, list.getElementType());
+	}
+
+	public void testObserveListNonInferredElementType() throws Exception {
+		elementType = Object.class;
+		IObservableList list = BeansObservables.observeList(Realm.getDefault(),
+				model, "list", null);
+		assertEquals("element type", elementType, list.getElementType());
+	}
+
+	public void testListFactory() throws Exception {
+		IObservableFactory factory = BeansObservables.listFactory(Realm
+				.getDefault(), "list", elementType);
+		IObservableList list = (IObservableList) factory
+				.createObservable(model);
+
+		assertTrue("elements of the list", Arrays.equals(elements, list
+				.toArray(new Bean[list.size()])));
+		assertEquals("element type", elementType, list.getElementType());
+	}
+
+	public void testObserveDetailListElementType() throws Exception {
+		WritableValue parent = WritableValue.withValueType(Bean.class);
+		parent.setValue(model);
+		IObservableList list = BeansObservables.observeDetailList(parent,
+				"list", elementType);
+
+		assertEquals("element type", elementType, list.getElementType());
+		assertTrue("elements of list", Arrays.equals(elements, list
+				.toArray(new Bean[list.size()])));
+	}
+
+	public void testObserveDetailValueIBeanObservable() throws Exception {
+		WritableValue parent = WritableValue.withValueType(Bean.class);
+		Bean bean = new Bean();
+		parent.setValue(bean);
+
+		IObservableValue detailValue = BeansObservables.observeDetailValue(
+				parent, "value", String.class);
+		assertTrue(detailValue instanceof IBeanObservable);
+
+		BeanObservableValueDecorator beanObservable = (BeanObservableValueDecorator) detailValue;
+		assertEquals("property descriptor", Bean.class.getMethod("getValue",
+				null), beanObservable.getPropertyDescriptor().getReadMethod());
+		assertEquals("observed", bean, beanObservable.getObserved());
+		assertTrue("delegate", beanObservable.getDecorated().getClass()
+				.getName().endsWith("DetailObservableValue"));
+	}
+
+	public void testObserveDetailValueNullOuterElementType() throws Exception {
+		WritableValue parent = new WritableValue(new Bean(), null);
+
+		IObservableValue detailValue = BeansObservables.observeDetailValue(
+				parent, "value", String.class);
+
+		assertNull("property descriptor", ((IBeanObservable) detailValue)
+				.getPropertyDescriptor());
+	}
+
+	public void testObservableDetailListIBeanObservable() throws Exception {
+		WritableValue parent = WritableValue.withValueType(Bean.class);
+		Bean bean = new Bean();
+		parent.setValue(bean);
+
+		IObservableList detailList = BeansObservables.observeDetailList(parent,
+				"list", Bean.class);
+		assertTrue("detail is not an IBeanObservable",
+				detailList instanceof IBeanObservable);
+
+		BeanObservableListDecorator beanObservable = (BeanObservableListDecorator) detailList;
+		assertEquals("property descriptor", Bean.class.getMethod("getList",
+				null), beanObservable.getPropertyDescriptor().getReadMethod());
+		assertEquals("observed", bean, beanObservable.getObserved());
+
+		// DetailObservableList is package level we can do a straight instanceof
+		// check
+		assertTrue("delegate is the observed", beanObservable.getDecorated()
+				.equals(detailList));
+	}
+
+	public void testObservableDetailListNullOuterElementType() throws Exception {
+		WritableValue parent = new WritableValue(new Bean(), null);
+
+		IObservableList detailList = BeansObservables.observeDetailList(parent,
+				"list", Bean.class);
+
+		assertNull("property descriptor", ((IBeanObservable) detailList)
+				.getPropertyDescriptor());
+	}
+
+	public void testObservableDetailSetIBeanObservable() throws Exception {
+		WritableValue parent = WritableValue.withValueType(Bean.class);
+		Bean bean = new Bean();
+		parent.setValue(bean);
+
+		IObservableSet detailSet = BeansObservables.observeDetailSet(parent,
+				"set", Bean.class);
+		assertTrue("detail is not an IBeanObservable",
+				detailSet instanceof IBeanObservable);
+
+		BeanObservableSetDecorator beanObservable = (BeanObservableSetDecorator) detailSet;
+		assertEquals("property descriptor", Bean.class
+				.getMethod("getSet", null), beanObservable
+				.getPropertyDescriptor().getReadMethod());
+		assertEquals("observed", bean, beanObservable.getObserved());
+
+		// DetailObservableSet is package level we can't do a straight
+		// instanceof check
+		assertTrue("delegate is the observed", beanObservable.getDecorated()
+				.equals(detailSet));
+	}
+
+	public void testObservableDetailSetNullOuterElementType() throws Exception {
+		WritableValue parent = new WritableValue(new Bean(), null);
+
+		IObservableSet detailSet = BeansObservables.observeDetailSet(parent,
+				"set", Bean.class);
+
+		assertNull("property descriptor", ((IBeanObservable) detailSet)
+				.getPropertyDescriptor());
+	}
+
+	public void testObserveSetElementType() throws Exception {
+		Bean bean = new Bean();
+		IObservableSet observableSet = BeansObservables.observeSet(Realm
+				.getDefault(), bean, "set", Bean.class);
+		assertEquals(Bean.class, observableSet.getElementType());
+	}
+
+	public void testObserveSetNonInferredElementType() throws Exception {
+		Bean bean = new Bean();
+		IObservableSet observableSet = BeansObservables.observeSet(Realm
+				.getDefault(), bean, "set");
+		assertEquals(Object.class, observableSet.getElementType());
+	}
+
+	/**
+	 * Test for fix for Bug 217558 [DataBinding] Databinding -
+	 * BeansObservables.observeList() - error when external code modifies
+	 * observed list.
+	 */
+	public void testHandleExternalChangeToProperty() {
+		Bean targetBean = new Bean();
+		IObservableList modelObservable = BeansObservables.observeList(Realm
+				.getDefault(), model, "array", elementType);
+		IObservableList targetObservable = BeansObservables.observeList(Realm
+				.getDefault(), targetBean, "array", elementType);
+
+		DataBindingContext context = new DataBindingContext(Realm.getDefault());
+		try {
+			// bind two beans and check the binding works
+			context.bindList(targetObservable, modelObservable);
+			assertTrue(Arrays.equals(elements, targetBean.getArray()));
+
+			// set source direct - target databinding still works...
+			Bean[] newElements = new Bean[] { new Bean("4"), new Bean("5"),
+					new Bean("6") };
+			model.setArray(newElements);
+			assertTrue(Arrays.equals(newElements, targetBean.getArray()));
+
+			// ... but setting the model's list breaks databinding the other
+			// way...
+
+			// ... so setting target direct breaks databinding without fix and
+			// the assert would fail
+			newElements = new Bean[] { new Bean("7"), new Bean("8"),
+					new Bean("9") };
+			targetBean.setArray(newElements);
+			assertTrue(Arrays.equals(newElements, model.getArray()));
+		} finally {
+			// context only needed for this test so not put in setUp / tearDown
+			context.dispose();
+		}
+
+	}
+
+	public void testObserveDetailValue_ValueType() {
+		Bean inner = new Bean("string");
+		Bean outer = new Bean(inner);
+		IValueProperty beanProperty = BeanProperties.value("bean");
+		IObservableValue beanObservable = beanProperty.observe(outer);
+		assertEquals(Bean.class, beanObservable.getValueType());
+
+		IValueProperty valueProperty = BeanProperties.value("value");
+		IObservableValue valueObservable = valueProperty
+				.observeDetail(beanObservable);
+		assertEquals(String.class, valueObservable.getValueType());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/PojoObservablesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/PojoObservablesTest.java
new file mode 100644
index 0000000..be8a123
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/PojoObservablesTest.java
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 194734, 264619
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.beans;
+
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ */
+public class PojoObservablesTest extends AbstractDefaultRealmTestCase {
+	private Bean pojo;
+	private String propertyName;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		pojo = new Bean();
+		propertyName = "value";
+	}
+
+	public void testObserveValue_ReturnsIBeanObservable() throws Exception {
+		IObservableValue value = PojoObservables.observeValue(pojo,
+				propertyName);
+
+		assertNotNull(value);
+		assertTrue(value instanceof IBeanObservable);
+	}
+
+	public void testObserveValue_DoesNotAttachListeners() throws Exception {
+		IObservableValue value = PojoObservables.observeValue(pojo,
+				propertyName);
+
+		ChangeEventTracker.observe(value);
+		assertFalse(pojo.hasListeners(propertyName));
+	}
+
+	public void testObservableValueWithRealm_ReturnsIBeanObservable()
+			throws Exception {
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableValue value = PojoObservables.observeValue(realm, pojo,
+				propertyName);
+
+		assertNotNull(value);
+		assertTrue(value instanceof IBeanObservable);
+	}
+
+	public void testObservableMap_ReturnsIBeanObservable() throws Exception {
+		IObservableSet set = new WritableSet();
+		set.add(new Bean());
+
+		IObservableMap map = PojoObservables.observeMap(set, Bean.class,
+				propertyName);
+		assertNotNull(map);
+		assertTrue(map instanceof IBeanObservable);
+	}
+
+	public void testObservableMap_DoesNotAttachListeners() throws Exception {
+		IObservableSet set = new WritableSet();
+		set.add(pojo);
+
+		IObservableMap map = PojoObservables.observeMap(set, Bean.class,
+				propertyName);
+		assertFalse(pojo.hasListeners(propertyName));
+		ChangeEventTracker.observe(map);
+		assertFalse(pojo.hasListeners(propertyName));
+	}
+
+	public void testObserveMaps_ReturnsMaps() throws Exception {
+		IObservableSet set = new WritableSet();
+		set.add(pojo);
+
+		IObservableMap[] maps = PojoObservables.observeMaps(set, Bean.class,
+				new String[] { "value", "class" });
+		assertEquals(2, maps.length);
+	}
+
+	public void testObserveListWithElementType_ReturnsIBeanObservable()
+			throws Exception {
+		IObservableList list = PojoObservables.observeList(Realm.getDefault(),
+				pojo, "list", String.class);
+		assertTrue(list instanceof IBeanObservable);
+	}
+
+	public void testObserveListWithElementType_DoesNotAttachListeners()
+			throws Exception {
+		IObservableList observable = PojoObservables.observeList(Realm
+				.getDefault(), pojo, "list", String.class);
+		assertFalse(pojo.hasListeners("list"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("list"));
+	}
+
+	public void testObserveList_ReturnsIBeanObservable() throws Exception {
+		IObservableList observable = PojoObservables.observeList(Realm
+				.getDefault(), pojo, "list");
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testObserveList_DoesNotAttachListeners() throws Exception {
+		IObservableList observable = PojoObservables.observeList(Realm
+				.getDefault(), pojo, "list");
+		assertFalse(pojo.hasListeners("list"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("list"));
+	}
+
+	public void testObserveSetWithElementType_ReturnsIBeanObservable()
+			throws Exception {
+		IObservableSet list = PojoObservables.observeSet(Realm.getDefault(),
+				pojo, "set", String.class);
+		assertTrue(list instanceof IBeanObservable);
+	}
+
+	public void testObserveSetWithElementType_DoesNotAttachListeners()
+			throws Exception {
+		IObservableSet observable = PojoObservables.observeSet(Realm
+				.getDefault(), pojo, "set", String.class);
+		assertFalse(pojo.hasListeners("set"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("set"));
+	}
+
+	public void testObserveSet_ReturnsIBeanObservable() throws Exception {
+		IObservableSet list = PojoObservables.observeSet(Realm.getDefault(),
+				pojo, "set");
+		assertTrue(list instanceof IBeanObservable);
+	}
+
+	public void testObserveSet_DoesNotAttachListeners() throws Exception {
+		IObservableSet observable = PojoObservables.observeSet(Realm
+				.getDefault(), pojo, "set");
+		assertFalse(pojo.hasListeners("set"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("set"));
+	}
+
+	public void testValueFactory_DoesNotAttachListeners() throws Exception {
+		IObservableFactory factory = PojoObservables.valueFactory(Realm
+				.getDefault(), "value");
+		IObservableValue observable = (IObservableValue) factory
+				.createObservable(pojo);
+
+		assertFalse(pojo.hasListeners("value"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("value"));
+	}
+
+	public void testListFactory_DoesNotAttachListeners() throws Exception {
+		IObservableFactory factory = PojoObservables.listFactory(Realm
+				.getDefault(), "list", String.class);
+		IObservableList observable = (IObservableList) factory
+				.createObservable(pojo);
+
+		assertFalse(pojo.hasListeners("value"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("value"));
+	}
+
+	public void testSetFactory_DoesNotAttachListeners() throws Exception {
+		IObservableFactory factory = PojoObservables.setFactory(Realm
+				.getDefault(), propertyName);
+		IObservableSet observable = (IObservableSet) factory
+				.createObservable(pojo);
+
+		assertFalse(pojo.hasListeners("set"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("set"));
+	}
+
+	public void testSetFactoryWithElementType_DoesNotAttachListeners()
+			throws Exception {
+		IObservableFactory factory = PojoObservables.setFactory(Realm
+				.getDefault(), propertyName, String.class);
+		IObservableSet observable = (IObservableSet) factory
+				.createObservable(pojo);
+
+		assertFalse(pojo.hasListeners("set"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(pojo.hasListeners("set"));
+	}
+
+	public void testObserveDetailValue_ValueType() {
+		Bean inner = new Bean("string");
+		Bean outer = new Bean(inner);
+		IValueProperty beanProperty = PojoProperties.value("bean");
+		IObservableValue beanObservable = beanProperty.observe(outer);
+		assertEquals(Bean.class, beanObservable.getValueType());
+
+		IValueProperty valueProperty = PojoProperties.value("value");
+		IObservableValue valueObservable = valueProperty
+				.observeDetail(beanObservable);
+		assertEquals(String.class, valueObservable.getValueType());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/PojoPropertiesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/PojoPropertiesTest.java
new file mode 100644
index 0000000..5dcf1c2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/beans/PojoPropertiesTest.java
@@ -0,0 +1,41 @@
+package org.eclipse.core.tests.databinding.beans;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class PojoPropertiesTest extends AbstractDefaultRealmTestCase {
+	private Bean bean;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		bean = new Bean();
+	}
+
+	public void testValue_ValueFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.value(Bean.class, "value")
+				.valueFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testSet_SetFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.set(Bean.class, "set")
+				.setFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testList_ListFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.list(Bean.class, "list")
+				.listFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+
+	public void testMap_MapFactory_ProducesIBeanObservable() {
+		IObservable observable = BeanProperties.map(Bean.class, "map")
+				.mapFactory().createObservable(bean);
+		assertTrue(observable instanceof IBeanObservable);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/conversion/NumberToStringConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/conversion/NumberToStringConverterTest.java
new file mode 100644
index 0000000..037ec3c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/conversion/NumberToStringConverterTest.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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     
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.conversion;
+
+import java.lang.reflect.Constructor;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.conversion.NumberToStringConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToStringConverterTest extends TestCase {
+	private NumberFormat numberFormat;
+	private NumberFormat integerFormat;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		numberFormat = NumberFormat.getNumberInstance();
+		integerFormat = NumberFormat.getIntegerInstance();
+	}
+
+	public void testFromTypes() throws Exception {
+		assertEquals("Integer.class", Integer.class, NumberToStringConverter
+				.fromInteger(false).getFromType());
+		assertEquals("Integer.TYPE", Integer.TYPE, NumberToStringConverter
+				.fromInteger(true).getFromType());
+		assertEquals("Double.class", Double.class, NumberToStringConverter
+				.fromDouble(false).getFromType());
+		assertEquals("Double.TYPE", Double.TYPE, NumberToStringConverter
+				.fromDouble(true).getFromType());
+		assertEquals("Long.class", Long.class, NumberToStringConverter
+				.fromLong(false).getFromType());
+		assertEquals("Long.TYPE", Long.TYPE, NumberToStringConverter.fromLong(
+				true).getFromType());
+		assertEquals("Float.class", Float.class, NumberToStringConverter
+				.fromFloat(false).getFromType());
+		assertEquals("Float.TYPE", Float.TYPE, NumberToStringConverter
+				.fromFloat(true).getFromType());
+		assertEquals("BigInteger.class", BigInteger.class,
+				NumberToStringConverter.fromBigInteger().getFromType());
+		assertEquals("BigDecimal.class", BigDecimal.class,
+				NumberToStringConverter.fromBigDecimal().getFromType());
+		assertEquals("Short.class", Short.class,
+				NumberToStringConverter.fromShort(false).getFromType());
+		assertEquals("Byte.class", Byte.class,
+				NumberToStringConverter.fromByte(false).getFromType());
+	}
+
+	public void testToTypeIsStringClass() throws Exception {
+		assertEquals(String.class, NumberToStringConverter.fromInteger(false)
+				.getToType());
+	}
+
+	public void testConvertIntegerToString() throws Exception {
+		Integer input = new Integer(1000);
+		String expected = integerFormat.format(input.longValue());
+
+		NumberToStringConverter converter = NumberToStringConverter
+				.fromInteger(false);
+		String result = (String) converter.convert(input);
+		assertEquals(expected, result);
+	}
+
+	public void testConvertDoubleToString() throws Exception {
+		Double input = new Double(1000.1d);
+		String expected = numberFormat.format(input.doubleValue());
+
+		NumberToStringConverter converter = NumberToStringConverter
+				.fromDouble(false);
+		String result = (String) converter.convert(input);
+		assertEquals(expected, result);
+	}
+
+	public void testConvertFloatToString() throws Exception {
+		Float input = new Float(1000.1f);
+		String expected = numberFormat.format(input.floatValue());
+
+		NumberToStringConverter converter = NumberToStringConverter
+				.fromFloat(false);
+		String result = (String) converter.convert(input);
+		assertEquals(expected, result);
+	}
+
+	public void testConvertLongToString() throws Exception {
+		Long input = new Long(1000l);
+		String expected = integerFormat.format(input.longValue());
+
+		NumberToStringConverter converter = NumberToStringConverter
+				.fromLong(false);
+		String result = (String) converter.convert(input);
+		assertEquals(expected, result);
+	}
+	
+	public void testConvertBigIntegerToString() throws Exception {
+		BigInteger input = BigInteger.valueOf(1000);
+		String expected = integerFormat.format(input);
+		
+		NumberToStringConverter converter = NumberToStringConverter.fromBigInteger();
+		String result = (String) converter.convert(input);
+		assertEquals(expected, result);
+	}
+
+	Class icuBigDecimal = null;
+	Constructor icuBigDecimalCtr = null;
+	{
+		try {
+			icuBigDecimal = Class.forName("com.ibm.icu.math.BigDecimal");
+			icuBigDecimalCtr = icuBigDecimal.getConstructor(new Class[] {BigInteger.class, int.class});
+		}
+		catch(ClassNotFoundException e) {}
+		catch(NoSuchMethodException e) {}
+	}
+	/**
+	 * Takes a java.math.BigDecimal and returns an ICU formatted string for it,
+	 * when ICU is available, otherwise platform default. Note that
+	 * Java < 1.5 did not format BigDecimals properly, truncating them via doubleValue(),
+	 * so this method will return bad results, Data Binding will not, so
+	 * the test will FAIL on Java < 1.5 under these conditions. 
+	 * @param bd
+	 * @return
+	 * @throws ClassNotFoundException
+	 * @throws NoSuchMethodException
+	 */
+	private String formatBigDecimal(BigDecimal javabd) throws Exception {
+		if(icuBigDecimal != null && icuBigDecimalCtr != null) {
+			// ICU Big Decimal constructor available
+			Number icubd = (Number) icuBigDecimalCtr.newInstance(
+					new Object[] { javabd.unscaledValue(), new Integer(javabd.scale()) });
+			return numberFormat.format(icubd);
+		}
+		throw new IllegalArgumentException("ICU not present. Cannot reliably format large BigDecimal values; needed for testing. Java platforms prior to 1.5 fail to format/parse these decimals correctly.");
+	}	
+	public void testConvertBigDecimalToString() throws Exception {
+		NumberToStringConverter converter = NumberToStringConverter.fromBigDecimal();
+		// Test 1: Decimal
+		BigDecimal input = new BigDecimal("100.23");
+		String expected = formatBigDecimal(input);
+		String result = (String) converter.convert(input);
+		assertEquals("Non-integer BigDecimal", expected, result);
+		
+		// Test 2: Long
+		input = new BigDecimal(Integer.MAX_VALUE + 100L);
+		expected = formatBigDecimal(input);
+		result = (String) converter.convert(input);
+		assertEquals("Integral BigDecimal in long range", expected, result);
+		
+		// Test 3: BigInteger range
+		input = new BigDecimal(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(100L)));
+		expected = formatBigDecimal(input);
+		result = (String) converter.convert(input);
+		assertEquals("Integral BigDecimal in BigInteger range", expected, result);
+
+		// Test 4: Very high precision Decimal
+		input = new BigDecimal("100404101.233456783456788934567893456789231982001345678234567890");
+		expected = formatBigDecimal(input);
+		result = (String) converter.convert(input);
+		assertEquals("High-precision BigDecimal", expected, result);
+		
+	}
+
+	public void testNullSourceConvertsToEmptyString() throws Exception {
+		NumberToStringConverter converter = NumberToStringConverter
+				.fromInteger(false);
+		assertEquals("", converter.convert(null));
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/conversion/StringToNumberConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/conversion/StringToNumberConverterTest.java
new file mode 100644
index 0000000..eaecbe2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/conversion/StringToNumberConverterTest.java
@@ -0,0 +1,252 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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     
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.conversion;
+
+import java.lang.reflect.Constructor;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToNumberConverterTest extends TestCase {
+	private NumberFormat numberFormat;
+	private NumberFormat numberIntegerFormat;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		numberFormat = NumberFormat.getNumberInstance();
+		numberFormat.setMaximumFractionDigits(305); // Used for BigDecimal test
+		numberFormat.setGroupingUsed(false); // Not really needed
+		numberIntegerFormat = NumberFormat.getIntegerInstance();
+	}
+
+	public void testToTypes() throws Exception {
+		assertEquals("Integer.class", Integer.class, StringToNumberConverter.toInteger(false).getToType());
+		assertEquals("Integer.TYPE", Integer.TYPE, StringToNumberConverter.toInteger(true).getToType());
+		assertEquals("Double.class", Double.class, StringToNumberConverter.toDouble(false).getToType());
+		assertEquals("Double.TYPE", Double.TYPE, StringToNumberConverter.toDouble(true).getToType());
+		assertEquals("Long.class", Long.class, StringToNumberConverter.toLong(false).getToType());
+		assertEquals("Long.TYPE", Long.TYPE, StringToNumberConverter.toLong(true).getToType());
+		assertEquals("Float.class", Float.class, StringToNumberConverter.toFloat(false).getToType());
+		assertEquals("Float.TYPE", Float.TYPE, StringToNumberConverter.toFloat(true).getToType());
+		assertEquals("BigInteger.TYPE", BigInteger.class, StringToNumberConverter.toBigInteger().getToType());
+		assertEquals("BigDecimal.TYPE", BigDecimal.class, StringToNumberConverter.toBigDecimal().getToType());
+		assertEquals("Short.class", Short.class, StringToNumberConverter.toShort(false).getToType());
+		assertEquals("Short.TYPE", Short.TYPE, StringToNumberConverter.toShort(true).getToType());
+		assertEquals("Byte.class", Byte.class, StringToNumberConverter.toByte(false).getToType());
+		assertEquals("Byte.TYPE", Byte.TYPE, StringToNumberConverter.toByte(true).getToType());
+	}
+	
+	public void testFromTypeIsString() throws Exception {
+		assertEquals(String.class, StringToNumberConverter.toInteger(false)
+				.getFromType());
+	}
+	
+	public void testConvertsToBigInteger() throws Exception {
+		BigInteger input = BigInteger.valueOf(1000);
+		
+		StringToNumberConverter converter = StringToNumberConverter.toBigInteger();
+		BigInteger result = (BigInteger) converter.convert(numberFormat.format(input));
+		
+		assertEquals(input, result);
+	}
+	
+	Class icuBigDecimal = null;
+	Constructor icuBigDecimalCtr = null;
+	{
+		try {
+			icuBigDecimal = Class.forName("com.ibm.icu.math.BigDecimal");
+			icuBigDecimalCtr = icuBigDecimal.getConstructor(new Class[] {BigInteger.class, int.class});
+		}
+		catch(ClassNotFoundException e) {}
+		catch(NoSuchMethodException e) {}
+	}
+	/**
+	 * Takes a java.math.BigDecimal and returns an ICU formatted string for it.
+	 * These tests depend on ICU to reliably format test strings for comparison. 
+	 * Java < 1.5 DecimalFormat did not format/parse BigDecimals properly, 
+	 * converting them via doubleValue(), so we have a dependency for this unit test on ICU4J.
+	 * See Bug #180392 for more info.  
+	 * @param bd
+	 * @return
+	 * @throws ClassNotFoundException
+	 * @throws NoSuchMethodException
+	 */
+	private String formatBigDecimal(BigDecimal javabd) throws Exception {
+		if(icuBigDecimal != null && icuBigDecimalCtr != null) {
+			// ICU Big Decimal constructor available
+			Number icubd = (Number) icuBigDecimalCtr.newInstance(
+					new Object[] { javabd.unscaledValue(), new Integer(javabd.scale()) });
+			return numberFormat.format(icubd);
+		}
+		throw new IllegalArgumentException("ICU not present. Cannot reliably format large BigDecimal values; needed for testing. Java platforms prior to 1.5 fail to format/parse these decimals correctly.");
+	}
+	
+	public void testConvertsToBigDecimal() throws Exception {
+		StringToNumberConverter converter = StringToNumberConverter.toBigDecimal();
+		// Test 1: Decimal
+		BigDecimal input = new BigDecimal("100.23");
+		BigDecimal result = (BigDecimal) converter.convert(formatBigDecimal(input));
+		assertEquals("Non-integer BigDecimal", input, result);
+
+		// Test 2: Long
+		input = new BigDecimal(Integer.MAX_VALUE + 100L);
+		result = (BigDecimal) converter.convert(formatBigDecimal(input));
+		assertEquals("Integral BigDecimal in long range", input, result);
+
+		// Test 3: BigInteger range
+		input = new BigDecimal("92233720368547990480");
+		result = (BigDecimal) converter.convert(formatBigDecimal(input));
+		assertEquals("Integral BigDecimal in long range", input, result);
+		
+		// Test 4: Very high precision Decimal. 
+		input = new BigDecimal("100404101.23345678345678893456789345678923198200134567823456789");
+		result = (BigDecimal) converter.convert(formatBigDecimal(input));
+		assertEquals("Non-integer BigDecimal", input, result);
+	}
+	
+	public void testConvertsToInteger() throws Exception {
+		Integer input = new Integer(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toInteger(false);
+		Integer result = (Integer) converter.convert(numberIntegerFormat.format(input
+				.longValue()));
+		assertEquals(input, result);
+	}
+
+	public void testConvertsToDouble() throws Exception {
+		Double input = new Double(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toDouble(false);
+		Double result = (Double) converter.convert(numberFormat.format(input
+				.doubleValue()));
+
+		assertEquals(input, result);
+	}
+
+	public void testConvertsToLong() throws Exception {
+		Long input = new Long(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toLong(false);
+		Long result = (Long) converter.convert(numberIntegerFormat.format(input
+				.longValue()));
+
+		assertEquals(input, result);
+	}
+
+	public void testConvertsToFloat() throws Exception {
+		Float input = new Float(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toFloat(false);
+		Float result = (Float) converter.convert(numberFormat.format(input
+				.floatValue()));
+
+		assertEquals(input, result);
+	}
+
+	public void testConvertedToIntegerPrimitive() throws Exception {
+		Integer input = new Integer(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toInteger(true);
+		Integer result = (Integer) converter.convert(numberIntegerFormat.format(input
+				.longValue()));
+		assertEquals(input, result);
+	}
+
+	public void testConvertsToDoublePrimitive() throws Exception {
+		Double input = new Double(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toDouble(true);
+		Double result = (Double) converter.convert(numberFormat.format(input
+				.doubleValue()));
+
+		assertEquals(input, result);
+	}
+
+	public void testConvertsToLongPrimitive() throws Exception {
+		Long input = new Long(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toLong(true);
+		Long result = (Long) converter.convert(numberIntegerFormat.format(input
+				.longValue()));
+
+		assertEquals(input, result);
+	}
+
+	public void testConvertsToFloatPrimitive() throws Exception {
+		Float input = new Float(1000);
+
+		StringToNumberConverter converter = StringToNumberConverter.toFloat(true);
+		Float result = (Float) converter.convert(numberFormat.format(input
+				.floatValue()));
+
+		assertEquals(input, result);
+	}
+	
+	public void testReturnsNullBoxedTypeForEmptyString() throws Exception {
+		StringToNumberConverter converter = StringToNumberConverter.toInteger(false);
+		try {
+			assertNull(converter.convert(""));
+		} catch (Exception e) {
+			fail("exception should not have been thrown");
+		}
+	}
+
+	public void testThrowsIllegalArgumentExceptionIfAskedToConvertNonString()
+			throws Exception {
+		StringToNumberConverter converter = StringToNumberConverter.toInteger(false);
+		try {
+			converter.convert(new Integer(1));
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+	
+	/**
+	 * Asserts a use case where the integer starts with a valid value but ends
+	 * in an unparsable format.
+	 * 
+	 * @throws Exception
+	 */
+	public void testInvalidInteger() throws Exception {
+		StringToNumberConverter converter = StringToNumberConverter
+				.toInteger(false);
+
+		try {
+			Object result = converter.convert("1 1 -1");
+			fail("exception should have been thrown, but result was " + result);
+		} catch (IllegalArgumentException e) {
+		}
+	}
+	
+	public void testThrowsIllegalArgumentExceptionIfNumberIsOutOfRange() throws Exception {
+		StringToNumberConverter converter = StringToNumberConverter.toInteger(false);
+		try {
+			converter.convert(numberFormat.format(Long.MAX_VALUE));
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/AbstractObservableTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/AbstractObservableTest.java
new file mode 100755
index 0000000..be67448
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/AbstractObservableTest.java
@@ -0,0 +1,313 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 116920
+ *     Matthew Hall - bugs 208332, 213145, 255734
+ *     Ovidio Mallo - bug 247741
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+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;
+import org.eclipse.jface.databinding.conformance.ObservableContractTest;
+import org.eclipse.jface.databinding.conformance.ObservableStaleContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * Tests for AbstractObservable.
+ * 
+ * @since 1.1
+ */
+public class AbstractObservableTest extends AbstractDefaultRealmTestCase {
+	private ObservableStub observable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		observable = new ObservableStub(Realm.getDefault());
+	}
+
+	public void testStaleListener() throws Exception {
+		assertFalse(observable.hasListeners());
+
+		StaleEventTracker listener1 = new StaleEventTracker();
+
+		assertFalse(observable.firstListenerAdded);
+		observable.addStaleListener(listener1);
+		assertTrue(observable.firstListenerAdded);
+		observable.firstListenerAdded = false; // reset
+
+		assertTrue(observable.hasListeners());
+		assertEquals(0, listener1.count);
+
+		observable.fireStale();
+
+		assertEquals(1, listener1.count);
+		assertSame(observable, listener1.event.getObservable());
+
+		// Add a second stale listener as 1 vs. 2 listener code is different.
+		StaleEventTracker listener2 = new StaleEventTracker();
+		assertEquals(0, listener2.count);
+		observable.addStaleListener(listener2);
+		observable.fireStale();
+
+		assertEquals(2, listener1.count);
+		assertEquals(1, listener2.count);
+
+		// Add a third stale listener as 2 vs. 3 or greater code is different.
+		StaleEventTracker listener3 = new StaleEventTracker();
+		observable.addStaleListener(listener3);
+		assertEquals(0, listener3.count);
+
+		observable.fireStale();
+
+		assertEquals(3, listener1.count);
+		assertEquals(2, listener2.count);
+		assertEquals(1, listener3.count);
+
+		assertFalse(observable.lastListenerRemoved);
+		observable.removeStaleListener(listener1);
+		observable.removeStaleListener(listener2);
+		observable.removeStaleListener(listener3);
+		assertTrue(observable.lastListenerRemoved);
+
+		assertFalse(observable.hasListeners());
+	}
+
+	public void testChangeListener() throws Exception {
+		assertFalse(observable.hasListeners());
+
+		ChangeEventTracker listener1 = new ChangeEventTracker();
+
+		assertFalse(observable.firstListenerAdded);
+		observable.addChangeListener(listener1);
+		assertTrue(observable.firstListenerAdded);
+		observable.firstListenerAdded = false;
+
+		assertTrue(observable.hasListeners());
+		assertEquals(0, listener1.count);
+
+		observable.fireChange();
+
+		assertEquals(1, listener1.count);
+		assertSame(observable, listener1.event.getSource());
+
+		// Add a second listener as the 1 vs. 2 listener code is different.
+		ChangeEventTracker listener2 = new ChangeEventTracker();
+		observable.addChangeListener(listener2);
+		assertEquals(0, listener2.count);
+
+		observable.fireChange();
+		assertEquals(2, listener1.count);
+		assertEquals(1, listener2.count);
+
+		// Add a third listener as the 2 vs. 3 or greater code is different.
+		ChangeEventTracker listener3 = new ChangeEventTracker();
+		observable.addChangeListener(listener3);
+		assertEquals(0, listener3.count);
+
+		observable.fireChange();
+
+		assertEquals(3, listener1.count);
+		assertEquals(2, listener2.count);
+		assertEquals(1, listener3.count);
+
+		assertFalse(observable.lastListenerRemoved);
+		observable.removeChangeListener(listener1);
+		observable.removeChangeListener(listener2);
+		observable.removeChangeListener(listener3);
+		assertTrue(observable.lastListenerRemoved);
+
+		assertFalse(observable.hasListeners());
+	}
+
+	public void testHasListenersWithChangeAndStaleListeners() throws Exception {
+		ChangeEventTracker changeListener = new ChangeEventTracker();
+		StaleEventTracker staleListener = new StaleEventTracker();
+
+		assertFalse(observable.hasListeners());
+		assertFalse(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		observable.addChangeListener(changeListener);
+		assertTrue(observable.hasListeners());
+		assertTrue(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		// reset
+		observable.firstListenerAdded = false;
+		observable.lastListenerRemoved = false;
+
+		observable.addStaleListener(staleListener);
+		assertTrue(observable.hasListeners());
+		assertFalse(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		observable.removeChangeListener(changeListener);
+		assertTrue(observable.hasListeners());
+		assertFalse(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		observable.removeStaleListener(staleListener);
+		assertFalse(observable.hasListeners());
+		assertFalse(observable.firstListenerAdded);
+		assertTrue(observable.lastListenerRemoved);
+	}
+
+	public void testFireStaleRealmChecks() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				observable = new ObservableStub();
+				observable.fireStale();
+			}
+		});
+	}
+
+	public void testFireChangeRealmChecks() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				observable = new ObservableStub();
+				observable.fireChange();
+			}
+		});
+	}
+
+	public void testAddDisposeListener_HasListenersFalse() {
+		IDisposeListener disposeListener = new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+			}
+		};
+		IStaleListener staleListener = new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+			}
+		};
+
+		assertFalse(observable.hasListeners());
+
+		observable.addDisposeListener(disposeListener);
+		assertFalse(observable.hasListeners());
+		assertFalse(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		observable.addStaleListener(staleListener);
+		assertTrue(observable.hasListeners());
+		assertTrue(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		observable.removeDisposeListener(disposeListener);
+		assertTrue(observable.hasListeners());
+		assertTrue(observable.firstListenerAdded);
+		assertFalse(observable.lastListenerRemoved);
+
+		observable.removeStaleListener(staleListener);
+		assertFalse(observable.hasListeners());
+		assertTrue(observable.firstListenerAdded);
+		assertTrue(observable.lastListenerRemoved);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AbstractObservableTest.class.getName());
+		suite.addTestSuite(AbstractObservableTest.class);
+		Delegate delegate = new Delegate();
+		suite.addTest(ObservableContractTest.suite(delegate));
+		suite.addTest(ObservableStaleContractTest.suite(delegate));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableContractDelegate {
+
+		public void change(IObservable observable) {
+			((ObservableStub) observable).fireChange();
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			((ObservableStub) observable).setStale(stale);
+		}
+
+		public IObservable createObservable(Realm realm) {
+			return new ObservableStub(realm);
+		}
+	}
+
+	private static class ObservableStub extends AbstractObservable {
+		private boolean stale;
+
+		public ObservableStub() {
+			this(Realm.getDefault());
+		}
+
+		/**
+		 * @param realm
+		 */
+		public ObservableStub(Realm realm) {
+			super(realm);
+		}
+
+		private boolean firstListenerAdded;
+
+		private boolean lastListenerRemoved;
+
+		protected void fireStale() {
+			super.fireStale();
+		}
+
+		protected void fireChange() {
+			super.fireChange();
+		}
+
+		public boolean isStale() {
+			getterCalled();
+			return stale;
+		}
+
+		private void getterCalled() {
+			ObservableTracker.getterCalled(this);
+		}
+
+		public void setStale(boolean stale) {
+			boolean old = this.stale;
+			this.stale = stale;
+
+			if (stale && !old) {
+				fireStale();
+			}
+		}
+
+		protected boolean hasListeners() {
+			return super.hasListeners();
+		}
+
+		protected void firstListenerAdded() {
+			firstListenerAdded = true;
+		}
+
+		protected void lastListenerRemoved() {
+			lastListenerRemoved = true;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ChangeSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ChangeSupportTest.java
new file mode 100644
index 0000000..ad82274
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ChangeSupportTest.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 255734)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.ChangeSupport;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.IStaleListener;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.StaleEvent;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ChangeSupportTest extends AbstractDefaultRealmTestCase {
+	private ChangeSupportStub changeSupport;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		changeSupport = new ChangeSupportStub(Realm.getDefault());
+	}
+
+	public void testAddDisposeListener_HasListenersFalse() {
+		IDisposeListener disposeListener = new IDisposeListener() {
+			public void handleDispose(DisposeEvent staleEvent) {
+			}
+		};
+		IStaleListener staleListener = new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+			}
+		};
+
+		assertFalse(changeSupport.hasListeners());
+
+		changeSupport.addDisposeListener(disposeListener);
+		assertFalse(changeSupport.hasListeners());
+		assertEquals(Collections.EMPTY_LIST, changeSupport.log);
+
+		changeSupport.addStaleListener(staleListener);
+		assertTrue(changeSupport.hasListeners());
+		assertEquals(Collections.singletonList(ADD_FIRST), changeSupport.log);
+
+		changeSupport.removeDisposeListener(disposeListener);
+		assertTrue(changeSupport.hasListeners());
+		assertEquals(Collections.singletonList(ADD_FIRST), changeSupport.log);
+
+		changeSupport.removeStaleListener(staleListener);
+		assertFalse(changeSupport.hasListeners());
+		assertEquals(Arrays.asList(new Object[] { ADD_FIRST, REMOVE_LAST }),
+				changeSupport.log);
+	}
+
+	private static final String ADD_FIRST = "firstListenerAdded";
+	private static final String REMOVE_LAST = "lastListenerRemoved";
+
+	private static class ChangeSupportStub extends ChangeSupport {
+		List log = new ArrayList();
+
+		ChangeSupportStub(Realm realm) {
+			super(realm);
+		}
+
+		protected void firstListenerAdded() {
+			log.add(ADD_FIRST);
+		}
+
+		protected void lastListenerRemoved() {
+			log.add(REMOVE_LAST);
+		}
+
+		protected boolean hasListeners() {
+			return super.hasListeners();
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/DecoratingObservableTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/DecoratingObservableTest.java
new file mode 100644
index 0000000..ce631db
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/DecoratingObservableTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 255734)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.DecoratingObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DecoratingObservableTest extends AbstractDefaultRealmTestCase {
+	private IObservable decorated;
+	private DecoratingObservable decorator;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		decorated = new ObservableStub(Realm.getDefault());
+		decorator = new DecoratingObservable(decorated, false);
+	}
+
+	public void testDisposeDecorated_DisposesDecorator() {
+		DisposeEventTracker tracker = DisposeEventTracker.observe(decorator);
+		assertFalse(decorator.isDisposed());
+		decorated.dispose();
+		assertEquals(1, tracker.count);
+		assertTrue(decorator.isDisposed());
+	}
+
+	static class ObservableStub extends AbstractObservable {
+		public ObservableStub(Realm realm) {
+			super(realm);
+		}
+
+		public boolean isStale() {
+			return false;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/DiffsTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/DiffsTest.java
new file mode 100755
index 0000000..551acdd
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/DiffsTest.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+
+/**
+ * @since 3.2
+ *
+ */
+public class DiffsTest extends TestCase {
+	/**
+	 * Asserts that the {@link SetDiff#toString()} implementation doesn't throw a NPE if any of its properties are <code>null</code>.
+	 */
+	public void test_SetDiff() {
+		SetDiff diff = new SetDiff() {
+			public Set getAdditions() {
+				return null;
+			}
+
+			public Set getRemovals() {
+				return null;
+			}
+		};
+		
+		try {
+			diff.toString();
+			assertTrue(true);
+		} catch (NullPointerException e) {
+			fail("NPE was thrown.");
+		}
+	}
+
+	/**
+	 * Asserts that if the {@link ValueDiff#toString()} implementation doesn't throw a NPE if any of its properties are <code>null</code>.
+	 *
+	 */
+	public void test_ValueDiff() {
+		ValueDiff diff = new ValueDiff() {
+			public Object getNewValue() {
+				return null;
+			}
+
+			public Object getOldValue() {
+				return null;
+			}
+		};
+		
+		try {
+			diff.toString();
+			assertTrue(true);
+		} catch (NullPointerException e) {
+			fail("NPE was thrown.");
+		}
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/Diffs_ListDiffTests.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/Diffs_ListDiffTests.java
new file mode 100644
index 0000000..5cd9aff
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/Diffs_ListDiffTests.java
@@ -0,0 +1,282 @@
+/*******************************************************************************
+ * 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 226216
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+
+/**
+ * @since 1.1
+ */
+public class Diffs_ListDiffTests extends TestCase {
+	public void testListDiffEntryToStringDoesNotThrowNPEForNullListDiffEntry() {
+		ListDiffEntry entry = new ListDiffEntry() {
+			public Object getElement() {
+				return null;
+			}
+
+			public int getPosition() {
+				return 0;
+			}
+
+			public boolean isAddition() {
+				return false;
+			}
+		};
+
+		try {
+			entry.toString();
+			assertTrue(true);
+		} catch (NullPointerException e) {
+			fail("NPE was thrown.");
+		}
+	}
+
+	public void testListDiffToStringDoesNotThrowNPEForNullListDiff() {
+		ListDiff diff = new ListDiff() {
+			public ListDiffEntry[] getDifferences() {
+				return null;
+			}
+		};
+
+		try {
+			diff.toString();
+			assertTrue(true);
+		} catch (NullPointerException e) {
+			fail("NPE was thrown.");
+		}
+	}
+
+	public void testListDiffToStringDoesNotThrowNPEForNullListDiffEntry() {
+		ListDiff diff = new ListDiff() {
+			public ListDiffEntry[] getDifferences() {
+				return new ListDiffEntry[1];
+			}
+		};
+
+		try {
+			diff.toString();
+			assertTrue(true);
+		} catch (NullPointerException e) {
+			fail("NPE was thrown.");
+		}
+	}
+
+	public void testDiffScenario1() throws Exception {
+		ListDiff diff = diff(null, null);
+		assertEquals(0, diff.getDifferences().length);
+	}
+	
+	private ListDiff diff(String[] oldArray, String[] newArray) {
+		List a = Arrays.asList((oldArray != null) ? oldArray : new String[] {});
+		List b = Arrays.asList((newArray != null) ? newArray : new String[] {});
+		
+		return Diffs.computeListDiff(a, b);
+	}
+	
+	public void testDiffScenario2() throws Exception {
+		ListDiff diff = diff(new String[] {"a"}, null);
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], false, 0, "a");
+	}
+	
+	public void testDiffScenario3() throws Exception {
+		ListDiff diff = diff(null, new String[] {"a"});
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 0, "a");
+	}
+	
+	public void testDiffScenario4() throws Exception {
+		ListDiff diff = diff(new String[] {"a"}, new String[] {"a"});
+		
+		assertEquals(0, diff.getDifferences().length);
+	}
+	
+	public void testDiffScenario5() throws Exception {
+		ListDiff diff = diff(new String[] {"a"}, new String[] {"b"});
+		assertEquals(2, diff.getDifferences().length);
+		
+		assertEntry(diff.getDifferences()[0], true, 0, "b");
+		assertEntry(diff.getDifferences()[1], false, 1, "a");
+	}
+	
+	public void testDiffScenario6() throws Exception {
+		ListDiff diff = diff(new String[] { "a" }, new String[] { "a", "b" });
+		
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 1, "b");
+	}
+	
+	public void testDiffScenario7() throws Exception {
+		ListDiff diff = diff(new String[] { "a" }, new String[] { "b", "a" });
+		
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 0, "b");
+	}
+	
+	public void testDiffScenario8() throws Exception {
+		ListDiff diff = diff(new String[] { "a" }, new String[] { "b", "b" });
+		
+		assertEquals(3, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 0, "b");
+		assertEntry(diff.getDifferences()[1], true, 1, "b");
+		assertEntry(diff.getDifferences()[2], false, 2, "a");
+	}
+	
+	public void testDiffScenario9() throws Exception {
+		ListDiff diff = diff(new String[] { "a" }, new String[] { "a", "b", "c" });
+		
+		assertEquals(2, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 1, "b");
+		assertEntry(diff.getDifferences()[1], true, 2, "c");
+	}
+	
+	public void testDiffScenario10() throws Exception {
+		ListDiff diff = diff(new String[] { "b" }, new String[] { "a", "b", "c" });
+		
+		assertEquals(2, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 0, "a");
+		assertEntry(diff.getDifferences()[1], true, 2, "c");
+	}
+	
+	public void testDiffScenario11() throws Exception {
+		ListDiff diff = diff(new String[] { "c" }, new String[] { "a", "b", "c" });
+		
+		assertEquals(2, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], true, 0, "a");
+		assertEntry(diff.getDifferences()[1], true, 1, "b");		
+	}
+	
+	public void testDiffScenario12() throws Exception {
+		ListDiff diff = diff(new String[] { "a", "b", "c" }, new String[] { "a", "b", "c" });
+		
+		assertEquals(0, diff.getDifferences().length);
+	}
+	
+	public void testDiffScenario13() throws Exception {
+		ListDiff diff = diff(new String[] { "a", "b", "c" }, new String[] { "b", "c" });
+		
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], false, 0, "a");
+	}
+	
+	public void testDiffScenarios14() throws Exception {
+		ListDiff diff = diff(new String[] { "a", "b", "c" }, new String[] { "a", "c" });
+		
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], false, 1, "b");
+	}
+	
+	public void testDiffScenarios15() throws Exception {
+		ListDiff diff = diff(new String[] { "a", "b", "c" }, new String[] { "a", "b" });
+		
+		assertEquals(1, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], false, 2, "c");
+	}
+	
+	public void testDiffScenarios16() throws Exception {
+		ListDiff diff = diff(new String[] { "a", "b", "c" }, new String[] { "c", "b", "a" });
+		
+		assertEquals(4, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], false, 2, "c");
+		assertEntry(diff.getDifferences()[1], true, 0, "c");
+		assertEntry(diff.getDifferences()[2], false, 2, "b");
+		assertEntry(diff.getDifferences()[3], true, 1, "b");
+	}
+	
+	public void testDiffScenarios17() throws Exception {
+		ListDiff diff = diff(new String[] { "a", "b", "c" }, new String[] { "c", "b" });
+		
+		assertEquals(3, diff.getDifferences().length);
+		assertEntry(diff.getDifferences()[0], false, 0, "a");
+		assertEntry(diff.getDifferences()[1], false, 1, "c");
+		assertEntry(diff.getDifferences()[2], true, 0, "c");
+	}
+	
+	private static void assertEntry(ListDiffEntry entry, boolean addition, int position, String element) {
+		assertEquals("addition", addition, entry.isAddition());
+		assertEquals("position", position, entry.getPosition());
+		assertEquals("element", element, entry.getElement());
+	}
+
+	public void testComputeListDiff_SingleInsert() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "c" }), Arrays
+				.asList(new Object[] { "a", "b", "c" }));
+	}
+
+	public void testComputeListDiff_SingleAppend() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b" }), Arrays
+				.asList(new Object[] { "a", "b", "c" }));
+	}
+
+	public void testComputeListDiff_SingleRemove() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b", "c" }),
+				Arrays.asList(new Object[] { "a", "b" }));
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b", "c" }),
+				Arrays.asList(new Object[] { "a", "c" }));
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b", "c" }),
+				Arrays.asList(new Object[] { "b", "c" }));
+	}
+
+	public void testComputeListDiff_MoveDown1() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b" }), Arrays
+				.asList(new Object[] { "b", "a" }));
+	}
+
+	public void testComputeListDiff_MoveDown2() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b", "c" }),
+				Arrays.asList(new Object[] { "b", "c", "a" }));
+	}
+
+	public void testComputeListDiff_MoveUp1() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b" }), Arrays
+				.asList(new Object[] { "b", "a" }));
+	}
+
+	public void testComputeListDiff_MoveUp2() {
+		checkComputedListDiff(Arrays.asList(new Object[] { "a", "b", "c" }),
+				Arrays.asList(new Object[] { "c", "a", "b" }));
+	}
+
+	private static void checkComputedListDiff(List oldList, List newList) {
+		ListDiff diff = Diffs.computeListDiff(oldList, newList);
+
+		final List list = new ArrayList(oldList);
+		diff.accept(new ListDiffVisitor() {
+			public void handleAdd(int index, Object element) {
+				list.add(index, element);
+			}
+
+			public void handleRemove(int index, Object element) {
+				assertEquals(element, list.remove(index));
+			}
+
+			public void handleReplace(int index, Object oldElement,
+					Object newElement) {
+				assertEquals(oldElement, list.set(index, newElement));
+			}
+		});
+
+		assertEquals(
+				"Applying diff to old list should make it equal to new list",
+				newList, list);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/LockRealm.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/LockRealm.java
new file mode 100755
index 0000000..768fb33
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/LockRealm.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.LinkedList;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ILock;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class LockRealm extends Realm {
+
+	private LinkedList queue;
+	private ILock lock;
+	private Job job;
+	private boolean lockAcquired;
+
+	public LockRealm() {
+		queue = new LinkedList();
+		lock = Job.getJobManager().newLock();
+		job = new Job("Lock Realm Job") {
+			protected IStatus run(IProgressMonitor monitor) {
+				for (Runnable runnable; (runnable = dequeue()) != null;) {
+					acquireLock();
+					try {
+						safeRun(runnable);
+					} finally {
+						releaseLock();
+					}
+				}
+				return Status.OK_STATUS;
+			}
+		};
+		job.setSystem(true);
+	}
+
+	protected void syncExec(Runnable runnable) {
+		acquireLock();
+		try {
+			safeRun(runnable);
+		} finally {
+			releaseLock();
+		}
+	}
+
+	public void asyncExec(Runnable runnable) {
+		enqueue(runnable);
+		job.schedule();
+	}
+
+	/**
+	 * @param runnable
+	 */
+	private void enqueue(Runnable runnable) {
+		synchronized (queue) {
+			queue.addLast(runnable);
+		}
+	}
+
+	private Runnable dequeue() {
+		synchronized (queue) {
+			if (queue.isEmpty()) {
+				return null;
+			}
+			return (Runnable) queue.getFirst();
+		}
+	}
+
+	public boolean isCurrent() {
+		return lockAcquired;
+	}
+
+	private void acquireLock() {
+		lock.acquire();
+		lockAcquired = true;
+	}
+
+	private void releaseLock() {
+		lockAcquired = false;
+		lock.release();
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ObservableTrackerTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ObservableTrackerTest.java
new file mode 100644
index 0000000..1a411fc
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ObservableTrackerTest.java
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 210115)
+ *     Matthew Hall - bugs 249526, 251424
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.util.ILogger;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.IdentitySet;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ObservableTrackerTest extends AbstractDefaultRealmTestCase {
+	public void testRunAndMonitor_GetterCalled() throws Exception {
+		final IObservable observable = new ObservableStub();
+		IObservable[] result = ObservableTracker.runAndMonitor(new Runnable() {
+			public void run() {
+				ObservableTracker.getterCalled(observable);
+			}
+		}, null, null);
+		assertEquals(1, result.length);
+		assertSame(observable, result[0]);
+	}
+
+	public void testGetterCalled_ObservableDisposed() throws Exception {
+		try {
+			IObservable observable = new ObservableStub();
+			observable.dispose();
+
+			ObservableTracker.getterCalled(observable);
+
+			fail("expected AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testGetterCalled_ObservableRealmNotCurrent() throws Exception {
+		try {
+			IObservable observable = new ObservableStub(new CurrentRealm(false));
+
+			ObservableTracker.getterCalled(observable);
+
+			fail("expected AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testRunAndCollect() throws Exception {
+		final IObservable[] created = new IObservable[1];
+		IObservable[] collected = ObservableTracker
+				.runAndCollect(new Runnable() {
+					public void run() {
+						created[0] = new ObservableStub();
+					}
+				});
+		assertEquals(1, collected.length);
+		assertSame(created[0], collected[0]);
+	}
+
+	public void testRunAndIgnore_RunAndMonitor() throws Exception {
+		final IObservable observable = new ObservableStub();
+		IObservable[] result = ObservableTracker.runAndMonitor(new Runnable() {
+			public void run() {
+				ObservableTracker.runAndIgnore(new Runnable() {
+					public void run() {
+						ObservableTracker.getterCalled(observable);
+					}
+				});
+			}
+		}, null, null);
+		assertEquals(0, result.length);
+	}
+
+	public void testRunAndIgnore_RunAndCollect() throws Exception {
+		IObservable[] result = ObservableTracker.runAndCollect(new Runnable() {
+			public void run() {
+				ObservableTracker.runAndIgnore(new Runnable() {
+					public void run() {
+						new ObservableStub();
+					}
+				});
+			}
+		});
+		assertEquals(0, result.length);
+	}
+
+	public void testSetIgnore_RunAndMonitor() throws Exception {
+		final IObservable observable = new ObservableStub();
+		IObservable[] result = ObservableTracker.runAndMonitor(new Runnable() {
+			public void run() {
+				ObservableTracker.setIgnore(true);
+				ObservableTracker.getterCalled(observable);
+				ObservableTracker.setIgnore(false);
+			}
+		}, null, null);
+		assertEquals(0, result.length);
+	}
+
+	public void testSetIgnore_RunAndCollect() throws Exception {
+		IObservable[] result = ObservableTracker.runAndCollect(new Runnable() {
+			public void run() {
+				ObservableTracker.setIgnore(true);
+				new ObservableStub();
+				ObservableTracker.setIgnore(false);
+			}
+		});
+		assertEquals(0, result.length);
+	}
+
+	public void testSetIgnore_Nested_RunAndCollect() throws Exception {
+		final List list = new ArrayList();
+
+		Set collected = new IdentitySet(Arrays.asList(ObservableTracker
+				.runAndCollect(new Runnable() {
+					public void run() {
+						list.add(new ObservableStub()); // list[0] collected
+						ObservableTracker.setIgnore(true);
+						list.add(new ObservableStub()); // list[1] ignored
+						ObservableTracker.setIgnore(true);
+						list.add(new ObservableStub()); // list[2] ignored
+						ObservableTracker.setIgnore(false);
+						list.add(new ObservableStub()); // list[3] ignored
+						ObservableTracker.setIgnore(false);
+						list.add(new ObservableStub()); // list[4] collected
+					}
+				})));
+
+		// Have to compare result in identity set because ObservableTracker may
+		// not return them in the same order they were collected
+		Set expected = new IdentitySet();
+		expected.add(list.get(0));
+		expected.add(list.get(4));
+		assertEquals(expected, collected);
+	}
+
+	public void testSetIgnore_Nested_RunAndMonitor() throws Exception {
+		final IObservable[] observables = { new ObservableStub(),
+				new ObservableStub(), new ObservableStub(),
+				new ObservableStub(), new ObservableStub() };
+
+		Set result = new IdentitySet(Arrays.asList(ObservableTracker
+				.runAndMonitor(new Runnable() {
+					public void run() {
+						ObservableTracker.getterCalled(observables[0]); // monitored
+						ObservableTracker.setIgnore(true);
+						ObservableTracker.getterCalled(observables[1]); // ignored
+						ObservableTracker.setIgnore(true);
+						ObservableTracker.getterCalled(observables[2]); // ignored
+						ObservableTracker.setIgnore(false);
+						ObservableTracker.getterCalled(observables[3]); // ignored
+						ObservableTracker.setIgnore(false);
+						ObservableTracker.getterCalled(observables[4]); // monitored
+					}
+				}, null, null)));
+
+		// Have to compare result in identity set because ObservableTracker may
+		// not return them in the same order they were monitored
+		Set expected = new IdentitySet();
+		expected.add(observables[0]);
+		expected.add(observables[4]);
+		assertEquals(expected, result);
+	}
+
+	public void testSetIgnore_RunAndMonitor_UnmatchedIgnore_LogsError() {
+		final List log = new ArrayList();
+		Policy.setLog(new ILogger() {
+			public void log(IStatus status) {
+				log.add(status);
+			}
+		});
+
+		ObservableTracker.runAndMonitor(new Runnable() {
+			public void run() {
+				ObservableTracker.setIgnore(true);
+				// do not call call setIgnore(false)
+			}
+		}, null, null);
+
+		assertEquals(1, log.size());
+		IStatus status = (IStatus) log.get(0);
+		assertEquals(IStatus.ERROR, status.getSeverity());
+		assertTrue(status.getMessage().indexOf("setIgnore") != -1);
+	}
+
+	public void testSetIgnore_RunAndCollect_UnmatchedIgnore_LogsError() {
+		final List log = new ArrayList();
+		Policy.setLog(new ILogger() {
+			public void log(IStatus status) {
+				log.add(status);
+			}
+		});
+
+		ObservableTracker.runAndCollect(new Runnable() {
+			public void run() {
+				ObservableTracker.setIgnore(true);
+				// do not call call setIgnore(false)
+			}
+		});
+
+		assertEquals(1, log.size());
+		IStatus status = (IStatus) log.get(0);
+		assertEquals(IStatus.ERROR, status.getSeverity());
+		assertTrue(status.getMessage().indexOf("setIgnore") != -1);
+	}
+
+	public void testSetIgnore_UnmatchedUnignore() {
+		try {
+			ObservableTracker.setIgnore(false);
+			fail("Expected IllegalStateException");
+		} catch (IllegalStateException expected) {
+		}
+	}
+
+	public static class ObservableStub extends AbstractObservable {
+		public ObservableStub() {
+			this(Realm.getDefault());
+		}
+
+		public ObservableStub(Realm realm) {
+			super(realm);
+		}
+
+		public boolean isStale() {
+			return false;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ObservablesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ObservablesTest.java
new file mode 100755
index 0000000..819a15d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ObservablesTest.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ObservableList;
+import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableList;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ObservablesTest extends AbstractDefaultRealmTestCase {	
+	public void testUnmodifableObservableListExceptions() throws Exception {
+		try {
+			Observables.unmodifiableObservableList(null);
+			fail("IllegalArgumentException should have been thrown.");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+	
+	public void testUnmodifiableObservableList() throws Exception {
+		IObservableList unmodifiable = Observables.unmodifiableObservableList(new ObservableListStub(new ArrayList(0), String.class));
+		assertTrue(unmodifiable instanceof UnmodifiableObservableList);
+	}
+	
+	private static class ObservableListStub extends ObservableList {
+		/**
+		 * @param wrappedList
+		 * @param elementType
+		 */
+		protected ObservableListStub(List wrappedList, Object elementType) {
+			super(wrappedList, elementType);
+		}		
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/RealmTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/RealmTest.java
new file mode 100644
index 0000000..17d473f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/RealmTest.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ */
+public class RealmTest extends TestCase {
+	public void testSetDefaultWithRunnable() throws Exception {
+		Realm oldRealm = new CurrentRealm(true);
+		final Realm newRealm = new CurrentRealm(true);
+		
+		RealmTester.setDefault(oldRealm);
+		Realm.runWithDefault(newRealm, new Runnable() {
+			public void run() {
+				assertEquals("new realm should be default", newRealm, Realm.getDefault());
+			}
+		});
+		
+		assertEquals("old realm should have been restored", oldRealm, Realm.getDefault());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ThreadRealm.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ThreadRealm.java
new file mode 100755
index 0000000..ba95804
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/ThreadRealm.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 118516, 281723, 286533
+ *******************************************************************************/
+package org.eclipse.core.tests.databinding.observable;
+
+import java.util.LinkedList;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * {@link Realm} that enforces execution to be within a specific
+ * {@link Thread}.
+ * 
+ * @since 3.2
+ */
+public class ThreadRealm extends Realm {
+    private Thread thread;
+
+    private final LinkedList queue = new LinkedList();
+
+    private volatile boolean block;
+
+    /**
+     * Initializes the realm.
+     * 
+     * @param thread
+     */
+    public synchronized void init(Thread thread) {
+        if (thread == null) {
+            throw new IllegalArgumentException("Parameter thread was null."); //$NON-NLS-1$
+        }
+        Assert.isTrue(this.thread == null, "Realm can only be initialized once.");
+
+        this.thread = thread;
+    }
+
+    /**
+     * @return <code>true</code> if the current thread is the thread for
+     *         the realm
+     */
+    public boolean isCurrent() {
+        return Thread.currentThread() == thread;
+    }
+
+    /**
+     * @return thread, <code>null</code> if not
+     *         {@link #init(Thread) initialized}
+     */
+    public Thread getThread() {
+        return thread;
+    }
+
+    /**
+     * Queues the provided <code>runnable</code>.
+     * 
+     * @param runnable
+     */
+    public void asyncExec(Runnable runnable) {
+        synchronized (queue) {
+            queue.add(runnable);
+            queue.notifyAll();
+        }
+    }
+    
+    /**
+     * Returns after the realm has completed all runnables currently on its
+     * queue.  Do not call from the realm's thread.
+     * 
+     * @throws IllegalStateException
+     *             if the ThreadRealm is not blocking on its thread.
+     * @throws IllegalStateException
+     *             if invoked from the realm's own thread.
+     */
+    public void processQueue() {
+        if (Thread.currentThread() == thread) {
+            throw new IllegalStateException(
+                    "Cannot execute this method in the realm's own thread");
+        }
+
+        try {
+            synchronized (queue) {
+                while (!queue.isEmpty()) {
+                    if (!block)
+                        throw new IllegalStateException(
+                                "Cannot process queue, ThreadRealm is not blocking on its thread");
+                    queue.wait();
+                }
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    public boolean isBlocking() {
+        return block;
+    }
+
+    /**
+     * Blocks the current thread invoking runnables.
+     */
+    public void block() {
+        if (block) {
+            throw new IllegalStateException("Realm is already blocking.");
+        }
+        
+        if (Thread.currentThread() != thread) {
+            throw new IllegalStateException("The current thread is not the correct thread.");
+        }
+        
+        try {
+            block = true;
+
+            synchronized (queue) {
+                queue.notifyAll(); // so waitUntilBlocking can return
+            }
+
+            while (block) {
+                Runnable runnable = null;
+                synchronized (queue) {
+                    if (queue.isEmpty()) {
+                        queue.wait();
+                    } else {
+                        runnable = (Runnable) queue.getFirst();
+                    }
+                }
+
+                if (runnable != null) {
+                    safeRun(runnable);
+                    synchronized (queue) {
+                        // Don't remove the runnable from the queue until after
+                        // it has run, or else processQueue() may return before
+                        // the last runnable has finished
+                        queue.removeFirst();
+                        queue.notifyAll();
+                    }
+                }
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } finally {
+            block = false;
+        }
+    }
+
+    /**
+     * Unblocks the thread.
+     */
+    public void unblock() {
+        block = false;
+
+        // Awaken the thread if waiting.
+        synchronized (queue) {
+            queue.notifyAll();
+        }
+    }
+
+    /**
+     * Blocks until the ThreadRealm is blocking on its own thread.
+     */
+    public void waitUntilBlocking() {
+        if (Thread.currentThread() == thread) {
+            throw new IllegalStateException(
+                    "Cannot execute this method in the realm's own thread");
+        }
+
+        while (!block) {
+            synchronized (queue) {
+                try {
+                    queue.wait();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/AbstractObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/AbstractObservableListTest.java
new file mode 100755
index 0000000..fde28d4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/AbstractObservableListTest.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 167204
+ *     Matthew Hall - bugs 208858, 213145, 247367
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+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.IObservableCollection;
+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.databinding.observable.list.AbstractObservableList;
+import org.eclipse.core.databinding.observable.list.IListChangeListener;
+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.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ */
+public class AbstractObservableListTest extends TestCase {
+	private AbstractObservableListStub list;
+
+	protected void setUp() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+		list = new AbstractObservableListStub();
+	}
+
+	protected void tearDown() throws Exception {
+		RealmTester.setDefault(null);
+	}
+
+	public void testFireChangeRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.fireChange();
+			}
+		});
+	}
+
+	public void testFireStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.fireStale();
+			}
+		});
+	}
+
+	public void testFireListChangeRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.fireListChange(null);
+			}
+		});
+	}
+
+	public void testMove_FiresListChanges() throws Exception {
+		list = new MutableObservableListStub();
+		final Object element = new Object();
+		list.add(element);
+		list.add(new Object());
+
+		final List diffEntries = new ArrayList();
+		list.addListChangeListener(new IListChangeListener() {
+			public void handleListChange(ListChangeEvent event) {
+				diffEntries.addAll(Arrays.asList(event.diff.getDifferences()));
+			}
+		});
+
+		list.move(0, 1);
+
+		assertEquals(2, diffEntries.size());
+
+		ListDiffEntry entry = (ListDiffEntry) diffEntries.get(0);
+		assertEquals(element, entry.getElement());
+		assertEquals(false, entry.isAddition());
+		assertEquals(0, entry.getPosition());
+
+		entry = (ListDiffEntry) diffEntries.get(1);
+		assertEquals(element, entry.getElement());
+		assertEquals(true, entry.isAddition());
+		assertEquals(1, entry.getPosition());
+	}
+
+	public void testMove_MovesElement() throws Exception {
+		list = new MutableObservableListStub();
+		final Object element0 = new Object();
+		final Object element1 = new Object();
+		list.add(element0);
+		list.add(element1);
+
+		list.move(0, 1);
+
+		assertEquals(element1, list.get(0));
+		assertEquals(element0, list.get(1));
+	}
+
+	public void testAddListChangeListener_AfterDispose() {
+		list.dispose();
+		list.addListChangeListener(new IListChangeListener() {
+			public void handleListChange(ListChangeEvent event) {
+				// do nothing
+			}
+		});
+	}
+
+	public void testRemoveListChangeListener_AfterDispose() {
+		list.dispose();
+		list.removeListChangeListener(new IListChangeListener() {
+			public void handleListChange(ListChangeEvent event) {
+				// do nothing
+			}
+		});
+	}
+
+	public void testAddChangeListener_AfterDispose() {
+		list.dispose();
+		list.addChangeListener(new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				// do nothing
+			}
+		});
+	}
+
+	public void testRemoveChangeListener_AfterDispose() {
+		list.dispose();
+		list.removeChangeListener(new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				// do nothing
+			}
+		});
+	}
+
+	public void testAddStaleListener_AfterDispose() {
+		list.dispose();
+		list.addStaleListener(new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+				// do nothing
+			}
+		});
+	}
+
+	public void testRemoveStaleListener_AfterDispose() {
+		list.dispose();
+		list.removeStaleListener(new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+				// do nothing
+			}
+		});
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AbstractObservableListTest.class.getName());
+		suite.addTestSuite(AbstractObservableListTest.class);
+		suite.addTest(ObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				final int itemCount) {
+
+			String[] items = new String[itemCount];
+			for (int i = 0; i < itemCount; i++) {
+				items[i] = String.valueOf(i);
+			}
+
+			AbstractObservableListStub observable = new AbstractObservableListStub(realm, Arrays.asList(items));
+			observable.elementType = String.class;
+			return observable;
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			((AbstractObservableListStub) observable).fireChange();
+		}
+	}
+
+	static class AbstractObservableListStub extends AbstractObservableList {
+		Object elementType;
+
+		List wrappedList;
+
+		public AbstractObservableListStub() {
+			super();
+			wrappedList = new ArrayList();
+		}
+
+		public AbstractObservableListStub(Realm realm, List list) {
+			super(realm);
+			this.wrappedList = list;
+		}
+
+		protected int doGetSize() {
+			return wrappedList.size();
+		}
+
+		public Object get(int index) {
+			ObservableTracker.getterCalled(this);
+			return wrappedList.get(index);
+		}
+
+		public Object getElementType() {
+			return elementType;
+		}
+
+		protected void fireChange() {
+			super.fireChange();
+		}
+
+		protected void fireStale() {
+			super.fireStale();
+		}
+
+		protected void fireListChange(ListDiff diff) {
+			super.fireListChange(diff);
+		}
+	}
+
+	static class MutableObservableListStub extends AbstractObservableListStub {
+		// These methods are present so we can test AbstractObservableList.move()
+
+		public void add(int index, Object element) {
+			checkRealm();
+			wrappedList.add(index, element);
+			fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(
+					index, true, element)));
+		}
+
+		public Object remove(int index) {
+			checkRealm();
+			Object element = wrappedList.remove(index);
+			fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(
+					index, false, element)));
+			return element;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java
new file mode 100644
index 0000000..e0a7d68
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * 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 211786)
+ *     Matthew Hall - bug 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+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.databinding.observable.list.ComputedList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ComputedListTest extends AbstractDefaultRealmTestCase {
+	ComputedListStub list;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		list = new ComputedListStub();
+		list.size(); // Force list to compute
+	}
+
+	public void testDependency_Staleness() {
+		assertFalse(list.isStale());
+		list.dependency.fireStale();
+		assertTrue(list.isStale());
+	}
+
+	public void testDependency_FiresListChange() {
+		assertEquals(list.nextComputation, list);
+
+		Object element = new Object();
+		list.nextComputation.add(element);
+
+		list.dependency.fireChange();
+
+		List expectedList = new ArrayList();
+		expectedList.add(element);
+		assertEquals(expectedList, list);
+	}
+
+	public void testDependency_NoStaleEventIfAlreadyDirty() {
+		list.dependency.fireChange();
+		list.addStaleListener(new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+				fail("Should not fire stale when list is already dirty");
+			}
+		});
+		list.dependency.fireStale();
+	}
+
+	public void testDependency_ListChangeEventFiresOnlyWhenNotDirty() {
+		ListChangeEventTracker tracker = ListChangeEventTracker.observe(list);
+
+		list.dependency.fireChange();
+		assertEquals(
+				"ComputedList should fire list change event when its dependency changes",
+				1, tracker.count);
+
+		list.dependency.fireChange();
+		assertEquals(
+				"ComputedList should not fire list change events when dirty",
+				1, tracker.count);
+
+		list.size(); // Force list to recompute.
+		list.dependency.fireChange();
+		assertEquals(
+				"ComputedList should fire list change event when its dependency changes",
+				2, tracker.count);
+	}
+
+	static class ComputedListStub extends ComputedList {
+		List nextComputation = new ArrayList();
+		ObservableStub dependency;
+
+		ComputedListStub() {
+			this(Realm.getDefault());
+		}
+
+		ComputedListStub(Realm realm) {
+			super(realm);
+			dependency = new ObservableStub(realm);
+		}
+
+		protected List calculate() {
+			ObservableTracker.getterCalled(dependency);
+			return new ArrayList(nextComputation);
+		}
+	}
+
+	static class ObservableStub extends AbstractObservable {
+		public ObservableStub(Realm realm) {
+			super(realm);
+		}
+
+		boolean stale;
+
+		public boolean isStale() {
+			return stale;
+		}
+
+		protected void fireStale() {
+			stale = true;
+			super.fireStale();
+		}
+
+		protected void fireChange() {
+			super.fireChange();
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ComputedListTest.class.getName());
+		suite.addTestSuite(ComputedListTest.class);
+		suite.addTest(ObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			final ComputedListStub list = new ComputedListStub(realm);
+			for (int i = 0; i < elementCount; i++)
+				list.nextComputation.add(createElement(list));
+			list.size(); // force list to compute
+			return list;
+		}
+
+		public void change(IObservable observable) {
+			ComputedListStub list = (ComputedListStub) observable;
+			list.nextComputation.add(new Object());
+			list.dependency.fireChange();
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			if (stale)
+				((ComputedListStub) observable).dependency.fireStale();
+			else {
+				ComputedListStub computedList = (ComputedListStub) observable;
+				computedList.dependency.stale = false;
+				computedList.dependency.fireChange();
+			}
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/DecoratingObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/DecoratingObservableListTest.java
new file mode 100644
index 0000000..ca361d5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/DecoratingObservableListTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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 208332)
+ *     Matthew Hall - bug 213145
+ *         (through ProxyObservableListTest.java)
+ *     Matthew Hall - bug 237718
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.DecoratingObservableList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DecoratingObservableListTest {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DecoratingObservableListTest.class
+				.getName());
+		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		private Object elementType = Object.class;
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableList wrappedList = new WritableList(realm,
+					new ArrayList(), elementType);
+			for (int i = 0; i < elementCount; i++)
+				wrappedList.add(new Object());
+			return new DecoratingObservableListStub(wrappedList);
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			((DecoratingObservableListStub) observable).decorated
+					.add(new Object());
+		}
+	}
+
+	static class DecoratingObservableListStub extends DecoratingObservableList {
+		IObservableList decorated;
+
+		DecoratingObservableListStub(IObservableList decorated) {
+			super(decorated, true);
+			this.decorated = decorated;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ListDiffTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ListDiffTest.java
new file mode 100644
index 0000000..672b643
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ListDiffTest.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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)
+ *     Matthew Hall - bug 272651
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+
+/**
+ * Tests for ListDiff class
+ * 
+ * @since 1.1
+ */
+public class ListDiffTest extends TestCase {
+	ListDiffVisitorStub visitor;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		visitor = new ListDiffVisitorStub();
+	}
+
+	public void testAccept_Add() {
+		createListDiff(add(0, "element")).accept(visitor);
+		assertEquals("add(0,element)", visitor.log);
+	}
+
+	public void testAccept_Remove() {
+		createListDiff(remove(0, "element")).accept(visitor);
+		assertEquals("remove(0,element)", visitor.log);
+	}
+
+	public void testAccept_MoveForward_RemoveBeforeAdd() {
+		createListDiff(remove(0, "element"), add(1, "element")).accept(visitor);
+		assertEquals("move(0,1,element)", visitor.log);
+	}
+
+	public void testAccept_MoveForward_AddBeforeRemove() {
+		// Add at index 2 then remove at index 0 leaves the element at index 1
+		createListDiff(add(2, "element"), remove(0, "element")).accept(visitor);
+		assertEquals("move(0,1,element)", visitor.log);
+	}
+
+	public void testAccept_MoveBackward_RemoveBeforeAdd() {
+		createListDiff(remove(4, "element"), add(1, "element")).accept(visitor);
+		assertEquals("move(4,1,element)", visitor.log);
+	}
+
+	public void testAccept_MoveBackward_AddBeforeRemove() {
+		// Element is originally at position 4 in this test, but we must remove
+		// it at 5 since the adding it at 1 first changes the index
+		createListDiff(add(1, "element"), remove(5, "element")).accept(visitor);
+		assertEquals("move(4,1,element)", visitor.log);
+	}
+
+	public void testAccept_Replace_RemoveBeforeAdd() {
+		createListDiff(remove(0, "element0"), add(0, "element1")).accept(
+				visitor);
+		assertEquals("replace(0,element0,element1)", visitor.log);
+	}
+
+	public void testAccept_Replace_AddBeforeRemove() {
+		createListDiff(add(0, "element1"), remove(1, "element0")).accept(
+				visitor);
+		assertEquals("replace(0,element0,element1)", visitor.log);
+	}
+
+	public void testAccept_AllPatterns() {
+		createListDiff(new ListDiffEntry[] {
+		// Replace (remove before add)
+				remove(0, "element0"), add(0, "element1"),
+				// Replace (add before remove)
+				add(0, "element3"), remove(1, "element2"),
+				// Remove
+				remove(1, "element4"),
+				// Add
+				add(2, "element5"),
+				// Move forward (remove before add)
+				remove(5, "element6"), add(6, "element6"),
+				// Move forward (add before remove)
+				add(7, "element6"), remove(5, "element6"),
+				// Move backward (remove before add)
+				remove(12, "element7"), add(11, "element7"),
+				// Move backward (add before remove)
+				add(11, "element7"), remove(13, "element7"),
+				// Remove then add in place -- treat as replace
+				remove(11, "element8"), add(11, "element8"),
+				// Add then remove in place (special case) -- treat as separate
+				// add and remove
+				add(12, "element9"), remove(12, "element9") }).accept(visitor);
+		assertEquals(
+				"replace(0,element0,element1), replace(0,element2,element3), "
+						+ "remove(1,element4), " + "add(2,element5), "
+						+ "move(5,6,element6), move(5,6,element6), "
+						+ "move(12,11,element7), move(12,11,element7), "
+						+ "replace(11,element8,element8), "
+						+ "add(12,element9), remove(12,element9)", visitor.log);
+	}
+
+	public void testAccept_MoveDetectionUsesEqualityNotSameness() {
+		Object element0 = new String("element");
+		Object element1 = new String("element");
+		assertNotSame(element0, element1);
+		assertEquals(element0, element1);
+
+		createListDiff(remove(0, element0), add(1, element1)).accept(visitor);
+		assertEquals("move(0,1,element)", visitor.log);
+	}
+
+	private ListDiffEntry add(int index, Object element) {
+		return Diffs.createListDiffEntry(index, true, element);
+	}
+
+	private ListDiffEntry remove(int index, Object element) {
+		return Diffs.createListDiffEntry(index, false, element);
+	}
+
+	private ListDiff createListDiff(ListDiffEntry difference) {
+		return createListDiff(new ListDiffEntry[] { difference });
+	}
+
+	private ListDiff createListDiff(ListDiffEntry first, ListDiffEntry second) {
+		return createListDiff(new ListDiffEntry[] { first, second });
+	}
+
+	private ListDiff createListDiff(ListDiffEntry[] differences) {
+		return Diffs.createListDiff(differences);
+	}
+
+	class ListDiffVisitorStub extends ListDiffVisitor {
+		String log = "";
+
+		public void handleAdd(int index, Object element) {
+			log("add(" + index + "," + element + ")");
+		}
+
+		public void handleRemove(int index, Object element) {
+			log("remove(" + index + "," + element + ")");
+		}
+
+		public void handleMove(int oldIndex, int newIndex, Object element) {
+			log("move(" + oldIndex + "," + newIndex + "," + element + ")");
+		}
+
+		public void handleReplace(int index, Object oldElement,
+				Object newElement) {
+			log("replace(" + index + "," + oldElement + "," + newElement + ")");
+		}
+
+		private void log(String message) {
+			if (log.length() > 0)
+				log += ", ";
+			log += message;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ListDiffVisitorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ListDiffVisitorTest.java
new file mode 100644
index 0000000..2b352e1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ListDiffVisitorTest.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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 208858)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for ListDiffVisitor class
+ * 
+ * @since 1.1
+ */
+public class ListDiffVisitorTest extends TestCase {
+	ListDiffVisitorStub visitor;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		visitor = new ListDiffVisitorStub();
+	}
+
+	public void testHandleMove_DelegatesByDefault() {
+		visitor.handleMove(0, 1, "element");
+		assertEquals(
+				"Default ListDiffVisitor.handleMove must delegate to handleRemove and handleAdd",
+				"remove(0,element), add(1,element)", visitor.log);
+	}
+
+	public void testHandleReplace_DelegatesByDefault() {
+		visitor.handleReplace(2, "oldElement", "newElement");
+		assertEquals(
+				"Default ListDiffVisitor.handleReplace must delegate to handleRemove and handleAdd",
+				"remove(2,oldElement), add(2,newElement)", visitor.log);
+	}
+
+	static class ListDiffVisitorStub extends ListDiffVisitor {
+		String log = "";
+
+		private void log(String message) {
+			if (log.length() > 0)
+				log += ", ";
+			log += message;
+		}
+
+		public void handleAdd(int index, Object element) {
+			log("add(" + index + "," + element + ")");
+		}
+
+		public void handleRemove(int index, Object element) {
+			log("remove(" + index + "," + element + ")");
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/MultiListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/MultiListTest.java
new file mode 100644
index 0000000..79859a6
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/MultiListTest.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * 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 222289)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+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.StaleEvent;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.MultiList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class MultiListTest extends AbstractDefaultRealmTestCase {
+	MultiListStub multiList;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		WritableList[] lists = new WritableList[] { new WritableList(),
+				new WritableList() };
+		multiList = new MultiListStub(Realm.getDefault(), lists);
+	}
+
+	public void testIsStale_FollowsSublist() {
+		assertFalse(multiList.isStale());
+		multiList.subLists[0].setStale(true);
+		assertTrue(multiList.isStale());
+		multiList.subLists[0].setStale(false);
+		assertFalse(multiList.isStale());
+	}
+
+	public void testDependency_FiresListChange() {
+		List expectedList = new ArrayList();
+		assertEquals(expectedList, multiList);
+
+		Object element = new Object();
+		expectedList.add(element);
+		multiList.subLists[0].add(element);
+		assertEquals(expectedList, multiList);
+	}
+
+	public void testStaleEvent_NoFireEventIfAlreadyStale() {
+		multiList.subLists[0].setStale(true);
+		multiList.addStaleListener(new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+				fail("Should not fire stale when list is already dirty");
+			}
+		});
+		multiList.subLists[1].setStale(true);
+	}
+
+	public void testModifySubList_FiresListChangeEventFromMultiList() {
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(multiList);
+		ListDiffEntry[] differences;
+
+		//
+
+		Object element0 = new Object();
+		multiList.subLists[0].add(element0);
+
+		assertEquals(1, tracker.count);
+
+		differences = tracker.event.diff.getDifferences();
+		assertEquals(1, differences.length);
+		assertEntry(differences[0], 0, true, element0);
+
+		//
+
+		Object element1 = new Object();
+		multiList.subLists[1].add(element1);
+
+		assertEquals(2, tracker.count);
+
+		differences = tracker.event.diff.getDifferences();
+		assertEquals(1, differences.length);
+		assertEntry(differences[0], 1, true, element1);
+
+		//
+
+		Object element2 = new Object();
+		multiList.subLists[0].add(element2);
+
+		assertEquals(3, tracker.count);
+
+		differences = tracker.event.diff.getDifferences();
+		assertEquals(1, differences.length);
+		assertEntry(differences[0], 1, true, element2);
+
+	}
+
+	/**
+	 * @param entry
+	 * @param position
+	 * @param addition
+	 * @param element
+	 */
+	private void assertEntry(ListDiffEntry entry, int position,
+			boolean addition, Object element) {
+		assertEquals(element, entry.getElement());
+		assertEquals(addition, entry.isAddition());
+		assertEquals(position, entry.getPosition());
+	}
+
+	private static class MultiListStub extends MultiList {
+		WritableList[] subLists;
+
+		MultiListStub(Realm realm, WritableList[] lists) {
+			super(realm, lists);
+			this.subLists = lists;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(MultiListTest.class.getName());
+		suite.addTestSuite(MultiListTest.class);
+		suite.addTest(ObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			WritableList[] subLists = new WritableList[] {
+					new WritableList(realm), new WritableList(realm) };
+			final MultiListStub list = new MultiListStub(realm, subLists);
+			for (int i = 0; i < elementCount; i++)
+				list.subLists[0].add(createElement(list));
+			return list;
+		}
+
+		public void change(IObservable observable) {
+			MultiListStub list = (MultiListStub) observable;
+			list.subLists[0].add(new Object());
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			((MultiListStub) observable).subLists[0].setStale(stale);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ObservableListTest.java
new file mode 100755
index 0000000..461e87b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ObservableListTest.java
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * 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 - bugs 208858, 213145
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IListChangeListener;
+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.ObservableList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ */
+public class ObservableListTest extends TestCase {
+	private ObservableListStub list;
+
+	protected void setUp() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+
+		list = new ObservableListStub(new ArrayList(0), Object.class);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	protected void tearDown() throws Exception {
+		RealmTester.setDefault(null);
+	}
+
+	public void testIsStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.isStale();
+			}
+		});
+	}
+
+	public void testSetStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.setStale(false);
+			}
+		});
+	}
+
+	public void testMove_FiresListChanges() throws Exception {
+		list = new MutableObservableListStub();
+		final Object element = new Object();
+		list.add(0, element);
+		list.add(1, new Object());
+
+		final List diffEntries = new ArrayList();
+		list.addListChangeListener(new IListChangeListener() {
+			public void handleListChange(ListChangeEvent event) {
+				diffEntries.addAll(Arrays.asList(event.diff.getDifferences()));
+			} 
+		}); 
+
+		list.move(0, 1);
+
+		assertEquals(2, diffEntries.size());
+
+		ListDiffEntry entry = (ListDiffEntry) diffEntries.get(0);
+		assertEquals(element, entry.getElement());
+		assertEquals(false, entry.isAddition());
+		assertEquals(0, entry.getPosition());
+
+		entry = (ListDiffEntry) diffEntries.get(1);
+		assertEquals(element, entry.getElement());
+		assertEquals(true, entry.isAddition());
+		assertEquals(1, entry.getPosition());
+	}
+
+	public void testMove_MovesElement() throws Exception {
+		list = new MutableObservableListStub();
+		final Object element0 = new Object();
+		final Object element1 = new Object();
+		list.add(0, element0);
+		list.add(1, element1);
+		
+		list.move(0, 1);
+
+		assertEquals(element1, list.get(0));
+		assertEquals(element0, list.get(1));
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ObservableListTest.class.getName());
+		suite.addTestSuite(ObservableListTest.class);
+		suite.addTest(ObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+	
+	/* package */ static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm, final int elementCount) {
+			List wrappedList = new ArrayList();
+			for (int i = 0; i < elementCount; i++) {
+				wrappedList.add(String.valueOf(i));
+			}
+			
+			return new MutableObservableListStub(realm, wrappedList, String.class);
+		}
+		
+		public void change(IObservable observable) {
+			ObservableListStub list = (ObservableListStub) observable;
+			Object element = "element";
+			list.wrappedList.add(element);
+			list.fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(list.size(), true, element)));
+		}
+		
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+	}
+
+	/* package */static class ObservableListStub extends ObservableList {
+		List wrappedList;
+		ObservableListStub(Realm realm, List wrappedList, Object elementType) {
+			super(realm, wrappedList, elementType);
+			this.wrappedList = wrappedList;
+		}
+		
+		ObservableListStub(List wrappedList, Object elementType) {
+			super(wrappedList, elementType);
+			this.wrappedList = wrappedList;
+		}
+		
+		protected void fireListChange(ListDiff diff) {
+			super.fireListChange(diff);
+		}
+	}
+
+	/* package */static class MutableObservableListStub extends
+			ObservableListStub {
+		MutableObservableListStub() {
+			this(Realm.getDefault(), new ArrayList(), null);
+		}
+
+		MutableObservableListStub(Realm realm, List wrappedList,
+				Object elementType) {
+			super(realm, wrappedList, elementType);
+		}
+
+		public void add(int index, Object element) {
+			checkRealm();
+			wrappedList.add(index, element);
+			fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(
+					index, true, element)));
+		}
+
+		public Object remove(int index) {
+			checkRealm();
+			Object element = wrappedList.remove(index);
+			fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(
+					index, false, element)));
+			return element;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java
new file mode 100755
index 0000000..9b4004c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/WritableListTest.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 147515
+ *     Matthew Hall - bug 213145
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.list;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 3.2
+ */
+public class WritableListTest extends TestCase {
+	protected void tearDown() throws Exception {
+		RealmTester.setDefault(null);
+	}
+
+	public void testSetRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.add("");
+				list.set(0, "");
+			}
+		});
+	}
+
+	public void testAddRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.add("");
+			}
+		});
+	}
+
+	public void testAddByIndexRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.add(0, "");
+			}
+		});
+	}
+
+	public void testAddAllRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.addAll(Collections.EMPTY_LIST);
+			}
+		});
+	}
+
+	public void testAddAllByIndexRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.addAll(0, Collections.EMPTY_LIST);
+			}
+		});
+	}
+
+	public void testRemoveRealmChecks() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+		final WritableList list = new WritableList();
+		list.add("");
+		list.add("");
+
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.remove("");
+			}
+		});
+		RealmTester.setDefault(null);
+	}
+
+	public void testRemoveByIndexRealmChecks() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+		final WritableList list = new WritableList();
+		list.add("");
+		list.add("");
+
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				list.remove(list.size() - 1);
+			}
+		});
+
+		RealmTester.setDefault(null);
+	}
+
+	public void testRemoveAllRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.removeAll(Collections.EMPTY_LIST);
+			}
+		});
+	}
+
+	public void testRetainAllRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.retainAll(Collections.EMPTY_LIST);
+			}
+		});
+	}
+
+	public void testClearRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableList list = new WritableList();
+				list.clear();
+			}
+		});
+	}
+
+	public void testNullElementType() throws Exception {
+		RealmTester.setDefault(SWTObservables.getRealm(Display.getDefault()));
+		WritableList writableList = new WritableList();
+		assertNull(writableList.getElementType());
+
+		writableList = new WritableList(Realm.getDefault());
+		assertNull(writableList.getElementType());
+	}
+
+	public void testWithElementType() throws Exception {
+		RealmTester.setDefault(SWTObservables.getRealm(Display.getDefault()));
+
+		Object elementType = String.class;
+		WritableList list = WritableList.withElementType(elementType);
+		assertNotNull(list);
+		assertEquals(Realm.getDefault(), list.getRealm());
+		assertEquals(elementType, list.getElementType());
+	}
+
+	public void testListConstructorsDoNotCopy_1() {
+		RealmTester.setDefault(new CurrentRealm(true));
+		List list = new ArrayList(Arrays.asList(new Object[] { "a", "b", "c" }));
+		WritableList wlist = new WritableList(list, Object.class);
+		wlist.remove(1);
+		assertEquals(2, list.size());
+		list.add("d");
+		assertEquals(3, wlist.size());
+	}
+
+	public void testListConstructorsDoNotCopy_2() {
+		List list = new ArrayList(Arrays.asList(new Object[] { "a", "b", "c" }));
+		WritableList wlist = new WritableList(new CurrentRealm(true), list,
+				Object.class);
+		wlist.remove(1);
+		assertEquals(2, list.size());
+		list.add("d");
+		assertEquals(3, wlist.size());
+	}
+
+	public void testCollectionConstructorsCopy_1() {
+		RealmTester.setDefault(new CurrentRealm(true));
+		List list = new ArrayList(Arrays.asList(new Object[] { "a", "b", "c" }));
+		WritableList wlist = new WritableList((Collection) list, Object.class);
+		wlist.remove(1);
+		assertEquals(3, list.size());
+		list.add("d");
+		assertEquals(2, wlist.size());
+	}
+
+	public void testCollectionConstructorsCopy_2() {
+		List list = new ArrayList(Arrays.asList(new Object[] { "a", "b", "c" }));
+		WritableList wlist = new WritableList(new CurrentRealm(true),
+				(Collection) list, Object.class);
+		wlist.remove(1);
+		assertEquals(3, list.size());
+		list.add("d");
+		assertEquals(2, wlist.size());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(WritableListTest.class.getName());
+		suite.addTestSuite(WritableListTest.class);
+		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		public Object createElement(IObservableCollection collection) {
+			return String.valueOf(collection.size() + 1);
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				final int itemCount) {
+			WritableList observable = new WritableList(realm, new ArrayList(),
+					String.class);
+
+			for (int i = 0; i < itemCount; i++) {
+				observable.add(String.valueOf(i));
+			}
+
+			return observable;
+		}
+
+		public void change(IObservable observable) {
+			((WritableList) observable).add("");
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/AbstractObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/AbstractObservableMapTest.java
new file mode 100644
index 0000000..7f617e8
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/AbstractObservableMapTest.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.map;
+
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.map.AbstractObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ */
+public class AbstractObservableMapTest extends TestCase {
+	private AbstractObservableMapStub map;
+
+	protected void setUp() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+		map = new AbstractObservableMapStub();
+	}
+	
+	protected void tearDown() throws Exception {
+		RealmTester.setDefault(null);
+	}
+	
+	public void testIsStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.isStale();
+			}			
+		});
+	}
+	
+	public void testSetStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.setStale(true);
+			}
+		});
+	}
+	
+	public void testFireStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() { 
+			public void run() {
+				map.fireStale();
+			}
+		});
+	}
+	
+	public void testFireChangeRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.fireChange();
+			}
+		});
+	}
+	
+	public void testFireMapChangeRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.fireMapChange(null);
+			}
+		});
+	}
+	
+	static class AbstractObservableMapStub extends AbstractObservableMap {
+		public Set entrySet() {
+			return null;
+		}
+		
+		protected void fireChange() {
+			super.fireChange();
+		}
+		
+		protected void fireMapChange(MapDiff diff) {
+			super.fireMapChange(diff);
+		}
+		
+		protected void fireStale() {
+			super.fireStale();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/BidiObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/BidiObservableMapTest.java
new file mode 100644
index 0000000..c9932aa
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/BidiObservableMapTest.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * 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 233306)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.map;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.map.BidiObservableMap;
+import org.eclipse.core.databinding.observable.map.BidirectionalMap;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.WritableMap;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class BidiObservableMapTest extends AbstractDefaultRealmTestCase {
+	private IObservableMap wrappedMap;
+	private BidiObservableMap bidiMap;
+	private Object key1;
+	private Object key2;
+	private Object value1;
+	private Object value2;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		wrappedMap = new WritableMap();
+		bidiMap = new BidiObservableMap(wrappedMap);
+		key1 = new Object();
+		key2 = new Object();
+		value1 = new Object();
+		value2 = new Object();
+	}
+
+	public void testConstructor_NullArgument() {
+		try {
+			new BidirectionalMap(null);
+			fail("Expected NullPointerException");
+		} catch (NullPointerException expected) {
+		}
+	}
+
+	public void withAndWithoutListeners(Runnable runnable) throws Exception {
+		// assuming setUp() beforehand
+
+		// without listeners
+		runnable.run();
+
+		tearDown();
+		setUp();
+
+		// with listeners
+		ChangeEventTracker.observe(wrappedMap);
+		runnable.run();
+
+		// assuming tearDown() afterward
+	}
+
+	public void testGetKeys_Empty() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				assertEquals(Collections.EMPTY_SET, bidiMap.getKeys(value1));
+			}
+		});
+	}
+
+	public void testGetKeys_NullKey() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				wrappedMap.put(null, value1);
+				assertEquals(Collections.singleton(null), bidiMap
+						.getKeys(value1));
+			}
+		});
+	}
+
+	public void testGetKeys_NullValue() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				wrappedMap.put(key1, null);
+				assertEquals(Collections.singleton(key1), bidiMap.getKeys(null));
+			}
+		});
+	}
+
+	public void testGetKeys_SinglePut() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				wrappedMap.put(key1, value1);
+				assertEquals(Collections.singleton(key1), bidiMap
+						.getKeys(value1));
+			}
+		});
+	}
+
+	public void testGetKeys_ReplaceValue() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				wrappedMap.put(key1, value1);
+				assertEquals(Collections.singleton(key1), bidiMap
+						.getKeys(value1));
+				assertEquals(Collections.EMPTY_SET, bidiMap.getKeys(value2));
+				wrappedMap.put(key1, value2);
+				assertEquals(Collections.EMPTY_SET, bidiMap.getKeys(value1));
+				assertEquals(Collections.singleton(key1), bidiMap
+						.getKeys(value2));
+			}
+		});
+	}
+
+	public void testGetKeys_MultipleKeysWithSameValue() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				wrappedMap.put(key1, value1);
+				wrappedMap.put(key2, value1);
+
+				Set expected = new HashSet();
+				expected.add(key1);
+				expected.add(key2);
+				assertEquals(expected, bidiMap.getKeys(value1));
+			}
+		});
+	}
+
+	public void testContainsValue_PutAndRemove() throws Exception {
+		withAndWithoutListeners(new Runnable() {
+			public void run() {
+				assertFalse(bidiMap.containsValue(value1));
+				wrappedMap.put(key1, value1);
+				assertTrue(bidiMap.containsValue(value1));
+				wrappedMap.put(key2, value1);
+				assertTrue(bidiMap.containsValue(value1));
+				wrappedMap.remove(key1);
+				assertTrue(bidiMap.containsValue(value1));
+				wrappedMap.remove(key2);
+				assertFalse(bidiMap.containsValue(value1));
+			}
+		});
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/CompositeMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/CompositeMapTest.java
new file mode 100644
index 0000000..53df90c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/CompositeMapTest.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.map;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.map.CompositeMap;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.examples.databinding.model.SimpleCart;
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class CompositeMapTest extends AbstractDefaultRealmTestCase {
+
+	private WritableSet persons;
+	private CompositeMap composedMap;
+	private IObservableMap first;
+
+	boolean hasListeners(AbstractObservable o) {
+		try {
+			Method method = AbstractObservable.class.getSuperclass().getDeclaredMethod("hasListeners", new Class[0]);
+			method.setAccessible(true);
+			return ((Boolean)method.invoke(o, new Object[0])).booleanValue();
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		persons = new WritableSet();
+		first = BeansObservables.observeMap(persons,
+				SimplePerson.class, "cart");
+		composedMap = new CompositeMap(first, new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return BeansObservables.observeMap((IObservableSet) target,
+						SimpleCart.class, "numItems");
+			}
+		});
+	}
+
+	public void testAddToFirstMap() {
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		SimplePerson newPerson = new SimplePerson("p1", "a1", "c1", "s1");
+		newPerson.getCart().setNumItems(42);
+		persons.add(newPerson);
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getRemovedKeys().size());
+		assertEquals(0, tracker.event.diff.getChangedKeys().size());
+		assertEquals(Collections.singleton(newPerson), tracker.event.diff
+				.getAddedKeys());
+		assertEquals(new Integer(42), tracker.event.diff.getNewValue(newPerson));
+		assertEquals(new Integer(42), composedMap.get(newPerson));
+	}
+
+	public void testAddSharedToFirstMap() {
+		SimplePerson person1 = new SimplePerson("p1", "a1", "c1", "s1");
+		person1.getCart().setNumItems(42);
+		persons.add(person1);
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		SimplePerson person2 = new SimplePerson("p1", "a1", "c1", "s1");
+		person2.setCart(person1.getCart());
+		persons.add(person2);
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getRemovedKeys().size());
+		assertEquals(0, tracker.event.diff.getChangedKeys().size());
+		assertEquals(Collections.singleton(person2), tracker.event.diff
+				.getAddedKeys());
+		assertEquals(new Integer(42), tracker.event.diff.getNewValue(person2));
+		assertEquals(new Integer(42), composedMap.get(person2));
+		assertEquals(new Integer(42), composedMap.get(person1));
+	}
+
+	public void testRemoveFromFirstMap() {
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		SimplePerson newPerson = new SimplePerson("p1", "a1", "c1", "s1");
+		newPerson.getCart().setNumItems(42);
+		persons.add(newPerson);
+		assertTrue("newPerson should be added", composedMap.containsKey(newPerson));
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		persons.remove(newPerson);
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getAddedKeys().size());
+		assertEquals(0, tracker.event.diff.getChangedKeys().size());
+		assertEquals(Collections.singleton(newPerson), tracker.event.diff
+				.getRemovedKeys());
+		assertEquals(new Integer(42), tracker.event.diff.getOldValue(newPerson));
+		assertFalse("newPerson should be removed", composedMap.containsKey(newPerson));
+	}
+
+	public void testRemoveSharedFromFirstMap() {
+		SimplePerson person1 = new SimplePerson("p1", "a1", "c1", "s1");
+		person1.getCart().setNumItems(42);
+		persons.add(person1);
+		SimplePerson person2 = new SimplePerson("p1", "a1", "c1", "s1");
+		person2.setCart(person1.getCart());
+		persons.add(person2);
+		assertTrue("person2 should be added", composedMap.containsKey(person2));
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		persons.remove(person2);
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getAddedKeys().size());
+		assertEquals(0, tracker.event.diff.getChangedKeys().size());
+		assertEquals(Collections.singleton(person2), tracker.event.diff
+				.getRemovedKeys());
+		assertEquals(new Integer(42), tracker.event.diff.getOldValue(person2));
+		assertFalse("person2 should be removed", composedMap.containsKey(person2));
+		assertEquals(new Integer(42), composedMap.get(person1));
+	}
+
+	public void testChangeInFirstMap() {
+		SimplePerson person1 = new SimplePerson("p1", "a1", "c1", "s1");
+		person1.getCart().setNumItems(42);
+		persons.add(person1);
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		person1.setCart(new SimpleCart());
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getAddedKeys().size());
+		assertEquals(0, tracker.event.diff.getRemovedKeys().size());
+		assertEquals(Collections.singleton(person1), tracker.event.diff
+				.getChangedKeys());
+		assertEquals(new Integer(42), tracker.event.diff.getOldValue(person1));
+		assertEquals(new Integer(0), tracker.event.diff.getNewValue(person1));
+		assertEquals(new Integer(0), composedMap.get(person1));
+	}
+	
+	public void testChangeInFirstMapToShared() {
+		SimplePerson person0 = new SimplePerson("p0", "a0", "c0", "s0");
+		person0.getCart().setNumItems(13);
+		persons.add(person0);
+		SimplePerson person1 = new SimplePerson("p1", "a1", "c1", "s1");
+		person1.getCart().setNumItems(42);
+		persons.add(person1);
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		person1.setCart(person0.getCart());
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getAddedKeys().size());
+		assertEquals(0, tracker.event.diff.getRemovedKeys().size());
+		assertEquals(Collections.singleton(person1), tracker.event.diff
+				.getChangedKeys());
+		assertEquals(new Integer(42), tracker.event.diff.getOldValue(person1));
+		assertEquals(new Integer(13), tracker.event.diff.getNewValue(person1));
+		assertEquals(new Integer(13), composedMap.get(person1));
+	}
+	
+	public void testChangeInFirstMapFromShared() {
+		SimplePerson person0 = new SimplePerson("p0", "a0", "c0", "s0");
+		person0.getCart().setNumItems(13);
+		persons.add(person0);
+		SimplePerson person1 = new SimplePerson("p1", "a1", "c1", "s1");
+		person1.setCart(person0.getCart());
+		persons.add(person1);
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		person1.setCart(new SimpleCart());
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getAddedKeys().size());
+		assertEquals(0, tracker.event.diff.getRemovedKeys().size());
+		assertEquals(Collections.singleton(person1), tracker.event.diff
+				.getChangedKeys());
+		assertEquals(new Integer(13), tracker.event.diff.getOldValue(person1));
+		assertEquals(new Integer(0), tracker.event.diff.getNewValue(person1));
+		assertEquals(new Integer(0), composedMap.get(person1));
+	}
+	
+	public void testChangeInSecondMap() {
+		SimplePerson person0 = new SimplePerson("p0", "a0", "c0", "s0");
+		person0.getCart().setNumItems(13);
+		persons.add(person0);
+		MapChangeEventTracker tracker = new MapChangeEventTracker();
+		composedMap.addMapChangeListener(tracker);
+		assertEquals(0, tracker.count);
+		person0.getCart().setNumItems(42);
+		assertEquals(1, tracker.count);
+		assertEquals(0, tracker.event.diff.getAddedKeys().size());
+		assertEquals(0, tracker.event.diff.getRemovedKeys().size());
+		assertEquals(Collections.singleton(person0), tracker.event.diff
+				.getChangedKeys());
+		assertEquals(new Integer(13), tracker.event.diff.getOldValue(person0));
+		assertEquals(new Integer(42), tracker.event.diff.getNewValue(person0));
+		assertEquals(new Integer(42), composedMap.get(person0));
+	}
+	
+	public void testDispose() {
+		SimplePerson person0 = new SimplePerson("p0", "a0", "c0", "s0");
+		person0.getCart().setNumItems(13);
+		persons.add(person0);
+		assertTrue(hasListeners((AbstractObservable) first));
+		composedMap.dispose();
+		assertFalse(hasListeners((AbstractObservable) first));
+	}
+	
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/ComputedObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/ComputedObservableMapTest.java
new file mode 100644
index 0000000..1ae6696
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/ComputedObservableMapTest.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 247394)
+ *     Matthew Hall - bug 266754
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.map;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ComputedObservableMapTest extends AbstractDefaultRealmTestCase {
+	private IObservableSet keySet;
+	private ComputedObservableMapStub map;
+	private String propertyName;
+	private Bean bean;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		keySet = new WritableSet();
+		map = new ComputedObservableMapStub(keySet);
+		propertyName = "value";
+		bean = new Bean("a");
+	}
+
+	public void testGet_ElementNotInKeySet() {
+		assertNull(map.get(bean));
+	}
+
+	public void testGet_ElementInKeySet() {
+		keySet.add(bean);
+		assertEquals("a", map.get(bean));
+	}
+
+	public void testPut_ElementNotInKeySet() {
+		assertNull(map.put(bean, "b"));
+		assertEquals("a", bean.getValue());
+	}
+
+	public void testPut_ElementInKeySet() {
+		keySet.add(bean);
+		assertEquals("a", map.put(bean, "b"));
+		assertEquals("b", map.get(bean));
+	}
+
+	public void testAddToKeySet_BeforeFirstListenerAdded_DoesNotAddListenerToKey() {
+		assertFalse(bean.hasListeners(propertyName));
+		keySet.add(bean);
+		assertFalse(bean.hasListeners(propertyName));
+	}
+
+	public void testAddToKeySet_AfterFirstListenerAdded_AddsListenerToKey() {
+		ChangeEventTracker.observe(map);
+		assertFalse(bean.hasListeners(propertyName));
+		keySet.add(bean);
+		assertTrue(bean.hasListeners(propertyName));
+	}
+
+	public void testRemoveFromKeySet_RemovesListenersFromKey() {
+		ChangeEventTracker.observe(map);
+		keySet.add(bean);
+		assertTrue(bean.hasListeners(propertyName));
+		keySet.remove(bean);
+		assertFalse(bean.hasListeners(propertyName));
+	}
+
+	public void testRemoveLastListener_DoNotDiscardKeySet() {
+		IChangeListener listener = new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				// do nothing
+			}
+		};
+		map.addChangeListener(listener); // first listener added
+		map.removeChangeListener(listener); // last listener removed
+		keySet.add(bean);
+		assertEquals(1, map.size());
+	}
+
+	public void testDispose_RemoveListenersFromKeySetElements() {
+		ChangeEventTracker.observe(map);
+		keySet.add(bean);
+		assertTrue(bean.hasListeners(propertyName));
+		map.dispose();
+		assertFalse(bean.hasListeners(propertyName));
+	}
+
+	public void testDisposeKeySet_DisposesMap() {
+		assertFalse(map.isDisposed());
+		keySet.dispose();
+		assertTrue(map.isDisposed());
+	}
+
+	public void testDisposeKeySet_RemoveListenersFromKeySetElements() {
+		ChangeEventTracker.observe(map);
+		keySet.add(bean);
+		assertTrue(bean.hasListeners(propertyName));
+		keySet.dispose();
+		assertFalse(bean.hasListeners(propertyName));
+	}
+
+	static class ComputedObservableMapStub extends ComputedObservableMap {
+		private PropertyChangeListener listener = new PropertyChangeListener() {
+			public void propertyChange(PropertyChangeEvent evt) {
+				fireSingleChange(evt.getSource(), evt.getOldValue(), evt
+						.getNewValue());
+			}
+		};
+
+		ComputedObservableMapStub(IObservableSet keySet) {
+			super(keySet);
+		}
+
+		protected Object doGet(Object key) {
+			return ((Bean) key).getValue();
+		}
+
+		protected Object doPut(Object key, Object value) {
+			Object result = doGet(key);
+			((Bean) key).setValue((String) value);
+			return result;
+		}
+
+		protected void hookListener(Object addedKey) {
+			((Bean) addedKey).addPropertyChangeListener(listener);
+		}
+
+		protected void unhookListener(Object removedKey) {
+			((Bean) removedKey).removePropertyChangeListener(listener);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/ObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/ObservableMapTest.java
new file mode 100644
index 0000000..acc4966
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/ObservableMapTest.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Ovidio Mallo - bug 247741
+ *     Matthew Hall - bug 274450
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.map;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.map.ObservableMap;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ObservableMapTest extends TestCase {
+	ObservableMapStub map;
+
+	protected void setUp() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+		map = new ObservableMapStub(new HashMap());
+	}
+
+	protected void tearDown() throws Exception {
+		RealmTester.setDefault(null);
+	}
+
+	public void testDisposeMapChangeListeners() throws Exception {
+		MapChangeEventTracker listener = MapChangeEventTracker.observe(map);
+
+		assertEquals(0, listener.count);
+		map.fireMapChange(null);
+		assertEquals(1, listener.count);
+
+		map.dispose();
+		try {
+			map.fireMapChange(null);
+		} catch (Exception e) {
+			// do nothing
+		}
+
+		assertEquals("listener should not have been notified", 1,
+				listener.count);
+	}
+
+	public void testIsStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.isStale();
+			}
+		});
+	}
+
+	public void testSetStaleRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.setStale(true);
+			}
+		});
+	}
+
+	public void testFireMapChangeRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				map.fireMapChange(null);
+			}
+		});
+	}
+
+	public void testEquals() {
+		assertTrue(map.equals(Collections.EMPTY_MAP));
+	}
+
+	static class ObservableMapStub extends ObservableMap {
+		/**
+		 * @param wrappedMap
+		 */
+		public ObservableMapStub(Map wrappedMap) {
+			super(wrappedMap);
+		}
+
+		protected void fireMapChange(MapDiff diff) {
+			super.fireMapChange(diff);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/WritableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/WritableMapTest.java
new file mode 100644
index 0000000..9190e25
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/map/WritableMapTest.java
@@ -0,0 +1,302 @@
+/*******************************************************************************
+ * Copyright (c) 2006-2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 184830, 233306
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.map;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.MapChangeEvent;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.map.WritableMap;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class WritableMapTest extends TestCase {
+	protected void setUp() throws Exception {
+		RealmTester.setDefault(new CurrentRealm(true));
+	}
+
+	protected void tearDown() throws Exception {
+		RealmTester.setDefault(null);
+	}
+
+	public void testPutRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableMap map = new WritableMap();
+				map.put("", "");
+			}
+		});
+	}
+
+	public void testRemoveRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableMap map = new WritableMap();
+				CurrentRealm realm = (CurrentRealm) Realm.getDefault();
+				boolean current = realm.isCurrent();
+				realm.setCurrent(true);
+				map.put("", "");
+				realm.setCurrent(current);
+
+				map.remove("");
+			}
+		});
+	}
+
+	public void testClearRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableMap map = new WritableMap();
+				map.clear();
+			}
+		});
+	}
+
+	public void testPutAllRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				WritableMap map = new WritableMap();
+				map.putAll(Collections.EMPTY_MAP);
+			}
+		});
+	}
+	
+	public void testPutWithExistingKeyMapChangeEvent() throws Exception {
+		WritableMap map = new WritableMap();
+		String key = "key";
+		String value = "value";
+		map.put(key, value);
+		
+		MapChangeEventTracker listener = new MapChangeEventTracker();
+		map.addMapChangeListener(listener);
+		
+		assertEquals(0, listener.count);
+		
+		String newValue = "new value";
+		map.put(key, newValue);
+		
+		assertEquals(1, listener.count);
+		MapChangeEvent event = listener.event;
+		
+		
+		Set changedKeys = event.diff.getChangedKeys();
+		assertEquals(1, changedKeys.size());
+		assertTrue(changedKeys.contains(key));
+		assertEquals(value, event.diff.getOldValue(key));
+		assertEquals(newValue, event.diff.getNewValue(key));
+	}
+
+	public void testPutSameValue_NoMapChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = new Object();
+		Object value = "value";
+		map.put(key, value);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		assertEquals(0, tracker.count);
+
+		Object equalValue = new String("value");
+		map.put(key, equalValue);
+
+		assertEquals(0, tracker.count);
+		
+	}
+
+	public void testPutNullKey_SingleAdditionChangeEvent() {
+		WritableMap map = new WritableMap();
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		assertEquals(0, tracker.count);
+
+		Object key = null;
+		Object value = new Object();
+		map.put(key, value);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.singleton(key), diff.getAddedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(value, diff.getNewValue(key));
+	}
+
+	public void testRemoveNullKey_SingleRemovalChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = null;
+		Object value = new Object();
+		map.put(key, value);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		map.remove(key);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.EMPTY_SET, diff.getAddedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getChangedKeys());
+		assertEquals(Collections.singleton(key), diff.getRemovedKeys());
+		assertEquals(value, diff.getOldValue(key));
+	}
+
+	public void testPutNullValue_SingleAdditionChangeEvent() {
+		WritableMap map = new WritableMap();
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		assertEquals(0, tracker.count);
+
+		Object key = new Object();
+		Object value = null;
+		map.put(key, value);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.singleton(key), diff.getAddedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(value, diff.getNewValue(key));
+	}
+
+	public void testPutNullOverNonNullValue_SingleChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = new Object();
+		Object oldValue = new Object();
+		map.put(key, oldValue);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		Object newValue = null;
+		map.put(key, newValue);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.EMPTY_SET, diff.getAddedKeys());
+		assertEquals(Collections.singleton(key), diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(oldValue, diff.getOldValue(key));
+		assertEquals(newValue, diff.getNewValue(key));
+	}
+
+	public void testPutNonNullOverNullValue_SingleChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = new Object();
+		Object oldValue = null;
+		map.put(key, oldValue);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		Object newValue = new Object();
+		map.put(key, newValue);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.EMPTY_SET, diff.getAddedKeys());
+		assertEquals(Collections.singleton(key), diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(oldValue, diff.getOldValue(key));
+		assertEquals(newValue, diff.getNewValue(key));
+	}
+
+	public void testRemoveNullValue_SingleRemovalChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = new Object();
+		Object value = null;
+		map.put(key, value);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		map.remove(key);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.EMPTY_SET, diff.getAddedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getChangedKeys());
+		assertEquals(Collections.singleton(key), diff.getRemovedKeys());
+		assertEquals(value, diff.getOldValue(key));
+	}
+
+	public void testPutAllNullValue_SingleAdditionChangeEvent() {
+		WritableMap map = new WritableMap();
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		Object key = new Object();
+		Object value = null;
+		Map other = new HashMap();
+		other.put(key, value);
+		map.putAll(other);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.singleton(key), diff.getAddedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(value, diff.getNewValue(key));
+	}
+
+	public void testPutAllNullValueToNonNullValue_SingleChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = new Object();
+		Object oldValue = null;
+		map.put(key, oldValue);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		Object newValue = new Object();
+		Map other = new HashMap();
+		other.put(key, newValue);
+		map.putAll(other);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.EMPTY_SET, diff.getAddedKeys());
+		assertEquals(Collections.singleton(key), diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(oldValue, diff.getOldValue(key));
+		assertEquals(newValue, diff.getNewValue(key));
+	}
+
+	public void testPutAllNonNullValueToNullValue_SingleChangeEvent() {
+		WritableMap map = new WritableMap();
+		Object key = new Object();
+		Object oldValue = new Object();
+		map.put(key, oldValue);
+
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		Object newValue = null;
+		Map other = new HashMap();
+		other.put(key, newValue);
+		map.putAll(other);
+
+		assertEquals(1, tracker.count);
+		MapDiff diff = tracker.event.diff;
+		assertEquals(Collections.EMPTY_SET, diff.getAddedKeys());
+		assertEquals(Collections.singleton(key), diff.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, diff.getRemovedKeys());
+		assertEquals(oldValue, diff.getOldValue(key));
+		assertEquals(newValue, diff.getNewValue(key));
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/AbstractObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/AbstractObservableSetTest.java
new file mode 100644
index 0000000..b52087f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/AbstractObservableSetTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.set;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.AbstractObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+
+/**
+ */
+public class AbstractObservableSetTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AbstractObservableSetTest.class.getName());
+		suite.addTest(ObservableCollectionContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		public void change(IObservable observable) {
+			((AbstractObservableSetStub) observable).fireSetChange(Diffs.createSetDiff(new HashSet(), new HashSet()));
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return Integer.toString(collection.size());
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			AbstractObservableSetStub set = new AbstractObservableSetStub(realm, String.class);
+			
+			for (int i = 0; i < elementCount; i++) {
+				set.getWrappedSet().add(Integer.toString(i));
+			}
+			
+			return set;
+		}
+	}
+	
+	private static class AbstractObservableSetStub extends AbstractObservableSet {
+		private Object type;
+		private HashSet set;
+		
+		private AbstractObservableSetStub(Realm realm, Object type) {
+			super (realm);
+			set = new HashSet();
+			this.type = type;
+		}
+		
+		protected Set getWrappedSet() {
+			return set;
+		}
+
+		public Object getElementType() {
+			return type;
+		}		
+		
+		protected void fireSetChange(SetDiff diff) {
+			super.fireSetChange(diff);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java
new file mode 100644
index 0000000..ead335c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * 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 237703)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.set;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+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.databinding.observable.set.ComputedSet;
+import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ComputedSetTest extends AbstractDefaultRealmTestCase {
+	ComputedSetStub set;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		set = new ComputedSetStub();
+		set.size(); // Force set to compute
+	}
+
+	public void testDependency_Staleness() {
+		assertFalse(set.isStale());
+		set.dependency.fireStale();
+		assertTrue(set.isStale());
+	}
+
+	public void testDependency_FiresSetChange() {
+		assertEquals(set.nextComputation, set);
+
+		Object element = new Object();
+		set.nextComputation.add(element);
+
+		set.dependency.fireChange();
+
+		assertEquals(Collections.singleton(element), set);
+	}
+
+	public void testDependency_NoStaleEventIfAlreadyDirty() {
+		set.dependency.fireChange();
+		set.addStaleListener(new IStaleListener() {
+			public void handleStale(StaleEvent staleEvent) {
+				fail("Should not fire stale when set is already dirty");
+			}
+		});
+		set.dependency.fireStale();
+	}
+
+	public void testDependency_SetChangeEventFiresOnlyWhenNotDirty() {
+		SetChangeEventTracker tracker = SetChangeEventTracker.observe(set);
+
+		set.dependency.fireChange();
+		assertEquals(
+				"ComputedSet should fire set change event when its dependency changes",
+				1, tracker.count);
+
+		set.dependency.fireChange();
+		assertEquals(
+				"ComputedSet should not fire set change events when dirty",
+				1, tracker.count);
+
+		set.size(); // Force set to recompute.
+		set.dependency.fireChange();
+		assertEquals(
+				"ComputedSet should fire set change event when its dependency changes",
+				2, tracker.count);
+	}
+
+	static class ComputedSetStub extends ComputedSet {
+		Set nextComputation = new HashSet();
+		ObservableStub dependency;
+
+		ComputedSetStub() {
+			this(Realm.getDefault());
+		}
+
+		ComputedSetStub(Realm realm) {
+			super(realm);
+			dependency = new ObservableStub(realm);
+		}
+
+		protected Set calculate() {
+			ObservableTracker.getterCalled(dependency);
+			return new HashSet(nextComputation);
+		}
+	}
+
+	static class ObservableStub extends AbstractObservable {
+		public ObservableStub(Realm realm) {
+			super(realm);
+		}
+
+		boolean stale;
+
+		public boolean isStale() {
+			return stale;
+		}
+
+		protected void fireStale() {
+			stale = true;
+			super.fireStale();
+		}
+
+		protected void fireChange() {
+			super.fireChange();
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ComputedSetTest.class.getName());
+		suite.addTestSuite(ComputedSetTest.class);
+		suite.addTest(ObservableCollectionContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			final ComputedSetStub set = new ComputedSetStub(realm);
+			for (int i = 0; i < elementCount; i++)
+				set.nextComputation.add(createElement(set));
+			set.size(); // force set to compute
+			return set;
+		}
+
+		public void change(IObservable observable) {
+			ComputedSetStub set = (ComputedSetStub) observable;
+			set.nextComputation.add(new Object());
+			set.dependency.fireChange();
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			if (stale)
+				((ComputedSetStub) observable).dependency.fireStale();
+			else {
+				ComputedSetStub computedSet = (ComputedSetStub) observable;
+				computedSet.dependency.stale = false;
+				computedSet.dependency.fireChange();
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/DecoratingObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/DecoratingObservableSetTest.java
new file mode 100644
index 0000000..7c7ae29
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/DecoratingObservableSetTest.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 208332)
+ *     Matthew Hall - bug 213145
+ *         (through ProxyObservableSetTest.java)
+ *     Matthew Hall - bug 237718
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.set;
+
+import java.util.Collections;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.DecoratingObservableSet;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.conformance.MutableObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DecoratingObservableSetTest {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DecoratingObservableSetTest.class
+				.getName());
+		suite.addTest(MutableObservableCollectionContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		private Object elementType = Object.class;
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableSet wrappedSet = new WritableSet(realm,
+					Collections.EMPTY_SET, elementType);
+			for (int i = 0; i < elementCount; i++)
+				wrappedSet.add(createElement(wrappedSet));
+			return new DecoratingObservableSetStub(wrappedSet);
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			DecoratingObservableSetStub set = (DecoratingObservableSetStub) observable;
+			set.wrappedSet.add(createElement(set));
+		}
+	}
+
+	static class DecoratingObservableSetStub extends DecoratingObservableSet {
+		IObservableSet wrappedSet;
+
+		DecoratingObservableSetStub(IObservableSet wrappedSet) {
+			super(wrappedSet, true);
+			this.wrappedSet = wrappedSet;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ObservableSetTest.java
new file mode 100755
index 0000000..2b545ed
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ObservableSetTest.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.set;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+
+/**
+ * @since 1.1
+ */
+public class ObservableSetTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ObservableSetTest.class.getName());
+		suite.addTest(ObservableCollectionContractTest.suite(new Delegate()));
+		return suite;
+	}
+	
+	private static class Delegate extends AbstractObservableCollectionContractDelegate {	
+		private Delegate() {	
+		}
+		
+		public void change(IObservable observable) {
+			((ObservableSetStub) observable).fireSetChange(Diffs.createSetDiff(new HashSet(), new HashSet()));
+		}
+		
+		public Object createElement(IObservableCollection collection) {
+			return Integer.toString(collection.size());
+		}
+	
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableSet set = new ObservableSetStub(realm, new HashSet(), String.class);
+			
+			for (int i = 0; i < elementCount; i++) {
+				set.add(Integer.toString(i));
+			}
+			
+			return set;
+		}
+	}
+
+	private static class ObservableSetStub extends ObservableSet {
+		/**
+		 * @param wrappedSet
+		 * @param elementType
+		 */
+		protected ObservableSetStub(Realm realm, Set wrappedSet, Object elementType) {
+			super(realm, wrappedSet, elementType);
+		}
+
+		public void fireSetChange(SetDiff diff) {
+			super.fireSetChange(diff);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/UnionSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/UnionSetTest.java
new file mode 100755
index 0000000..64a2ac3
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/UnionSetTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.UnionSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+
+/**
+ */
+public class UnionSetTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UnionSetTest.class.getName());
+		suite.addTest(ObservableCollectionContractTest.suite(new Delegate()));
+		return suite;
+	}
+	
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		private IObservableSet[] sets;
+
+		private Delegate() {
+		}
+
+		public void setUp() {
+			
+			super.setUp();
+		}
+
+		public void tearDown() {
+			sets = null;
+
+			super.tearDown();
+		}
+
+		public void change(IObservable observable) {
+			sets[0].add(Integer.toString(sets[0].size()));
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return Integer.toString(collection.size());
+		}
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {			
+			sets = new IObservableSet[]{new WritableSet(realm), new WritableSet(realm)};
+			
+			IObservableSet set = new UnionSet(sets);
+
+			for (int i = 0; i < elementCount; i++) {
+				sets[0].add(Integer.toString(i));
+			}
+
+			return set;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/WritableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/WritableSetTest.java
new file mode 100644
index 0000000..f23f315
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/WritableSetTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2007-2008 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 213145
+ ******************************************************************************/
+package org.eclipse.core.tests.databinding.observable.set;
+
+import java.util.Collections;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ */
+public class WritableSetTest extends AbstractDefaultRealmTestCase {
+	public void testWithElementType() throws Exception {
+		Object elementType = String.class;
+		WritableSet set = WritableSet.withElementType(elementType);
+		assertNotNull(set);
+		assertEquals(Realm.getDefault(), set.getRealm());
+		assertEquals(elementType, set.getElementType());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(WritableSetTest.class.getName());
+		suite.addTestSuite(WritableSetTest.class);
+		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		private Delegate() {
+			super();
+		}
+
+		public void change(IObservable observable) {
+			IObservableSet set = (IObservableSet) observable;
+			set.add(createElement(set));
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableSet set = new WritableSet(realm, Collections.EMPTY_SET,
+					String.class);
+			for (int i = 0; i < elementCount; i++) {
+				set.add(createElement(set));
+			}
+
+			return set;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/AbstractObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/AbstractObservableValueTest.java
new file mode 100755
index 0000000..9c1841e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/AbstractObservableValueTest.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ */
+public class AbstractObservableValueTest extends TestCase {
+	public void testSetValueRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				ObservableValueStub observable = new ObservableValueStub();
+				try {
+					observable.setValue(null);
+				} catch (UnsupportedOperationException e) {
+					// do nothing
+				}
+			}
+		});
+	}
+
+	public void testSetValueInvokesDoSetValue() throws Exception {
+		class ValueStub extends ObservableValueStub {
+			int doSetValue;
+
+			ValueStub(Realm realm) {
+				super(realm);
+			}
+
+			protected void doSetValue(Object value) {
+				doSetValue++;
+			}
+		}
+
+		Realm realm = new CurrentRealm(true);
+		ValueStub stub = new ValueStub(realm);
+		assertEquals(0, stub.doSetValue);
+		stub.setValue(new Object());
+		assertEquals("doSetValue should have been invoked", 1, stub.doSetValue);
+	}
+
+	public void testFireValueChangeRealmChecks() throws Exception {
+		RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				ObservableValueStub observable = new ObservableValueStub();
+				observable.fireValueChange(null);
+			}
+		});
+	}
+
+	private static class ObservableValueStub extends AbstractObservableValue {
+		ObservableValueStub() {
+			super(Realm.getDefault());
+		}
+
+		private ObservableValueStub(Realm realm) {
+			super(realm);
+		}
+
+		protected Object doGetValue() {
+			return null;
+		}
+
+		public Object getValueType() {
+			return null;
+		}
+
+		protected void fireValueChange(ValueDiff diff) {
+			super.fireValueChange(diff);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/AbstractVetoableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/AbstractVetoableValueTest.java
new file mode 100755
index 0000000..d0130fa
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/AbstractVetoableValueTest.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractVetoableValue;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+
+/**
+ * @since 3.2
+ */
+public class AbstractVetoableValueTest extends TestCase {
+    public void testSetValueInvokesDoSetApprovedValue() throws Exception {
+        class VetoableValue extends VetoableValueStub {
+            int count;
+            Object value;
+            
+            VetoableValue(Realm realm) {
+                super(realm);
+            }
+            
+            protected void doSetApprovedValue(Object value) {
+                count++;
+                this.value = value;
+            }      
+        }
+        
+        Realm realm = new CurrentRealm(true);
+        VetoableValue vetoableValue = new VetoableValue(realm);
+        assertEquals(0, vetoableValue.count);
+        assertEquals(null, vetoableValue.value);
+        
+        Object value = new Object();
+        vetoableValue.setValue(value);
+        assertEquals(1, vetoableValue.count);
+        assertEquals(value, vetoableValue.value);
+    }
+    
+    public void testFireValueChangeRealmChecks() throws Exception {
+    	RealmTester.exerciseCurrent(new Runnable() {
+			public void run() {
+				VetoableValueStub observable = new VetoableValueStub();
+				observable.fireValueChanging(null);
+			}
+    	});
+	}
+    
+    private static class VetoableValueStub extends AbstractVetoableValue {
+    	VetoableValueStub() {
+    		this(Realm.getDefault());
+    	}
+    	
+    	VetoableValueStub(Realm realm) {
+    		super(realm);
+    	}
+    	
+		protected void doSetApprovedValue(Object value) {
+		}
+
+		protected Object doGetValue() {
+			return null;
+		}
+
+
+		public Object getValueType() {
+			return null;
+		}    	
+		
+		protected boolean fireValueChanging(ValueDiff diff) {
+			return super.fireValueChanging(diff);
+		}
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java
new file mode 100755
index 0000000..8d90011
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * 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 116920
+ *     Brad Reynolds - bug 164653
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.0
+ * 
+ */
+public class ComputedValueTest extends AbstractDefaultRealmTestCase {
+    public void testValueType() throws Exception {
+        ComputedValue cv = new ComputedValue(Integer.TYPE) {
+            protected Object calculate() {
+                return new Integer(42);
+            }
+        };
+        assertEquals("value type should be the type that was set", Integer.TYPE, cv.getValueType());
+
+        cv = new ComputedValue() {
+            protected Object calculate() {
+                // TODO Auto-generated method stub
+                return null;
+            }
+        };
+
+        assertNull(cv.getValueType());
+    }
+
+    public void test_getValue() throws Exception {
+        ComputedValue cv = new ComputedValue() {
+            protected Object calculate() {
+                return new Integer(42);
+            }
+        };
+        assertEquals("Calculated value should be 42", new Integer(42), cv.getValue());
+    }
+
+    public void testDependencyValueChange() throws Exception {
+        final WritableValue value = new WritableValue(new Integer(42), Integer.TYPE);
+
+        ComputedValue cv = new ComputedValue() {
+            protected Object calculate() {
+                return value.getValue();
+            }
+        };
+
+        assertEquals("calculated value should have been that of the writable value", value.getValue(), cv.getValue());
+
+        value.setValue(new Integer(44));
+
+        assertEquals("calculated value should have been that of the writable value", value.getValue(), cv.getValue());
+    }
+
+    private static class WritableValueExt extends WritableValue {
+        public WritableValueExt(Object valueType, Object initialValue) {
+            super(initialValue, valueType);
+        }
+
+        public boolean hasListeners() {
+            return super.hasListeners();
+        }
+    }
+
+    public void testHookAndUnhookDependantObservables() throws Exception {
+        final List values = new ArrayList();
+
+        ComputedValue cv = new ComputedValue() {
+            protected Object calculate() {
+                int sum = 0;
+                for (Iterator it = values.iterator(); it.hasNext();) {
+                    WritableValue value = (WritableValue) it.next();
+                    sum += ((Integer) value.getValue()).intValue();
+
+                }
+
+                return new Integer(sum);
+            }
+        };
+
+        WritableValueExt value1 = new WritableValueExt(Integer.TYPE, new Integer(1));
+        WritableValueExt value2 = new WritableValueExt(Integer.TYPE, new Integer(1));
+        values.add(value1);
+        values.add(value2);
+        
+        assertFalse(value1.hasListeners());
+        assertFalse(value2.hasListeners());
+        cv.getValue();
+        assertTrue(value1.hasListeners());
+        assertTrue(value2.hasListeners());
+        
+        //force the computed value to be stale
+        value2.setValue(new Integer(2));
+        //remove value2 from the values that are used to compute the value
+        values.remove(value2);
+        
+        //force the value to be computed
+        cv.getValue();
+        assertEquals(new Integer(1), cv.getValue());
+        assertTrue(value1.hasListeners());
+        assertFalse("because value2 is not a part of the calculation the listeners should have been removed", value2.hasListeners());
+    }
+    
+    public void testSetValueUnsupportedOperationException() throws Exception {
+        ComputedValue cv = new ComputedValue() {
+            protected Object calculate() {
+                return null;
+            }
+        };
+        
+        try {
+            cv.setValue(new Object());
+            fail("exception should have been thrown");
+        } catch (UnsupportedOperationException e) {
+        }
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DateAndTimeObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DateAndTimeObservableValueTest.java
new file mode 100644
index 0000000..c3f61cd
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DateAndTimeObservableValueTest.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 169876)
+ *     Matthew Hall - bug 271720
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.core.databinding.observable.value.DateAndTimeObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.0
+ * 
+ */
+public class DateAndTimeObservableValueTest extends
+		AbstractDefaultRealmTestCase {
+	private IObservableValue date;
+	private IObservableValue time;
+	private IObservableValue dateAndTime;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		date = WritableValue.withValueType(Date.class);
+		time = WritableValue.withValueType(Date.class);
+
+		dateAndTime = new DateAndTimeObservableValue(date, time);
+	}
+
+	private static Date date(int year, int month, int day) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.clear();
+		calendar.set(Calendar.YEAR, year);
+		calendar.set(Calendar.MONTH, month - 1); // Calendar.JANUARY == 0
+		calendar.set(Calendar.DAY_OF_MONTH, day);
+		return calendar.getTime();
+	}
+
+	private static Date time(int hour, int minute, int second) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.clear();
+		calendar.set(Calendar.HOUR_OF_DAY, hour);
+		calendar.set(Calendar.MINUTE, minute);
+		calendar.set(Calendar.SECOND, second);
+		return calendar.getTime();
+	}
+
+	private static Date timestamp(int year, int month, int day, int hour,
+			int minute, int second) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.clear();
+		calendar.set(year, month - 1, day, hour, minute, second);
+		return calendar.getTime();
+	}
+
+	public void testGetValue_NullDateNullResult() {
+		date.setValue(null);
+		time.setValue(time(12, 27, 17));
+		assertNull(dateAndTime.getValue());
+	}
+
+	public void testGetValue_NullTimeClearsTime() {
+		date.setValue(date(2009, 3, 3));
+		time.setValue(null);
+		assertEquals(timestamp(2009, 3, 3, 0, 0, 0), dateAndTime.getValue());
+	}
+
+	public void testGetValue() {
+		date.setValue(timestamp(2009, 3, 3, 12, 30, 33));
+
+		time.setValue(timestamp(1999, 12, 31, 23, 59, 59)); // apocalypse - 1s
+
+		assertEquals(timestamp(2009, 3, 3, 23, 59, 59), dateAndTime.getValue());
+	}
+
+	public void testSetValue() {
+		date.setValue(date(2009, 3, 3));
+		time.setValue(time(12, 32, 55));
+
+		dateAndTime.setValue(timestamp(2010, 1, 1, 2, 3, 5));
+
+		assertEquals(date(2010, 1, 1), date.getValue());
+		assertEquals(time(2, 3, 5), time.getValue());
+	}
+
+	public void testSetValue_NullNullsDateClearsTime() {
+		date.setValue(date(2009, 3, 3));
+		time.setValue(time(12, 25, 34));
+
+		dateAndTime.setValue(null);
+		assertEquals(null, date.getValue());
+		assertEquals(time(0, 0, 0), time.getValue());
+	}
+
+	public void testSetValue_PreserveTimeOfDateAndDateOfTime() {
+		date.setValue(timestamp(2009, 3, 3, 12, 32, 55));
+		time.setValue(timestamp(2009, 3, 3, 12, 32, 55));
+
+		dateAndTime.setValue(timestamp(2010, 1, 1, 2, 3, 5));
+
+		assertEquals(timestamp(2010, 1, 1, 12, 32, 55), date.getValue());
+		assertEquals(timestamp(2009, 3, 3, 2, 3, 5), time.getValue());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DecoratingObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DecoratingObservableValueTest.java
new file mode 100644
index 0000000..6ea9056
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DecoratingObservableValueTest.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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 237718)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.DecoratingObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DecoratingObservableValueTest {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DecoratingObservableValueTest.class
+				.getName());
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		private Object valueType = Object.class;
+
+		public IObservableValue createObservableValue(Realm realm) {
+			IObservableValue decorated = new WritableValue(realm, new Object(),
+					valueType);
+			return new DecoratingObservableValueStub(decorated);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return valueType;
+		}
+
+		public void change(IObservable observable) {
+			((DecoratingObservableValueStub) observable).decorated
+					.setValue(new Object());
+		}
+	}
+
+	static class DecoratingObservableValueStub extends
+			DecoratingObservableValue {
+		IObservableValue decorated;
+
+		DecoratingObservableValueStub(IObservableValue decorated) {
+			super(decorated, true);
+			this.decorated = decorated;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DuplexingObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DuplexingObservableValueTest.java
new file mode 100644
index 0000000..a9ae326
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/DuplexingObservableValueTest.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 173735)
+ *     Matthew Hall - bug 262407
+ *******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.DuplexingObservableValue;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.0
+ * 
+ */
+public class DuplexingObservableValueTest extends AbstractDefaultRealmTestCase {
+	private IObservableList list;
+	private DuplexingObservableValue observable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		list = new WritableList(new ArrayList(), String.class);
+	}
+
+	public void testValueType_InheritFromTargetList() throws Exception {
+		observable = new DuplexingObservableValue(list) {
+			protected Object coalesceElements(Collection elements) {
+				return null;
+			}
+		};
+		assertEquals(
+				"value type should be the element type of the target list",
+				String.class, observable.getValueType());
+	}
+
+	public void testValueType_ProvidedInConstructor() throws Exception {
+		observable = new DuplexingObservableValue(list, Object.class) {
+			protected Object coalesceElements(Collection elements) {
+				return null;
+			}
+		};
+		assertEquals("value type should be the type passed to constructor",
+				Object.class, observable.getValueType());
+	}
+
+	public void test_getValue() throws Exception {
+		observable = DuplexingObservableValue.withDefaults(list, null,
+				"<Multiple Values>");
+		assertNull(observable.getValue());
+		list.add("42");
+		assertEquals("Value should be \"42\"", "42", observable.getValue());
+		list.add("42");
+		assertEquals("Value should be \"42\"", "42", observable.getValue());
+		list.add("watermelon");
+		assertEquals("<Multiple Values>", observable.getValue());
+		list.remove(2);
+		assertEquals("Value should be \"42\"", "42", observable.getValue());
+		list.clear();
+		assertNull(observable.getValue());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/SelectObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/SelectObservableValueTest.java
new file mode 100644
index 0000000..61b7a2f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/SelectObservableValueTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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 249992)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.SelectObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class SelectObservableValueTest extends AbstractDefaultRealmTestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SelectObservableValueTest.class
+				.getName());
+		// suite.addTestSuite(SelectObservableValueValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		public IObservableValue createObservableValue(Realm realm) {
+			return new SelectObservableValue(realm, Object.class);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Object.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			SelectObservableValue select = (SelectObservableValue) observable;
+			Object value = new Object();
+			IObservableValue optionObservable = new WritableValue(select
+					.getRealm(), Boolean.FALSE, Boolean.TYPE);
+			select.addOption(value, optionObservable);
+			return value;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/WritableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/WritableValueTest.java
new file mode 100755
index 0000000..6bec2a2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/WritableValueTest.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 Brad Reynolds
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bug 158687
+ *     Brad Reynolds - bug 164653
+ *     Matthew Hall - bug 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.observable.value;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 3.2
+ */
+public class WritableValueTest extends AbstractDefaultRealmTestCase {
+	/**
+	 * All constructors delegate to the 3 arg constructor.
+	 * 
+	 * @throws Exception
+	 */
+	public void testConstructor() throws Exception {
+		WritableValue value = new WritableValue(SWTObservables.getRealm(Display
+				.getDefault()));
+		assertNull(value.getValue());
+		assertNull(value.getValueType());
+	}
+
+	public void testWithValueType() throws Exception {
+		Object elementType = String.class;
+		WritableValue value = WritableValue.withValueType(elementType);
+		assertNotNull(value);
+		assertEquals(Realm.getDefault(), value.getRealm());
+		assertEquals(elementType, value.getValueType());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(WritableValueTest.class.getName());
+		suite.addTestSuite(WritableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		public IObservableValue createObservableValue(Realm realm) {
+			return new WritableValue(realm, "", String.class);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+		
+		public Object createValue(IObservableValue observable) {
+			return observable.getValue() + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/util/PolicyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/util/PolicyTest.java
new file mode 100644
index 0000000..c98857b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/util/PolicyTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 258774, 260316
+ ******************************************************************************/
+package org.eclipse.core.tests.databinding.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.util.ILogger;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class PolicyTest extends TestCase {
+
+	public void testConstructor() {
+		// cover the constructor too
+		new Policy();
+	}
+
+	public void testDummyLog() {
+		ILogger oldLog = Policy.getLog();
+		PrintStream oldErr = System.err;
+		try {
+			// this should reset to using dummy log
+			Policy.setLog(null);
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+			PrintStream ps = new PrintStream(os);
+			System.setErr(ps);
+			IStatus status = new Status(IStatus.CANCEL, "somePluginId",
+					"someMessage", new RuntimeException());
+			Policy.getLog().log(status);
+			ps.flush();
+			String message = new String(os.toByteArray());
+			System.out.println("testDummyLog message: " + message);
+			assertTrue("expecting severity", message.indexOf("CANCEL") != -1);
+			assertTrue("expecting plugin id",
+					message.indexOf("somePluginId") != -1);
+			assertTrue("expecting message",
+					message.indexOf("someMessage") != -1);
+			assertTrue("expecting RuntimeException", message
+					.indexOf("RuntimeException") != -1);
+		} finally {
+			Policy.setLog(oldLog);
+			System.setErr(oldErr);
+		}
+	}
+
+	public void testCustomLog() {
+		ILogger oldLog = Policy.getLog();
+		try {
+			final IStatus[] statusHolder = new IStatus[1];
+			Policy.setLog(new ILogger() {
+				public void log(IStatus status) {
+					statusHolder[0] = status;
+				}
+			});
+			IStatus status = new Status(IStatus.CANCEL, "somePluginId",
+					"someMessage", new RuntimeException());
+			Policy.getLog().log(status);
+			assertEquals(status, statusHolder[0]);
+		} finally {
+			Policy.setLog(oldLog);
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/validation/MultiValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/validation/MultiValidatorTest.java
new file mode 100644
index 0000000..4506a57
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/validation/MultiValidatorTest.java
@@ -0,0 +1,449 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bug 237884, 251003
+ *     Ovidio Mallo - bugs 240590, 238909, 251003, 247741, 235859
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.validation;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.AbstractObservable;
+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.ObservableTracker;
+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.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.validation.MultiValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableValue;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class MultiValidatorTest extends AbstractDefaultRealmTestCase {
+	private DependencyObservableValue dependency;
+	private MultiValidator validator;
+	private IObservableValue validationStatus;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		dependency = new DependencyObservableValue(null, IStatus.class);
+		validator = new MultiValidator() {
+			protected IStatus validate() {
+				return (IStatus) dependency.getValue();
+			}
+		};
+		validationStatus = validator.getValidationStatus();
+	}
+
+	public void testConstructor_NullArgument() {
+		try {
+			new MultiValidator(null) {
+				protected IStatus validate() {
+					return null;
+				}
+			};
+			fail("Expected AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testGetValidationStatus_NullResultYieldsOKStatus() {
+		IStatus status = (IStatus) validationStatus.getValue();
+		assertTrue(status.isOK()); // null -> OK
+	}
+
+	public void testGetValidationStatus_ExceptionThrownYieldsErrorStatus() {
+		final RuntimeException e = new RuntimeException("message");
+		validator = new MultiValidator() {
+			protected IStatus validate() {
+				throw e;
+			}
+		};
+		assertEquals(ValidationStatus.error("message", e), validator
+				.getValidationStatus().getValue());
+	}
+
+	public void testGetValidationStatus_TracksWithDependency() {
+		IStatus newStatus = ValidationStatus.error("error");
+		dependency.setValue(newStatus);
+		assertEquals(newStatus, validationStatus.getValue());
+	}
+
+	public void testInit_AddsValidationProducer() {
+		DataBindingContext dbc = new DataBindingContext();
+		dbc.addValidationStatusProvider(validator);
+		assertTrue(dbc.getValidationStatusProviders().contains(validator));
+	}
+
+	public void testObserveValidatedValue_NullArgument() {
+		try {
+			validator.observeValidatedValue(null);
+			fail("Expected AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testObserveValidatedValue_WrongRealm() {
+		Realm otherRealm = new CurrentRealm(true);
+		try {
+			validator.observeValidatedValue(new WritableValue(otherRealm));
+			fail("Expected AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testObserveValidatedValue_ReturnValue() {
+		WritableValue target = new WritableValue();
+		ValidatedObservableValue validated = (ValidatedObservableValue) validator
+				.observeValidatedValue(target);
+
+		target.setValue(new Object());
+		assertEquals(target.getValue(), validated.getValue());
+
+		dependency.setValue(ValidationStatus.error("error"));
+		assertFalse(validated.isStale());
+
+		target.setValue(new Object());
+		assertTrue(validated.isStale());
+		assertFalse(target.getValue().equals(validated.getValue()));
+
+		dependency.setValue(ValidationStatus.info("info")); // considered valid
+		assertEquals(target.getValue(), validated.getValue());
+		assertFalse(validated.isStale());
+	}
+
+	public void testBug237884_DisposeCausesNPE() {
+		MultiValidator validator = new MultiValidator() {
+			protected IStatus validate() {
+				return ValidationStatus.ok();
+			}
+		};
+		try {
+			validator.dispose();
+		} catch (NullPointerException e) {
+			fail("Bug 237884: MultiValidator.dispose() causes NPE");
+		}
+	}
+
+	public void testBug237884_MultipleDispose() {
+		validator.dispose();
+		validator.dispose();
+	}
+
+	public void testBug237884_Comment3_ValidationStatusAsDependencyCausesStackOverflow() {
+		dependency = new DependencyObservableValue(new Object(), Object.class);
+		validator = new MultiValidator() {
+			private int counter;
+
+			protected IStatus validate() {
+				ObservableTracker.getterCalled(dependency);
+				return ValidationStatus.info("info " + counter++);
+			}
+		};
+		validationStatus = validator.getValidationStatus();
+
+		// bug behavior: the validation status listener causes the validation
+		// status observable to become a dependency of the validator.
+		validationStatus.addChangeListener(new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				ObservableTracker.getterCalled(validationStatus);
+			}
+		});
+		dependency.setValue(new Object());
+
+		// at this point, because the validation status observable is a
+		// dependency, changes to the validation status cause revalidation in an
+		// infinite recursion.
+		try {
+			dependency.setValue(new Object());
+		} catch (StackOverflowError e) {
+			fail("Bug 237884: Accessing MultiValidator validation status from within listener "
+					+ "causes infinite recursion");
+		}
+	}
+
+	public void testBug237884_ValidationStatusListenerCausesLoopingDependency() {
+		validationStatus.addChangeListener(new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				ObservableTracker.getterCalled(validationStatus);
+			}
+		});
+		assertFalse(validator.getTargets().contains(validationStatus));
+		// trigger revalidation
+		dependency.setValue(ValidationStatus.info("info"));
+		assertFalse(validator.getTargets().contains(validationStatus));
+	}
+
+	public void testRevalidate() {
+		// Use this as an easy way to inject a validation status into the
+		// validator without using an observable value.
+		final IStatus[] status = new IStatus[] { ValidationStatus.ok() };
+
+		class MyMultiValidator extends MultiValidator {
+			protected IStatus validate() {
+				return status[0];
+			}
+
+			protected void callRevalidate() {
+				revalidate();
+			}
+		}
+
+		MyMultiValidator validator = new MyMultiValidator();
+
+		// Initially, the validation status should always be in sync.
+		assertSame(status[0], validator.getValidationStatus().getValue());
+
+		// When the validation status depends on something different than the
+		// IObservable dependency set, the MultiValidator cannot track those
+		// changes automatically so the validation status will get inconsistent
+		// without further ado.
+		status[0] = ValidationStatus.error("");
+		assertNotSame(status[0], validator.getValidationStatus().getValue());
+
+		// By calling makeDirty(), the validation status should be updated.
+		validator.callRevalidate();
+		assertSame(status[0], validator.getValidationStatus().getValue());
+	}
+
+	public void testBug237884_ValidationStatusAccessDuringValidationCausesLoopingDependency() {
+		validator = new MultiValidator() {
+			protected IStatus validate() {
+				ObservableTracker.getterCalled(getValidationStatus());
+				return (IStatus) dependency.getValue();
+			}
+		};
+		// trigger revalidation
+		dependency.setValue(ValidationStatus.info("info"));
+		assertFalse(validator.getTargets().contains(validationStatus));
+	}
+
+	public void testBug240590_ValidationStatusSetWhileTrackingDependencies() {
+		final IObservableValue noDependency = new WritableValue();
+		validationStatus.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				// Explicitly track the faked dependency.
+				ObservableTracker.getterCalled(noDependency);
+			}
+		});
+
+		// Trigger a validation change.
+		dependency.setValue(ValidationStatus.error("new error"));
+
+		// Make sure the faked dependency has not been included in the
+		// dependency set (the validator's targets).
+		assertFalse(validator.getTargets().contains(noDependency));
+	}
+
+	public void testValidationStaleness() {
+		ValueChangeEventTracker validationChangeCounter = ValueChangeEventTracker
+				.observe(validationStatus);
+
+		StaleEventTracker validationStaleCounter = StaleEventTracker
+				.observe(validationStatus);
+
+		// Assert initial state.
+		assertFalse(validationStatus.isStale());
+		assertEquals(0, validationChangeCounter.count);
+		assertEquals(0, validationStaleCounter.count);
+
+		// Change to a stale state.
+		dependency.setStale(true);
+		assertTrue(validationStatus.isStale());
+		assertEquals(0, validationChangeCounter.count);
+		assertEquals(1, validationStaleCounter.count); // +1
+
+		// The validation status is already stale so even if it gets another
+		// stale event from its dependencies, it should not propagate that
+		// event.
+		dependency.fireStale();
+		assertTrue(validationStatus.isStale());
+		assertEquals(0, validationChangeCounter.count);
+		assertEquals(1, validationStaleCounter.count);
+
+		// Change the validation status while remaining stale.
+		dependency.setValue(ValidationStatus.error("e1"));
+		assertTrue(validationStatus.isStale());
+		assertEquals(1, validationChangeCounter.count); // +1
+		assertEquals(1, validationStaleCounter.count);
+
+		// Move back to a non-stale state.
+		dependency.setStale(false);
+		assertFalse(dependency.isStale());
+		assertFalse(validationStatus.isStale());
+		assertEquals(2, validationChangeCounter.count); // +1
+		assertEquals(1, validationStaleCounter.count);
+	}
+
+	public void testStatusValueChangeWhileValidationStale() {
+		// Change to a stale state.
+		dependency.setStale(true);
+		assertTrue(validationStatus.isStale());
+
+		// Even if the validation is stale, we want the current value to be
+		// tracked.
+		dependency.setValue(ValidationStatus.error("e1"));
+		assertTrue(validationStatus.isStale());
+		assertEquals(dependency.getValue(), validationStatus.getValue());
+		dependency.setValue(ValidationStatus.error("e2"));
+		assertTrue(validationStatus.isStale());
+		assertEquals(dependency.getValue(), validationStatus.getValue());
+	}
+
+	public void testValidationStatusBecomesStaleThroughNewDependency() {
+		final DependencyObservableValue nonStaleDependency = new DependencyObservableValue(
+				ValidationStatus.ok(), IStatus.class);
+		nonStaleDependency.setStale(false);
+
+		final DependencyObservableValue staleDependency = new DependencyObservableValue(
+				ValidationStatus.ok(), IStatus.class);
+		staleDependency.setStale(true);
+
+		validator = new MultiValidator() {
+			protected IStatus validate() {
+				if (nonStaleDependency.getValue() != null) {
+					return (IStatus) nonStaleDependency.getValue();
+				}
+				return (IStatus) staleDependency.getValue();
+			}
+		};
+		validationStatus = validator.getValidationStatus();
+
+		assertFalse(validationStatus.isStale());
+
+		StaleEventTracker validationStaleCounter = StaleEventTracker
+				.observe(validationStatus);
+		assertEquals(0, validationStaleCounter.count);
+
+		// Setting the status of the non-stale dependency to null leads to the
+		// new stale dependency being accessed which in turn should trigger a
+		// stale event.
+		nonStaleDependency.setValue(null);
+		assertTrue(validationStatus.isStale());
+		assertEquals(1, validationStaleCounter.count);
+	}
+
+	public void testBug251003_CompareDependenciesByIdentity() {
+		DependencyObservable dependency1 = new DependencyObservable();
+		DependencyObservable dependency2 = new DependencyObservable();
+		assertEquals(dependency1, dependency2);
+		assertNotSame(dependency1, dependency2);
+
+		final List dependencies = new ArrayList();
+		dependencies.add(dependency1);
+		validator = new MultiValidator() {
+			protected IStatus validate() {
+				for (Iterator it = dependencies.iterator(); it.hasNext();)
+					ObservableTracker.getterCalled((IObservable) it.next());
+				return null;
+			}
+		};
+
+		// force init validation
+		validationStatus = validator.getValidationStatus();
+
+		IObservableList targets = validator.getTargets();
+		assertEquals(1, targets.size());
+		assertSame(dependency1, targets.get(0));
+
+		dependencies.set(0, dependency2);
+		dependency1.fireChange(); // force revalidate
+
+		assertEquals(1, targets.size());
+		assertSame(dependency2, targets.get(0));
+	}
+
+	public void testBug251003_MissingDependencies() {
+		final WritableList emptyListDependency = new WritableList();
+		validator = new MultiValidator() {
+			protected IStatus validate() {
+				ObservableTracker.getterCalled(emptyListDependency);
+				return null;
+			}
+		};
+
+		// Make sure the validation above is really triggered.
+		validator.getValidationStatus().getValue();
+
+		// emptyListDependency should be included in the dependency set.
+		assertTrue(validator.getTargets().contains(emptyListDependency));
+	}
+
+	private static class DependencyObservableValue extends WritableValue {
+		private boolean stale = false;
+
+		public DependencyObservableValue(Object initialValue, Object valueType) {
+			super(initialValue, valueType);
+		}
+
+		public boolean isStale() {
+			ObservableTracker.getterCalled(this);
+			return stale;
+		}
+
+		public void setStale(boolean stale) {
+			if (this.stale != stale) {
+				this.stale = stale;
+				if (stale) {
+					fireStale();
+				} else {
+					fireValueChange(Diffs.createValueDiff(doGetValue(),
+							doGetValue()));
+				}
+			}
+		}
+
+		protected void fireStale() {
+			super.fireStale();
+		}
+	}
+
+	private static class DependencyObservable extends AbstractObservable {
+		public DependencyObservable() {
+			super(Realm.getDefault());
+		}
+
+		public boolean isStale() {
+			return false;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj == null)
+				return false;
+			return getClass() == obj.getClass();
+		}
+
+		public int hashCode() {
+			return getClass().hashCode();
+		}
+
+		protected void fireChange() {
+			// TODO Auto-generated method stub
+			super.fireChange();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/validation/ValidationStatusTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/validation/ValidationStatusTest.java
new file mode 100644
index 0000000..b6f831e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/validation/ValidationStatusTest.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.databinding.validation;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.1
+ */
+public class ValidationStatusTest extends TestCase {
+	public void testEqualsAndHashCode() throws Exception {
+		String message = "error";
+		Exception e = new IllegalArgumentException();
+		IStatus status1 = ValidationStatus.error(message, e);
+		IStatus status2 = ValidationStatus.error(message, e);
+		
+		assertEquals(status1, status2);
+		assertEquals(status1.hashCode(), status2.hashCode());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java
new file mode 100644
index 0000000..5e6b516
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingMessagesTest.java
@@ -0,0 +1,33 @@
+package org.eclipse.core.tests.internal.databinding;
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.BindingMessages;
+
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+/**
+ * @since 3.2
+ *
+ */
+public class BindingMessagesTest extends TestCase {
+	public void testFormatString() throws Exception {
+		String key = "Validate_NumberOutOfRangeError";
+		String result = BindingMessages.formatString(key, new Object[] {"1", "2"});
+		assertFalse("key should not be returned", key.equals(result));
+	}
+	
+	public void testFormatStringForKeyNotFound() throws Exception {
+		String key = "key_that_does_not_exist";
+		String result = BindingMessages.formatString(key, null);
+		assertTrue(key.equals(result));
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingStatusTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingStatusTest.java
new file mode 100644
index 0000000..4e73204
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/BindingStatusTest.java
@@ -0,0 +1,123 @@
+package org.eclipse.core.tests.internal.databinding;
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.BindingStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+/**
+ * @since 1.1
+ */
+public class BindingStatusTest extends TestCase {
+	private BindingStatus bindingStatus;
+
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		bindingStatus = BindingStatus.ok();
+	}
+	
+	public void testMessageIsFromStatus() throws Exception {
+		String message = "error message";
+		IStatus status = ValidationStatus.error(message);
+		
+		bindingStatus.add(status);
+		assertEquals(message, bindingStatus.getMessage());
+	}
+	
+	public void testExceptionIsFromStatus() throws Exception {
+		IllegalArgumentException e = new IllegalArgumentException();
+		Status status = new Status(0, Policy.JFACE_DATABINDING, 0, "", e);
+		
+		bindingStatus.add(status);
+		assertEquals(e, bindingStatus.getException());
+	}
+	
+	public void testPluginIsFromStatus() throws Exception {
+		String plugin = "test";
+		Status status = new Status(0, plugin, 0, "", null);
+		
+		bindingStatus.add(status);
+		assertEquals(plugin, bindingStatus.getPlugin());
+	}
+	
+	public void testCodeIsFromStatus() throws Exception {
+		int code = 1;
+		Status status = new Status(0, Policy.JFACE_DATABINDING, code, "", null);
+		
+		bindingStatus.add(status);
+		assertEquals(code, status.getCode());
+	}
+	
+	public void testSeverityIsFromStatus() throws Exception {
+		IStatus status = ValidationStatus.error("");
+		
+		bindingStatus.add(status);
+		assertEquals(IStatus.ERROR, status.getSeverity());
+	}
+	
+	public void testLowerSeverityDoesNotOverwriteGreaterSeverity() throws Exception {
+		String info = "info";
+		String error = "error";
+		
+		bindingStatus.add(ValidationStatus.error(error));
+		assertEquals(IStatus.ERROR, bindingStatus.getSeverity());
+		assertEquals(error, bindingStatus.getMessage());
+		
+		bindingStatus.add(ValidationStatus.info(info));
+		assertEquals(IStatus.ERROR, bindingStatus.getSeverity());
+		assertEquals(error, bindingStatus.getMessage());
+		
+		IStatus[] children = bindingStatus.getChildren();
+		assertEquals(2, children.length);
+		assertEquals(IStatus.ERROR, children[0].getSeverity());
+		assertEquals(IStatus.INFO, children[1].getSeverity());
+	}
+	
+	public void testEqual() throws Exception {
+		BindingStatus status1 = BindingStatus.ok();
+		BindingStatus status2 = BindingStatus.ok();
+		
+		assertEquals(status1, status2);
+	}
+	
+	public void testNotEqual() throws Exception {
+		BindingStatus status1 = BindingStatus.ok();
+		BindingStatus status2 = BindingStatus.ok();
+		
+		status2.add(ValidationStatus.error(""));
+		assertFalse(status1.equals(status2));
+	}
+	
+	public void testHashCode() throws Exception {
+		BindingStatus status1 = BindingStatus.ok();
+		BindingStatus status2 = BindingStatus.ok();
+		
+		assertEquals(status1.hashCode(), status2.hashCode());
+	}
+	
+	public void testOkInitializesStatus() throws Exception {
+		BindingStatus status = BindingStatus.ok();
+		assertEquals(Policy.JFACE_DATABINDING, status.getPlugin());
+		assertEquals("", status.getMessage());
+		assertEquals(0, status.getCode());
+		assertEquals(0, status.getChildren().length);
+		assertNull(status.getException());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/ConverterValuePropertyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/ConverterValuePropertyTest.java
new file mode 100644
index 0000000..a99170f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/ConverterValuePropertyTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 306611)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding;
+
+import org.eclipse.core.databinding.BindingProperties;
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.internal.databinding.ConverterValueProperty;
+import org.eclipse.core.internal.databinding.conversion.ObjectToStringConverter;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * Tests for the {@link ConverterValueProperty} class.
+ */
+public class ConverterValuePropertyTest extends AbstractDefaultRealmTestCase {
+
+	private IConverter converter;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		converter = new ObjectToStringConverter(Integer.class);
+	}
+
+	public void testGetValue() {
+		IValueProperty property = BindingProperties.convertedValue(converter);
+
+		assertEquals("123", property.getValue(new Integer(123)));
+	}
+
+	public void testGetValueForNullSource() {
+		// The converter converts null to "".
+		IValueProperty property = BindingProperties.convertedValue(converter);
+
+		// null should also be converted rather than simply returning null.
+		assertEquals("", property.getValue(null));
+	}
+
+	public void testSetValue() {
+		IValueProperty property = BindingProperties.convertedValue(converter);
+
+		try {
+			property.setValue(new Integer(123), "123");
+			fail("setting a value should trigger an exception!");
+		} catch (UnsupportedOperationException e) {
+			// expected exception
+		}
+	}
+
+	public void testGetValueType() {
+		IValueProperty property = BindingProperties.convertedValue(converter);
+
+		assertEquals(converter.getToType(), property.getValueType());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/IdentityMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/IdentityMapTest.java
new file mode 100644
index 0000000..c393697
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/IdentityMapTest.java
@@ -0,0 +1,519 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *         (through ViewerElementMapTest.java)
+ *     Matthew Hall - bug 262269
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.identity.IdentityMap;
+
+/**
+ * @since 1.2
+ */
+public class IdentityMapTest extends TestCase {
+	IdentityMap map;
+
+	Object key;
+	Object value;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		map = new IdentityMap();
+		key = new Object();
+		value = new Object();
+	}
+
+	public void testConstructor_NullComparer() {
+		try {
+			new IdentityMap(null);
+			fail("Constructor should throw exception when null comparer passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_NullCollection() {
+		try {
+			new IdentityMap(null);
+			fail("Constructor should throw exception when null collection passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_ContainsAllEntries() {
+		Map toCopy = new HashMap();
+		toCopy.put(new Object(), new Object());
+		map = new IdentityMap(toCopy);
+		assertEquals(toCopy, map);
+	}
+
+	public void testIsEmpty() {
+		assertTrue(map.isEmpty());
+		map.put(key, value);
+		assertFalse(map.isEmpty());
+	}
+
+	public void testSize() {
+		assertEquals(0, map.size());
+		map.put(key, value);
+		assertEquals(1, map.size());
+	}
+
+	public void testClear() {
+		map.put(key, value);
+		assertFalse(map.isEmpty());
+		map.clear();
+		assertTrue(map.isEmpty());
+	}
+
+	public void testGet() {
+		assertNull(map.get(key));
+		map.put(key, value);
+		assertEquals(value, map.get(key));
+	}
+
+	public void testContainsKey() {
+		String key1 = new String("key");
+		String key2 = new String("key"); // equal but distinct instances
+		assertFalse(map.containsKey(key1));
+		map.put(key1, value);
+		assertTrue(map.containsKey(key1));
+		assertFalse(map.containsKey(key2));
+	}
+
+	public void testContainsValue() {
+		assertFalse(map.containsValue(value));
+		map.put(key, value);
+		assertTrue(map.containsValue(value));
+	}
+
+	public void testPutAll() {
+		Map other = new HashMap();
+		other.put(key, value);
+
+		assertTrue(map.isEmpty());
+		map.putAll(other);
+		assertEquals(1, map.size());
+		assertTrue(map.containsKey(key));
+		assertEquals(value, map.get(key));
+	}
+
+	public void testRemove() {
+		map.put(key, value);
+		assertTrue(map.containsKey(key));
+		map.remove(key);
+		assertFalse(map.containsKey(key));
+	}
+
+	public void testValues() {
+		Collection values = map.values();
+		assertTrue(values.isEmpty());
+
+		map.put(key, value);
+
+		assertEquals(1, values.size());
+		assertEquals(value, values.iterator().next());
+
+		map.remove(key);
+		assertTrue(map.values().isEmpty());
+	}
+
+	public void testKeySet() {
+		Set keySet = map.keySet();
+		assertTrue(keySet.isEmpty());
+
+		map.put(key, value);
+		assertEquals(1, keySet.size());
+		assertTrue(keySet.contains(key));
+
+		map.remove(key);
+		assertTrue(keySet.isEmpty());
+	}
+
+	public void testKeySet_Add() {
+		try {
+			map.keySet().add(key);
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testKeySet_AddAll() {
+		try {
+			map.keySet().addAll(Collections.singleton(key));
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testKeySet_Clear() {
+		map.put(key, value);
+		Set keySet = map.keySet();
+		assertTrue(keySet.contains(key));
+		keySet.clear();
+		assertTrue(keySet.isEmpty());
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_Contains() {
+		Set keySet = map.keySet();
+		assertFalse(keySet.contains(key));
+		map.put(key, value);
+		assertTrue(keySet.contains(key));
+	}
+
+	public void testKeySet_ContainsAll() {
+		Set keySet = map.keySet();
+		assertFalse(keySet.containsAll(Collections.singleton(key)));
+		map.put(key, value);
+		assertTrue(keySet.containsAll(Collections.singleton(key)));
+	}
+
+	public void testKeySet_IsEmpty() {
+		Set keySet = map.keySet();
+		assertTrue(keySet.isEmpty());
+		map.put(key, value);
+		assertFalse(keySet.isEmpty());
+	}
+
+	public void testKeySet_Iterator() {
+		map.put(key, value);
+		Iterator iterator = map.keySet().iterator();
+		assertTrue(iterator.hasNext());
+		assertEquals(key, iterator.next());
+
+		assertEquals(1, map.size());
+		iterator.remove();
+		assertTrue(map.isEmpty());
+
+		assertFalse(iterator.hasNext());
+	}
+
+	public void testKeySet_Remove() {
+		map.put(key, value);
+		assertEquals(1, map.size());
+		map.keySet().remove(key);
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_RemoveAll() {
+		map.put(key, value);
+		Set keySet = map.keySet();
+		assertFalse(keySet.removeAll(Collections.EMPTY_SET));
+		assertEquals(1, map.size());
+		assertTrue(keySet.removeAll(Collections.singleton(key)));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_RetainAll() {
+		map.put(key, value);
+		Set keySet = map.keySet();
+		assertFalse(keySet.retainAll(Collections.singleton(key)));
+		assertEquals(1, map.size());
+		assertTrue(keySet.retainAll(Collections.EMPTY_SET));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_Size() {
+		Set keySet = map.keySet();
+		assertEquals(0, keySet.size());
+		map.put(key, value);
+		assertEquals(1, keySet.size());
+		map.clear();
+		assertEquals(0, keySet.size());
+	}
+
+	public void testKeySet_ToArray() {
+		Set keySet = map.keySet();
+		map.put(key, value);
+		Object[] array = keySet.toArray();
+		assertEquals(1, array.length);
+		assertSame(key, array[0]);
+	}
+
+	public void testKeySet_ToArrayWithObjectArray() {
+		key = new String("key");
+		map.put(key, value);
+		String[] array = (String[]) map.keySet().toArray(new String[0]);
+		assertEquals(1, array.length);
+		assertSame(key, array[0]);
+	}
+
+	public void testKeySet_Equals() {
+		Set keySet = map.keySet();
+		assertFalse(keySet.equals(null));
+		assertTrue(keySet.equals(keySet));
+
+		assertTrue(keySet.equals(Collections.EMPTY_SET));
+		map.put(key, value);
+		assertTrue(keySet.equals(Collections.singleton(key)));
+	}
+
+	public void testKeySet_HashCode() {
+		Set keySet = map.keySet();
+		assertEquals(0, keySet.hashCode());
+		map.put(key, value);
+		int hash = key.hashCode();
+		assertEquals(hash, keySet.hashCode());
+	}
+
+	public void testEntrySet() {
+		Set entrySet = map.entrySet();
+		assertTrue(entrySet.isEmpty());
+
+		map.put(key, value);
+		assertEquals(1, entrySet.size());
+		Map.Entry entry = (Map.Entry) entrySet.iterator().next();
+		assertEquals(key, entry.getKey());
+		assertEquals(value, entry.getValue());
+
+		map.remove(key);
+		assertTrue(entrySet.isEmpty());
+	}
+
+	public void testEntrySet_Add() {
+		try {
+			map.entrySet().add(key);
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testEntrySet_AddAll() {
+		try {
+			map.entrySet().addAll(Collections.EMPTY_SET);
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testEntrySet_Clear() {
+		map.put(key, value);
+		assertEquals(1, map.size());
+		map.entrySet().clear();
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_Contains() {
+		map.put(key, value);
+		Set entrySet = map.entrySet();
+		assertTrue(entrySet.contains(new MapEntryStub(key, value)));
+		map.remove(key);
+		assertFalse(entrySet.contains(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_ContainsAll() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.containsAll(Collections
+				.singleton(new MapEntryStub(key, value))));
+		assertTrue(entrySet.containsAll(Collections.EMPTY_SET));
+
+		map.put(key, value);
+		assertTrue(entrySet.containsAll(Collections.singleton(new MapEntryStub(
+				key, value))));
+	}
+
+	public void testEntrySet_IsEmpty() {
+		Set entrySet = map.entrySet();
+		assertTrue(entrySet.isEmpty());
+		map.put(key, value);
+		assertFalse(entrySet.isEmpty());
+	}
+
+	public void testEntrySet_Iterator() {
+		map.put(key, value);
+		Iterator iterator = map.entrySet().iterator();
+		assertTrue(iterator.hasNext());
+		Map.Entry entry = (Map.Entry) iterator.next();
+		assertTrue(entry.equals(new MapEntryStub(key, value)));
+
+		assertEquals(1, map.size());
+		iterator.remove();
+		assertTrue(map.isEmpty());
+		assertFalse(iterator.hasNext());
+	}
+
+	public void testEntrySet_Remove() {
+		map.put(key, value);
+		assertEquals(1, map.size());
+
+		assertTrue(map.entrySet().remove(new MapEntryStub(key, value)));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_RemoveAll() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.removeAll(Collections.EMPTY_SET));
+
+		map.put(key, value);
+		assertEquals(1, map.size());
+		assertTrue(entrySet.removeAll(Collections.singleton(new MapEntryStub(
+				key, value))));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_RetainAll() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.retainAll(Collections.EMPTY_SET));
+
+		map.put(key, value);
+		assertEquals(1, map.size());
+		assertFalse(entrySet.retainAll(Collections.singleton(new MapEntryStub(
+				key, value))));
+		assertEquals(1, map.size());
+		assertTrue(entrySet.retainAll(Collections.EMPTY_SET));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_Size() {
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.size());
+		map.put(key, value);
+		assertEquals(1, entrySet.size());
+	}
+
+	public void testEntrySet_ToArray() {
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.toArray().length);
+
+		map.put(key, value);
+		Object[] array = entrySet.toArray();
+		assertEquals(1, array.length);
+		assertTrue(array[0].equals(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_ToArrayWithObjectArray() {
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.toArray(new Object[0]).length);
+
+		map.put(key, value);
+		Map.Entry[] array = (Map.Entry[]) entrySet.toArray(new Map.Entry[0]);
+		assertEquals(1, array.length);
+		assertTrue(array[0].equals(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_Equals() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.equals(null));
+		assertTrue(entrySet.equals(entrySet));
+
+		assertTrue(entrySet.equals(Collections.EMPTY_SET));
+		assertFalse(entrySet.equals(Collections.singleton(new MapEntryStub(key,
+				value))));
+
+		map.put(key, value);
+		assertFalse(entrySet.equals(Collections.EMPTY_SET));
+		assertTrue(entrySet.equals(Collections.singleton(new MapEntryStub(key,
+				value))));
+	}
+
+	public void testEntrySet_HashCode() {
+		// hash formula mandated by Map contract
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.hashCode());
+
+		map.put(key, value);
+		int hash = key.hashCode() ^ value.hashCode();
+		assertEquals(hash, entrySet.hashCode());
+	}
+
+	public void testEntrySet_Entry_SetValue() {
+		map.put(key, value);
+
+		Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
+
+		Object newValue = new Object();
+		Object oldValue = entry.setValue(newValue);
+		assertEquals(value, oldValue);
+		assertEquals(newValue, entry.getValue());
+		assertEquals(newValue, map.get(key));
+	}
+
+	public void testEntrySet_Entry_Equals() {
+		map.put(key, value);
+
+		Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
+		assertFalse(entry.equals(null));
+		assertTrue(entry.equals(entry));
+		assertTrue(entry.equals(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_Entry_HashCode() {
+		map.put(key, value);
+
+		// hash computed as required by Map contract
+		int hash = key.hashCode() ^ value.hashCode();
+		assertEquals(hash, map.entrySet().iterator().next().hashCode());
+	}
+
+	public void testEquals() {
+		assertFalse(map.equals(null));
+		assertTrue(map.equals(map));
+
+		Map other = new HashMap();
+		other.put(key, value);
+
+		assertTrue(map.equals(Collections.EMPTY_MAP));
+		assertFalse(map.equals(other));
+
+		map.put(key, value);
+
+		assertFalse(map.equals(Collections.EMPTY_MAP));
+		assertTrue(map.equals(other));
+	}
+
+	public void testHashCode() {
+		assertEquals(0, map.hashCode());
+
+		map.put(key, value);
+		int hash = key.hashCode() ^ value.hashCode();
+		assertEquals(hash, map.hashCode());
+	}
+
+	static class MapEntryStub implements Map.Entry {
+		private final Object key;
+		private final Object value;
+
+		public MapEntryStub(Object key, Object value) {
+			this.key = key;
+			this.value = value;
+		}
+
+		public Object getKey() {
+			return key;
+		}
+
+		public Object getValue() {
+			return value;
+		}
+
+		public Object setValue(Object value) {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean equals(Object obj) {
+			throw new UnsupportedOperationException();
+		}
+
+		public int hashCode() {
+			throw new UnsupportedOperationException();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/IdentitySetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/IdentitySetTest.java
new file mode 100644
index 0000000..7e2ebc4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/IdentitySetTest.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *         (through ViewerElementSetTest.java)
+ *     Matthew Hall - bug 262269
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.identity.IdentitySet;
+import org.eclipse.jface.internal.databinding.viewers.ViewerElementSet;
+
+/**
+ * @since 1.2
+ */
+public class IdentitySetTest extends TestCase {
+	IdentitySet set;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		set = new IdentitySet();
+	}
+
+	public void testConstructor_NullComparer() {
+		try {
+			new ViewerElementSet(null);
+			fail("Constructor should throw exception when null comparer passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_NullCollection() {
+		try {
+			new ViewerElementSet(null);
+			fail("Constructor should throw exception when null collection passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_AddsAllElements() {
+		Collection toCopy = Collections.singleton(new Object());
+		set = new IdentitySet(toCopy);
+		assertTrue(set.containsAll(toCopy));
+	}
+
+	public void testAdd_ContainsHonorsComparer() {
+		Object o1 = new String("string");
+		Object o2 = new String("string"); // distinct instances
+		assertTrue(o1.equals(o2));
+		assertNotSame(o1, o2);
+
+		assertTrue(set.add(o1));
+
+		assertTrue(set.contains(o1));
+		assertFalse(set.contains(o2));
+	}
+
+	public void testAdd_FilterDuplicateElements() {
+		Object o = new Object();
+
+		assertTrue(set.add(o));
+		assertFalse(set.add(o)); // no change--element already in set
+
+		assertEquals(1, set.size());
+		assertTrue(set.contains(o));
+	}
+
+	public void testAddAll_ContainsAllHonorsComparer() {
+		String o1 = new String("o1");
+		String o2 = new String("o2");
+		Collection items = Arrays.asList(new Object[] { o1, o2 });
+		assertTrue(set.addAll(items));
+
+		assertTrue(set.containsAll(items));
+		assertFalse(set.containsAll(Collections.singleton(new String("o1"))));
+		assertFalse(set.containsAll(Collections.singleton(new String("o2"))));
+	}
+
+	public void testAddAll_FiltersDuplicateElements() {
+		Object o = new Object();
+		set.add(o);
+
+		assertFalse(set.addAll(Collections.singleton(o)));
+	}
+
+	public void testClear() {
+		set.add(new Object());
+		assertEquals(1, set.size());
+
+		set.clear();
+		assertEquals(0, set.size());
+	}
+
+	public void testIsEmpty() {
+		assertTrue(set.isEmpty());
+		set.add(new Object());
+		assertFalse(set.isEmpty());
+	}
+
+	public void testIterator() {
+		Object o = new Object();
+		set.add(o);
+
+		Iterator iterator = set.iterator();
+		assertTrue(iterator.hasNext());
+		assertSame(o, iterator.next());
+
+		assertTrue(set.contains(o));
+		iterator.remove();
+		assertFalse(set.contains(o));
+
+		assertFalse(iterator.hasNext());
+	}
+
+	public void testRemove() {
+		Object o = new Object();
+		assertFalse(set.remove(o));
+
+		assertTrue(set.add(o));
+		assertTrue(set.contains(o));
+		assertTrue(set.remove(o));
+
+		assertFalse(set.contains(o));
+	}
+
+	public void testRemoveAll() {
+		assertFalse(set.removeAll(Collections.EMPTY_SET));
+
+		Object o1 = new Object();
+		Object o2 = new Object();
+		set.addAll(Arrays.asList(new Object[] { o1, o2 }));
+
+		assertTrue(set.removeAll(Collections.singleton(o1)));
+		assertFalse(set.contains(o1));
+		assertFalse(set.removeAll(Collections.singleton(o1)));
+
+		assertTrue(set.removeAll(Arrays.asList(new Object[] { o2, "some",
+				"other", "objects" })));
+		assertFalse(set.contains(o2));
+	}
+
+	public void testRetainAll() {
+		Object o1 = new Object();
+		Object o2 = new Object();
+		set.add(o1);
+		set.add(o2);
+
+		assertFalse(set.retainAll(Arrays.asList(new Object[] { o1, o2 }))); // no
+		// change
+
+		assertTrue(set.contains(o2));
+		assertTrue(set.retainAll(Collections.singleton(o1)));
+		assertFalse(set.contains(o2));
+
+		assertTrue(set.contains(o1));
+		assertTrue(set.retainAll(Collections.EMPTY_SET));
+		assertFalse(set.contains(o1));
+	}
+
+	public void testSize() {
+		assertEquals(0, set.size());
+
+		Object o = new Object();
+		set.add(o);
+		assertEquals(1, set.size());
+
+		set.remove(o);
+		assertEquals(0, set.size());
+	}
+
+	public void testToArray() {
+		assertEquals(0, set.toArray().length);
+
+		Object o = new Object();
+		set.add(o);
+		assertTrue(Arrays.equals(new Object[] { o }, set.toArray()));
+	}
+
+	public void testToArrayWithObjectArray() {
+		Object o = new String("unique");
+		set.add(o);
+
+		String[] array = (String[]) set.toArray(new String[0]);
+		assertEquals(1, array.length);
+		assertSame(o, array[0]);
+	}
+
+	public void testEquals() {
+		assertTrue(set.equals(set));
+		assertFalse(set.equals(null));
+		assertFalse(set.equals(new Object()));
+
+		assertTrue(set.equals(Collections.EMPTY_SET));
+
+		Object o = new String("string");
+		Object distinct = new String("string");
+		set.add(o);
+		assertTrue(set.equals(Collections.singleton(o)));
+		assertFalse(set.equals(Collections.singleton(distinct)));
+	}
+
+	public void testHashCode() {
+		// Hash code implementation is mandated
+		assertEquals(0, set.hashCode());
+
+		Object o = new Object();
+		set.add(o);
+		int hash = o.hashCode();
+		assertEquals(hash, set.hashCode());
+
+		Object o2 = new Object();
+		set.add(o2);
+		hash += o2.hashCode();
+		assertEquals(hash, set.hashCode());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/QueueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/QueueTest.java
new file mode 100644
index 0000000..3a14c1f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/QueueTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding;
+
+import org.eclipse.core.internal.databinding.observable.Queue;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 3.2
+ *
+ */
+public class QueueTest extends TestCase {
+
+	private Queue queue;
+
+	protected void setUp() throws Exception {
+		this.queue = new Queue();
+	}
+	
+	public void testIsEmpty() {
+		assertTrue(queue.isEmpty());
+		queue.enqueue("foo");
+		assertFalse(queue.isEmpty());
+		queue.enqueue("bar");
+		assertFalse(queue.isEmpty());
+		queue.dequeue();
+		assertFalse(queue.isEmpty());
+		queue.dequeue();
+		assertTrue(queue.isEmpty());
+	}
+	
+	public void testEnqueueAndDequeue() {
+		try {
+			queue.dequeue();
+			fail("expected IllegalStateException");
+		} catch(IllegalStateException ex) {
+			// expected
+		}
+		queue.enqueue("foo");
+		assertEquals("foo", queue.dequeue());
+		try {
+			queue.dequeue();
+			fail("expected IllegalStateException");
+		} catch(IllegalStateException ex) {
+			// expected
+		}
+		queue.enqueue("foo");
+		queue.enqueue("bar");
+		queue.dequeue();
+		queue.enqueue("bas");
+		queue.enqueue("moo");
+		assertEquals("bar", queue.dequeue());
+		assertEquals("bas", queue.dequeue());
+		assertEquals("moo", queue.dequeue());
+		assertTrue(queue.isEmpty());
+	}
+	
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/AnnoyingBean.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/AnnoyingBean.java
new file mode 100644
index 0000000..257218c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/AnnoyingBean.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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 246103)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A bean in which all property change events are fired according to an annoying
+ * provision in the bean spec, where <code>(oldValue == null && newValue ==
+ * null)</code> indicates that an unknown change occured.
+ * 
+ * @since 3.2
+ */
+public class AnnoyingBean extends Bean {
+	public void setValue(String value) {
+		this.value = value;
+		changeSupport.firePropertyChange("value", null, null);
+	}
+
+	public void setArray(Object[] array) {
+		this.array = array;
+		changeSupport.firePropertyChange("array", null, null);
+	}
+
+	public void setList(List list) {
+		this.list = list;
+		changeSupport.firePropertyChange("list", null, null);
+	}
+
+	public void setSet(Set set) {
+		this.set = set;
+		changeSupport.firePropertyChange("set", null, null);
+	}
+
+	public void setMap(Map map) {
+		this.map = map;
+		changeSupport.firePropertyChange("map", null, null);
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/Bean.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/Bean.java
new file mode 100644
index 0000000..1c2b4b8
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/Bean.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 256150, 264619
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Simple Java Bean for testing.
+ * 
+ * @since 3.3
+ */
+public class Bean implements IBean {
+	protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+			this);
+	protected String value;
+	protected Object[] array;
+	protected List list;
+	protected Set set;
+	protected Map map;
+	protected Bean bean;
+
+	public Bean() {
+	}
+
+	public Bean(String value) {
+		this.value = value;
+	}
+
+	public Bean(Object[] array) {
+		this.array = array;
+	}
+
+	public Bean(List list) {
+		this.list = list;
+	}
+
+	public Bean(Set set) {
+		this.set = set;
+	}
+
+	public Bean(Map map) {
+		this.map = map;
+	}
+
+	public Bean(Bean bean) {
+		this.bean = bean;
+	}
+
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		changeSupport.addPropertyChangeListener(listener);
+	}
+
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		changeSupport.removePropertyChangeListener(listener);
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		changeSupport.firePropertyChange("value", this.value,
+				this.value = value);
+	}
+
+	public Object[] getArray() {
+		return array;
+	}
+
+	public void setArray(Object[] array) {
+		changeSupport.firePropertyChange("array", this.array,
+				this.array = array);
+	}
+
+	public List getList() {
+		return list;
+	}
+
+	public void setList(List list) {
+		changeSupport.firePropertyChange("list", this.list, this.list = list);
+	}
+
+	public Set getSet() {
+		return set;
+	}
+
+	public void setSet(Set set) {
+		changeSupport.firePropertyChange("set", this.set, this.set = set);
+	}
+
+	public Map getMap() {
+		return map;
+	}
+
+	public void setMap(Map map) {
+		changeSupport.firePropertyChange("map", this.map, this.map = map);
+	}
+
+	public Bean getBean() {
+		return bean;
+	}
+
+	public void setBean(Bean bean) {
+		changeSupport.firePropertyChange("bean", this.bean, this.bean = bean);
+	}
+
+	public boolean hasListeners(String propertyName) {
+		return changeSupport.hasListeners(propertyName);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableListDecoratorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableListDecoratorTest.java
new file mode 100644
index 0000000..ada9d5c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableListDecoratorTest.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 208858, 213145, 246625, 194734
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+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.internal.databinding.beans.BeanObservableListDecorator;
+import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 3.3
+ */
+public class BeanObservableListDecoratorTest extends TestCase {
+	private Bean bean;
+	private PropertyDescriptor propertyDescriptor;
+	private IObservableList observableList;
+	private BeanObservableListDecorator decorator;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		bean = new Bean();
+		propertyDescriptor = new PropertyDescriptor(
+				"list", Bean.class,"getList","setList");
+		observableList = BeansObservables.observeList(
+				SWTObservables.getRealm(Display.getDefault()), bean, "list");
+		decorator = new BeanObservableListDecorator(observableList, propertyDescriptor);
+	}
+
+	public void testGetDelegate() throws Exception {
+		assertSame(observableList, decorator.getDecorated());
+	}
+
+	public void testGetObserved() throws Exception {
+		assertSame(bean, decorator.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertSame(propertyDescriptor, decorator.getPropertyDescriptor());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(BeanObservableListDecoratorTest.class.getName());
+		suite.addTestSuite(BeanObservableListDecoratorTest.class);
+		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			final WritableList delegate = new WritableList(realm);
+			for (int i = 0; i < elementCount; i++)
+				delegate.add(createElement(delegate));
+			return new BeanObservableListDecorator(delegate, null);
+		}
+
+		private int counter;
+
+		public Object createElement(IObservableCollection collection) {
+			return Integer.toString(counter++);
+		}
+
+		public void change(IObservable observable) {
+			IObservableList list = (IObservableList) observable;
+			list.add(createElement(list));
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableSetDecoratorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableSetDecoratorTest.java
new file mode 100644
index 0000000..8dff2e5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableSetDecoratorTest.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 246625, 194734
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.internal.databinding.beans.BeanObservableSetDecorator;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 3.3
+ */
+public class BeanObservableSetDecoratorTest extends TestCase {
+	private PropertyDescriptor propertyDescriptor;
+	private IObservableSet observableSet;
+	private BeanObservableSetDecorator decorator;
+	private Bean bean;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		bean = new Bean();
+		propertyDescriptor = new PropertyDescriptor("set", Bean.class);
+		observableSet = BeansObservables.observeSet(SWTObservables
+				.getRealm(Display.getDefault()), bean, "set");
+		decorator = new BeanObservableSetDecorator(observableSet,
+				propertyDescriptor);
+	}
+
+	public void testGetDecorated() throws Exception {
+		assertSame(observableSet, decorator.getDecorated());
+	}
+
+	public void testGetObserved() throws Exception {
+		assertSame(bean, decorator.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertSame(propertyDescriptor, decorator.getPropertyDescriptor());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableValueDecoratorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableValueDecoratorTest.java
new file mode 100644
index 0000000..eef16fb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanObservableValueDecoratorTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 246625, 194734
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.internal.databinding.beans.BeanObservableValueDecorator;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 3.3
+ */
+public class BeanObservableValueDecoratorTest extends AbstractDefaultRealmTestCase {
+	private Bean bean;
+	private IObservableValue observableValue;
+	private BeanObservableValueDecorator decorator;
+	private PropertyDescriptor propertyDescriptor;
+	
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		bean = new Bean();
+		propertyDescriptor = new PropertyDescriptor("value", Bean.class);
+		observableValue = BeansObservables.observeValue(SWTObservables
+				.getRealm(Display.getDefault()), bean, "value");
+		decorator = new BeanObservableValueDecorator(observableValue,
+				propertyDescriptor);
+	}
+
+	public void testGetDelegate() throws Exception {
+		assertSame(observableValue, decorator.getDecorated());
+	}
+	
+	public void testGetObserved() throws Exception {
+		assertSame(bean, decorator.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertSame(propertyDescriptor, decorator.getPropertyDescriptor());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyHelperTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyHelperTest.java
new file mode 100644
index 0000000..fd66d65
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyHelperTest.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 256150)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import org.eclipse.core.internal.databinding.beans.BeanPropertyHelper;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class BeanPropertyHelperTest extends TestCase {
+	public void testGetPropertyDescriptor_ClassProperty()
+			throws SecurityException, NoSuchMethodException {
+		PropertyDescriptor pd = BeanPropertyHelper.getPropertyDescriptor(
+				Bean.class, "value");
+		assertEquals(Bean.class.getMethod("getValue", null), pd.getReadMethod());
+		assertEquals(Bean.class.getMethod("setValue",
+				new Class[] { String.class }), pd.getWriteMethod());
+	}
+
+	public void testGetPropertyDescriptor_InterfaceProperty()
+			throws SecurityException, NoSuchMethodException {
+		PropertyDescriptor pd = BeanPropertyHelper.getPropertyDescriptor(
+				IBean.class, "value");
+		assertEquals(IBean.class.getMethod("getValue", null), pd
+				.getReadMethod());
+		assertEquals(IBean.class.getMethod("setValue",
+				new Class[] { String.class }), pd.getWriteMethod());
+	}
+
+	public void testGetPropertyDescriptor_SuperInterfaceProperty()
+			throws SecurityException, NoSuchMethodException {
+		PropertyDescriptor pd = BeanPropertyHelper.getPropertyDescriptor(
+				IBeanExtension.class, "value");
+		assertEquals(IBean.class.getMethod("getValue", null), pd
+				.getReadMethod());
+		assertEquals(IBean.class.getMethod("setValue",
+				new Class[] { String.class }), pd.getWriteMethod());
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyListenerSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyListenerSupportTest.java
new file mode 100644
index 0000000..156e266
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyListenerSupportTest.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.util.ILogger;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.beans.BeanPropertyListenerSupport;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class BeanPropertyListenerSupportTest extends
+		AbstractDefaultRealmTestCase {
+	private PropertyChangeListenerStub listener;
+	private String propertyName;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		listener = new PropertyChangeListenerStub();
+		propertyName = "value";
+	}
+
+	public void testAddPropertyChangeListenerWithPropertyName()
+			throws Exception {
+		SpecificListenerBean bean = new SpecificListenerBean();
+
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+
+		BeanPropertyListenerSupport.hookListener(bean, propertyName, listener);
+		assertTrue("has listeners", bean.changeSupport
+				.hasListeners(propertyName));
+	}
+
+	public void testAddPropertyChangeListenerWithoutPropertyName()
+			throws Exception {
+		GenericListenerBean bean = new GenericListenerBean();
+
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+
+		BeanPropertyListenerSupport.hookListener(bean, propertyName, listener);
+		assertTrue("has listeners", bean.changeSupport
+				.hasListeners(propertyName));
+	}
+
+	public void testLogStatusWhenAddPropertyChangeListenerMethodIsNotFound()
+			throws Exception {
+		class BeanStub {
+		}
+
+		class Log implements ILogger {
+			int count;
+			IStatus status;
+
+			public void log(IStatus status) {
+				count++;
+				this.status = status;
+			}
+		}
+
+		Log log = new Log();
+		Policy.setLog(log);
+
+		BeanStub bean = new BeanStub();
+
+		assertEquals(0, log.count);
+		BeanPropertyListenerSupport.hookListener(bean, "value", listener);
+		assertEquals(1, log.count);
+		assertEquals(IStatus.WARNING, log.status.getSeverity());
+	}
+
+	public void testRemovePropertyChangeListenerWithPropertyName()
+			throws Exception {
+		SpecificListenerBean bean = new SpecificListenerBean();
+		BeanPropertyListenerSupport.hookListener(bean, propertyName, listener);
+
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+
+		BeanPropertyListenerSupport
+				.unhookListener(bean, propertyName, listener);
+		assertFalse("has listeners", bean.changeSupport
+				.hasListeners(propertyName));
+	}
+
+	public void testRemovePropertyChangeListenerWithoutPropertyName()
+			throws Exception {
+		GenericListenerBean bean = new GenericListenerBean();
+		BeanPropertyListenerSupport.hookListener(bean, propertyName, listener);
+
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+
+		BeanPropertyListenerSupport
+				.unhookListener(bean, propertyName, listener);
+		assertFalse("has listeners", bean.changeSupport
+				.hasListeners(propertyName));
+	}
+
+	public void testLogStatusWhenRemovePropertyChangeListenerMethodIsNotFound()
+			throws Exception {
+		class InvalidBean {
+		}
+
+		class Log implements ILogger {
+			int count;
+			IStatus status;
+
+			public void log(IStatus status) {
+				count++;
+				this.status = status;
+			}
+		}
+
+		Log log = new Log();
+		Policy.setLog(log);
+
+		InvalidBean bean = new InvalidBean();
+
+		BeanPropertyListenerSupport.hookListener(bean, "value", listener);
+		log.count = 0;
+		log.status = null;
+		assertEquals(0, log.count);
+		BeanPropertyListenerSupport.unhookListener(bean, "value", listener);
+		assertEquals(1, log.count);
+		assertEquals(IStatus.WARNING, log.status.getSeverity());
+	}
+
+	static class GenericListenerBean {
+		private String other;
+		PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
+		private String value;
+
+		public String getValue() {
+			return value;
+		}
+
+		public void setValue(String value) {
+			changeSupport.firePropertyChange("value", this.value,
+					this.value = value);
+		}
+
+		public String getOther() {
+			return other;
+		}
+
+		public void setOther(String other) {
+			changeSupport.firePropertyChange("other", this.other,
+					this.other = other);
+		}
+
+		public void addPropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.addPropertyChangeListener(listener);
+		}
+
+		public void removePropertyChangeListener(PropertyChangeListener listener) {
+			changeSupport.removePropertyChangeListener(listener);
+		}
+	}
+
+	static class SpecificListenerBean {
+		PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
+		String propertyName;
+		String value;
+
+		public void addPropertyChangeListener(String name,
+				PropertyChangeListener listener) {
+			this.propertyName = name;
+			changeSupport.addPropertyChangeListener(name, listener);
+		}
+
+		public void removePropertyChangeListener(String name,
+				PropertyChangeListener listener) {
+			changeSupport.removePropertyChangeListener(name, listener);
+		}
+
+		public String getValue() {
+			return value;
+		}
+
+		public void setValue(String value) {
+			this.value = value;
+		}
+	}
+
+	static class PropertyChangeListenerStub implements PropertyChangeListener {
+		PropertyChangeEvent event;
+		int count;
+
+		public void propertyChange(PropertyChangeEvent evt) {
+			count++;
+			this.event = evt;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyListenerTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyListenerTest.java
new file mode 100644
index 0000000..7334297
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanPropertyListenerTest.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 268336)
+ *******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyDescriptor;
+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.IDiff;
+import org.eclipse.core.databinding.property.IProperty;
+import org.eclipse.core.databinding.property.ISimplePropertyListener;
+import org.eclipse.core.databinding.property.SimplePropertyEvent;
+import org.eclipse.core.internal.databinding.beans.BeanPropertyListener;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class BeanPropertyListenerTest extends AbstractDefaultRealmTestCase {
+	private PropertyStub property;
+	private PropertyDescriptor propertyDescriptor;
+	private SimplePropertyListenerStub simpleListener;
+	private BeanPropertyListenerStub listener;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		property = new PropertyStub();
+		propertyDescriptor = new PropertyDescriptor("value", Bean.class);
+		simpleListener = new SimplePropertyListenerStub();
+		listener = new BeanPropertyListenerStub(property, propertyDescriptor,
+				simpleListener);
+	}
+
+	public void testPropertyChange_ExpectedPropertyName() {
+		Object source = new Object();
+		String propertyName = "value";
+		Object oldValue = new Object();
+		Object newValue = new Object();
+		listener.propertyChange(new PropertyChangeEvent(source, propertyName,
+				oldValue, newValue));
+
+		SimplePropertyEvent expectedEvent = new SimplePropertyEvent(
+				SimplePropertyEvent.CHANGE, source, property, Diffs
+						.createValueDiff(oldValue, newValue));
+		assertEquals(Collections.singletonList(expectedEvent),
+				simpleListener.log);
+	}
+
+	public void testPropertyChange_OtherPropertyName() {
+		Object source = new Object();
+		String propertyName = "other";
+		Object oldValue = new Object();
+		Object newValue = new Object();
+		listener.propertyChange(new PropertyChangeEvent(source, propertyName,
+				oldValue, newValue));
+
+		assertEquals(Collections.EMPTY_LIST, simpleListener.log);
+	}
+
+	public void testPropertyChange_NullPropertyName() {
+		Object source = new Object();
+		String propertyName = null;
+		Object oldValue = null;
+		Object newValue = null;
+		listener.propertyChange(new PropertyChangeEvent(source, propertyName,
+				oldValue, newValue));
+
+		SimplePropertyEvent expectedEvent = new SimplePropertyEvent(
+				SimplePropertyEvent.CHANGE, source, property, null);
+		assertEquals(Collections.singletonList(expectedEvent),
+				simpleListener.log);
+	}
+
+	public void testPropertyChange_NullPropertyName_IgnoreOldAndNewValues() {
+		Object source = new Object();
+		String propertyName = null;
+		Object oldValue = new Object();
+		Object newValue = new Object();
+		listener.propertyChange(new PropertyChangeEvent(source, propertyName,
+				oldValue, newValue));
+
+		SimplePropertyEvent expectedEvent = new SimplePropertyEvent(
+				SimplePropertyEvent.CHANGE, source, property, null);
+		assertEquals(Collections.singletonList(expectedEvent),
+				simpleListener.log);
+	}
+
+	private static class PropertyStub implements IProperty {
+	}
+
+	private static class SimplePropertyListenerStub implements
+			ISimplePropertyListener {
+		public List log = new ArrayList();
+
+		public void handleEvent(SimplePropertyEvent event) {
+			log.add(event);
+		}
+	}
+
+	private static class BeanPropertyListenerStub extends BeanPropertyListener {
+		BeanPropertyListenerStub(IProperty property,
+				PropertyDescriptor propertyDescriptor,
+				ISimplePropertyListener listener) {
+			super(property, propertyDescriptor, listener);
+		}
+
+		protected IDiff computeDiff(Object oldValue, Object newValue) {
+			return Diffs.createValueDiff(oldValue, newValue);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanValuePropertyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanValuePropertyTest.java
new file mode 100644
index 0000000..0efa6da
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/BeanValuePropertyTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ *     Matthew Hall - bug 195222
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+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.property.value.IValueProperty;
+import org.eclipse.core.tests.internal.databinding.beans.BeanPropertyListenerSupportTest.GenericListenerBean;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class BeanValuePropertyTest extends AbstractDefaultRealmTestCase {
+	public void testChangeListenerIsOnlyNotifiedWhenWatchedPropertyChanges()
+			throws Exception {
+		GenericListenerBean bean = new GenericListenerBean();
+		IValueProperty property = BeanProperties
+				.value(GenericListenerBean.class, "value");
+		class Listener implements IValueChangeListener {
+			private int count = 0;
+
+			public void handleValueChange(ValueChangeEvent event) {
+				count++;
+			}
+		}
+		Listener listener = new Listener();
+
+		IObservableValue observable = property.observe(new CurrentRealm(true), bean);
+		observable.addValueChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		bean.setValue("1");
+		assertEquals(1, listener.count);
+
+		bean.setOther("2");
+		assertEquals(1, listener.count);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/IBean.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/IBean.java
new file mode 100644
index 0000000..134602b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/IBean.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 256150)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @since 3.2
+ * 
+ */
+public interface IBean {
+	public String getValue();
+
+	public void setValue(String value);
+
+	public Object[] getArray();
+
+	public void setArray(Object[] array);
+
+	public List getList();
+
+	public void setList(List list);
+
+	public Set getSet();
+
+	public void setSet(Set set);
+
+	public Map getMap();
+
+	public void setMap(Map map);
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/IBeanExtension.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/IBeanExtension.java
new file mode 100644
index 0000000..775744b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/IBeanExtension.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 256150)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+/**
+ * @since 3.2
+ * 
+ */
+public interface IBeanExtension extends IBean {
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedListTest.java
new file mode 100644
index 0000000..f40ef0e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedListTest.java
@@ -0,0 +1,602 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 213145, 244098, 246103, 194734, 268688,
+ *                    301774
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.IBeanProperty;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+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.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 1.1
+ */
+public class JavaBeanObservableArrayBasedListTest extends
+		AbstractDefaultRealmTestCase {
+	private IObservableList list;
+	private IBeanObservable beanObservable;
+
+	private PropertyDescriptor propertyDescriptor;
+
+	private Bean bean;
+
+	private String propertyName;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		propertyName = "array";
+		propertyDescriptor = ((IBeanProperty) BeanProperties.list(Bean.class,
+				propertyName)).getPropertyDescriptor();
+		bean = new Bean(new Object[0]);
+
+		list = BeansObservables.observeList(SWTObservables.getRealm(Display
+				.getDefault()), bean, propertyName);
+		beanObservable = (IBeanObservable) list;
+	}
+
+	public void testGetObserved() throws Exception {
+		assertSame(bean, beanObservable.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertEquals(propertyDescriptor, beanObservable.getPropertyDescriptor());
+	}
+
+	public void testRegistersListenerAfterFirstListenerIsAdded()
+			throws Exception {
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+		list.addListChangeListener(new ListChangeEventTracker());
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testRemovesListenerAfterLastListenerIsRemoved()
+			throws Exception {
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+		list.removeListChangeListener(listener);
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testFiresListChangeEvents() throws Exception {
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		bean.setArray(new Bean[] { new Bean() });
+		assertEquals(1, listener.count);
+	}
+
+	public void testAddAddsElement() throws Exception {
+		int count = list.size();
+		String element = "1";
+
+		assertEquals(0, count);
+		list.add(element);
+		assertEquals(count + 1, list.size());
+		assertEquals(element, bean.getArray()[count]);
+	}
+
+	public void testAddListChangeEvent() throws Exception {
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		String element = "1";
+
+		list.add(element);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+
+		assertSame(list, event.getObservableList());
+		assertDiff(event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList("1"));
+	}
+
+	public void testAdd_FiresPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.add("0");
+			}
+		});
+	}
+
+	public void testAddWithIndex() throws Exception {
+		String element = "1";
+		assertEquals(0, list.size());
+
+		list.add(0, element);
+		assertEquals(element, bean.getArray()[0]);
+	}
+
+	public void testAddAtIndexListChangeEvent() throws Exception {
+		String element = "1";
+		assertEquals(0, list.size());
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		list.add(0, element);
+
+		ListChangeEvent event = listener.event;
+		assertDiff(event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList("1"));
+	}
+
+	public void testAddAtIndexPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.add(0, "0");
+			}
+		});
+	}
+
+	public void testRemove() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, bean.getArray().length);
+		list.remove(element);
+		assertEquals(0, bean.getArray().length);
+	}
+
+	public void testRemoveListChangeEvent() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, list.size());
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		list.remove(element);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.singletonList("1"),
+				Collections.EMPTY_LIST);
+	}
+
+	public void testRemovePropertyChangeEvent() throws Exception {
+		list.add("0");
+
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.remove("0");
+			}
+		});
+	}
+
+	public void testRemoveAtIndex() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(element, bean.getArray()[0]);
+
+		list.remove(0);
+		assertEquals(0, bean.getArray().length);
+	}
+
+	public void testRemoveAtIndexListChangeEvent() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, list.size());
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		list.remove(0);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.singletonList(element),
+				Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAtIndexPropertyChangeEvent() throws Exception {
+		list.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.remove(0);
+			}
+		});
+	}
+
+	public void testAddAll() throws Exception {
+		Collection elements = Arrays.asList(new String[] { "1", "2" });
+		assertEquals(0, list.size());
+
+		list.addAll(elements);
+
+		assertEquals(2, bean.getArray().length);
+	}
+
+	public void testAddAllListChangEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		assertEquals(0, list.size());
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+		assertEquals(0, listener.count);
+
+		list.addAll(elements);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.EMPTY_LIST, Arrays
+				.asList(new String[] { "1", "2" }));
+	}
+
+	public void testAddAllPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.addAll(Arrays.asList(new String[] { "0", "1" }));
+			}
+		});
+	}
+
+	public void testAddAllAtIndex() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		list.addAll(elements);
+
+		assertEquals(2, list.size());
+
+		list.addAll(2, elements);
+
+		assertEquals(4, bean.getArray().length);
+		assertEquals(elements.get(0), bean.getArray()[0]);
+		assertEquals(elements.get(1), bean.getArray()[1]);
+	}
+
+	public void testAddAllAtIndexListChangeEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		list.addAll(elements);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+
+		list.addAll(2, elements);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Arrays.asList(new Object[] { "1", "2" }), Arrays
+				.asList(new Object[] { "1", "2", "1", "2" }));
+	}
+
+	public void testAddAllAtIndexPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.addAll(0, Arrays.asList(new String[] { "1", "2" }));
+			}
+		});
+	}
+
+	public void testRemoveAll() throws Exception {
+		list.addAll(Arrays.asList(new String[] { "1", "2", "3", "4" }));
+		assertEquals(4, bean.getArray().length);
+
+		list.removeAll(Arrays.asList(new String[] { "2", "4" }));
+
+		assertEquals(2, bean.getArray().length);
+		assertEquals("1", bean.getArray()[0]);
+		assertEquals("3", bean.getArray()[1]);
+	}
+
+	public void testRemoveAllListChangeEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		list.addAll(elements);
+		list.addAll(elements);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		list.removeAll(elements);
+
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Arrays
+				.asList(new Object[] { "1", "2", "1", "2" }),
+				Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAllPropertyChangeEvent() throws Exception {
+		list.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.removeAll(Arrays.asList(new String[] { "0" }));
+			}
+		});
+	}
+
+	public void testRetainAll() throws Exception {
+		List elements = Arrays.asList(new String[] { "0", "1", "2", "3" });
+		list.addAll(elements);
+
+		assertEquals(4, bean.getArray().length);
+
+		list.retainAll(elements.subList(0, 2));
+		assertEquals(2, bean.getArray().length);
+
+		assertEquals(elements.get(0), bean.getArray()[0]);
+		assertEquals(elements.get(1), bean.getArray()[1]);
+	}
+
+	public void testRetainAllListChangeEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "0", "1", "2", "3" });
+		list.addAll(elements);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		list.retainAll(elements.subList(0, 2));
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Arrays
+				.asList(new Object[] { "0", "1", "2", "3" }), Arrays
+				.asList(new Object[] { "0", "1" }));
+	}
+
+	public void testRetainAllPropertyChangeEvent() throws Exception {
+		list.addAll(Arrays.asList(new String[] { "0", "1" }));
+
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.retainAll(Arrays.asList(new String[] { "0" }));
+			}
+		});
+	}
+
+	public void testSet() throws Exception {
+		String oldElement = "old";
+		String newElement = "new";
+		list.add(oldElement);
+
+		assertEquals(oldElement, bean.getArray()[0]);
+
+		list.set(0, newElement);
+		assertEquals(newElement, bean.getArray()[0]);
+	}
+
+	public void testMove() throws Exception {
+		String element0 = "element0";
+		String element1 = "element1";
+		list.add(element0);
+		list.add(element1);
+
+		assertEquals(2, bean.getArray().length);
+		assertEquals(element0, bean.getArray()[0]);
+		assertEquals(element1, bean.getArray()[1]);
+
+		list.move(0, 1);
+
+		assertEquals(2, bean.getArray().length);
+		assertEquals(element1, bean.getArray()[0]);
+		assertEquals(element0, bean.getArray()[1]);
+	}
+
+	public void testSetListChangeEvent() throws Exception {
+		String oldElement = "old";
+		String newElement = "new";
+		list.add(oldElement);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+		assertEquals(0, listener.count);
+
+		list.set(0, newElement);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.singletonList(oldElement),
+				Collections.singletonList(newElement));
+	}
+
+	public void testSetPropertyChangeEvent() throws Exception {
+		list.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.set(0, "1");
+			}
+		});
+	}
+
+	public void testListChangeEventFiresWhenNewListIsSet() throws Exception {
+		Bean[] elements = new Bean[] { new Bean(), new Bean() };
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		bean.setArray(elements);
+		assertEquals(1, listener.count);
+	}
+
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to be null, which indicates that an unknown
+		// change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff even if the bean implementor is lazy :-P
+
+		Bean bean = new AnnoyingBean();
+		bean.setArray(new Object[] { "old" });
+		IObservableList observable = BeansObservables.observeList(
+				new CurrentRealm(true), bean, "array");
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(observable);
+		bean.setArray(new Object[] { "new" });
+		assertEquals(1, tracker.count);
+
+		List list = new ArrayList();
+		list.add("old");
+		tracker.event.diff.applyTo(list);
+		assertEquals(Collections.singletonList("new"), list);
+	}
+
+	public void testModifyObservableList_FiresListChange() {
+		Bean bean = new Bean(new Object[] { "old" });
+		IObservableList observable = BeansObservables
+				.observeList(bean, "array");
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(observable);
+
+		observable.set(0, "new");
+
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.singletonList("old"),
+				Collections.singletonList("new"));
+	}
+
+	public void testSetBeanPropertyOutsideRealm_FiresEventInsideRealm() {
+		Bean bean = new Bean(new Object[0]);
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableList observable = BeansObservables.observeList(realm, bean,
+				"array");
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(observable);
+
+		realm.setCurrent(false);
+		bean.setArray(new Object[] { "element" });
+		assertEquals(0, tracker.count);
+
+		realm.setCurrent(true);
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList("element"));
+	}
+
+	private static void assertDiff(ListDiff diff, List oldList, List newList) {
+		oldList = new ArrayList(oldList); // defensive copy in case arg is
+		// unmodifiable
+		diff.applyTo(oldList);
+		assertEquals("applying diff to list did not produce expected result",
+				newList, oldList);
+	}
+
+	private static void assertPropertyChangeEvent(Bean bean, Runnable runnable) {
+		PropertyChangeTracker listener = new PropertyChangeTracker();
+		bean.addPropertyChangeListener(listener);
+
+		Object[] old = bean.getArray();
+		assertEquals(0, listener.count);
+
+		runnable.run();
+
+		PropertyChangeEvent event = listener.evt;
+		assertEquals("event did not fire", 1, listener.count);
+		assertEquals("array", event.getPropertyName());
+		assertTrue("old value", Arrays.equals(old, (Object[]) event
+				.getOldValue()));
+		assertTrue("new value", Arrays.equals(bean.getArray(), (Object[]) event
+				.getNewValue()));
+		assertFalse("lists are equal", Arrays.equals(bean.getArray(), old));
+	}
+
+	private static class PropertyChangeTracker implements
+			PropertyChangeListener {
+		int count;
+
+		PropertyChangeEvent evt;
+
+		public void propertyChange(PropertyChangeEvent evt) {
+			count++;
+			this.evt = evt;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				JavaBeanObservableArrayBasedListTest.class.getName());
+		suite.addTestSuite(JavaBeanObservableArrayBasedListTest.class);
+		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			String propertyName = "array";
+			Object bean = new Bean(new Object[0]);
+
+			IObservableList list = BeansObservables.observeList(realm, bean,
+					propertyName, String.class);
+			for (int i = 0; i < elementCount; i++)
+				list.add(createElement(list));
+			return list;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableList list = (IObservableList) observable;
+			list.add(createElement(list));
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedSetTest.java
new file mode 100644
index 0000000..22e88ee
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedSetTest.java
@@ -0,0 +1,415 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 221351)
+ *     Brad Reynolds - through JavaBeanObservableArrayBasedListTest.java
+ *     Matthew Hall - bug 213145, 244098, 246103, 194734, 268688, 301774
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyDescriptor;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.IBeanProperty;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 1.1
+ */
+public class JavaBeanObservableArrayBasedSetTest extends
+		AbstractDefaultRealmTestCase {
+	private IObservableSet set;
+	private IBeanObservable beanObservable;
+
+	private PropertyDescriptor propertyDescriptor;
+
+	private Bean bean;
+
+	private String propertyName;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		propertyName = "array";
+		propertyDescriptor = ((IBeanProperty) BeanProperties.set(Bean.class,
+				propertyName)).getPropertyDescriptor();
+		bean = new Bean(new HashSet());
+
+		set = BeansObservables.observeSet(SWTObservables.getRealm(Display
+				.getDefault()), bean, propertyName);
+		beanObservable = (IBeanObservable) set;
+	}
+
+	public void testGetObserved() throws Exception {
+		assertEquals(bean, beanObservable.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertEquals(propertyDescriptor, beanObservable.getPropertyDescriptor());
+	}
+
+	public void testRegistersListenerAfterFirstListenerIsAdded()
+			throws Exception {
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+		SetChangeEventTracker.observe(set);
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testRemovesListenerAfterLastListenerIsRemoved()
+			throws Exception {
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+		set.removeSetChangeListener(listener);
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testSetBeanProperty_FiresSetChangeEvents() throws Exception {
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+
+		assertEquals(0, listener.count);
+		bean.setArray(new String[] { "element" });
+		assertEquals(1, listener.count);
+	}
+
+	public void testAdd_AddsElement() throws Exception {
+		assertEquals(0, set.size());
+
+		String element = "1";
+		set.add(element);
+
+		assertEquals(1, set.size());
+		assertEquals(element, bean.getArray()[0]);
+	}
+
+	public void testAdd_SetChangeEvent() throws Exception {
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+		assertEquals(0, listener.count);
+
+		String element = "1";
+		set.add(element);
+
+		assertEquals(1, listener.count);
+		SetChangeEvent event = listener.event;
+
+		assertSame(set, event.getObservableSet());
+		assertEquals(Collections.singleton(element), event.diff.getAdditions());
+		assertEquals(Collections.EMPTY_SET, event.diff.getRemovals());
+	}
+
+	public void testAdd_FiresPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				set.add("0");
+			}
+		});
+	}
+
+	public void testRemove() throws Exception {
+		String element = "1";
+		set.add(element);
+
+		assertEquals(1, bean.getArray().length);
+		set.remove(element);
+		assertEquals(0, bean.getArray().length);
+	}
+
+	public void testRemove_SetChangeEvent() throws Exception {
+		String element = "1";
+		set.add(element);
+		assertEquals(1, set.size());
+
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+		assertEquals(0, listener.count);
+
+		set.remove(element);
+
+		assertEquals(1, listener.count);
+		SetChangeEvent event = listener.event;
+		assertEquals(set, event.getObservableSet());
+		assertEquals(Collections.singleton(element), event.diff.getRemovals());
+		assertEquals(Collections.EMPTY_SET, event.diff.getAdditions());
+	}
+
+	public void testRemovePropertyChangeEvent() throws Exception {
+		set.add("0");
+
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				set.remove("0");
+			}
+		});
+	}
+
+	public void testAddAll() throws Exception {
+		Collection elements = Arrays.asList(new String[] { "1", "2" });
+		assertEquals(0, set.size());
+
+		set.addAll(elements);
+
+		assertEquals(2, bean.getArray().length);
+	}
+
+	public void testAddAll_SetChangeEvent() throws Exception {
+		Collection elements = Arrays.asList(new String[] { "1", "2" });
+		assertEquals(0, set.size());
+
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+		assertEquals(0, listener.count);
+
+		set.addAll(elements);
+
+		assertEquals(1, listener.count);
+		SetChangeEvent event = listener.event;
+		assertEquals(set, event.getObservableSet());
+
+		assertEquals(new HashSet(elements), event.diff.getAdditions());
+		assertEquals(Collections.EMPTY_SET, event.diff.getRemovals());
+	}
+
+	public void testAddAllPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				set.addAll(Arrays.asList(new String[] { "0", "1" }));
+			}
+		});
+	}
+
+	public void testRemoveAll() throws Exception {
+		Collection elements = Arrays.asList(new String[] { "1", "2" });
+		set.addAll(elements);
+
+		assertEquals(2, bean.getArray().length);
+		set.removeAll(elements);
+
+		assertEquals(0, bean.getArray().length);
+	}
+
+	public void testRemoveAll_SetChangeEvent() throws Exception {
+		Collection elements = Arrays.asList(new String[] { "1", "2" });
+		set.addAll(elements);
+
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+		assertEquals(0, listener.count);
+
+		set.removeAll(elements);
+
+		SetChangeEvent event = listener.event;
+		assertEquals(set, event.getObservableSet());
+		assertEquals(Collections.EMPTY_SET, event.diff.getAdditions());
+		assertEquals(new HashSet(elements), event.diff.getRemovals());
+	}
+
+	public void testRemoveAllPropertyChangeEvent() throws Exception {
+		set.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				set.removeAll(Arrays.asList(new String[] { "0" }));
+			}
+		});
+	}
+
+	public void testRetainAll() throws Exception {
+		set.addAll(Arrays.asList(new String[] { "0", "1", "2", "3" }));
+
+		assertEquals(4, bean.getArray().length);
+
+		set.retainAll(Arrays.asList(new String[] { "0", "1" }));
+		assertEquals(2, bean.getArray().length);
+
+		assertTrue(set.containsAll(Arrays.asList(new String[] { "1", "0" })));
+	}
+
+	public void testRetainAll_SetChangeEvent() throws Exception {
+		set.addAll(Arrays.asList(new String[] { "0", "1", "2", "3" }));
+
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+
+		assertEquals(0, listener.count);
+		set.retainAll(Arrays.asList(new String[] { "0", "1" }));
+
+		assertEquals(1, listener.count);
+		SetChangeEvent event = listener.event;
+		assertEquals(set, event.getObservableSet());
+		assertEquals(Collections.EMPTY_SET, event.diff.getAdditions());
+		assertEquals(new HashSet(Arrays.asList(new String[] { "2", "3" })),
+				event.diff.getRemovals());
+	}
+
+	public void testRetainAllPropertyChangeEvent() throws Exception {
+		set.addAll(Arrays.asList(new String[] { "0", "1" }));
+
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				set.retainAll(Arrays.asList(new String[] { "0" }));
+			}
+		});
+	}
+
+	public void testSetChangeEventFiresWhenNewSetIsSet() throws Exception {
+		Bean[] elements = new Bean[] { new Bean(), new Bean() };
+
+		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
+
+		assertEquals(0, listener.count);
+		bean.setArray(elements);
+		assertEquals(1, listener.count);
+	}
+
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to be null, which indicates that an unknown
+		// change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff even if the bean implementor is lazy :-P
+
+		Bean bean = new AnnoyingBean();
+		bean.setArray(new Object[] { "old" });
+		IObservableSet observable = BeansObservables.observeSet(
+				new CurrentRealm(true), bean, "array");
+		SetChangeEventTracker tracker = SetChangeEventTracker
+				.observe(observable);
+		bean.setArray(new Object[] { "new" });
+		assertEquals(1, tracker.count);
+		assertEquals(Collections.singleton("old"), tracker.event.diff
+				.getRemovals());
+		assertEquals(Collections.singleton("new"), tracker.event.diff
+				.getAdditions());
+	}
+
+	public void testModifyObservableSet_FiresSetChange() {
+		Bean bean = new Bean(new Object[] {});
+		IObservableSet observable = BeansObservables.observeSet(bean, "array");
+		SetChangeEventTracker tracker = SetChangeEventTracker
+				.observe(observable);
+
+		observable.add("new");
+
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_SET, Collections
+				.singleton("new"));
+	}
+
+	public void testSetBeanPropertyOutsideRealm_FiresEventInsideRealm() {
+		Bean bean = new Bean(new Object[0]);
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableSet observable = BeansObservables.observeSet(realm, bean,
+				"array");
+		SetChangeEventTracker tracker = SetChangeEventTracker
+				.observe(observable);
+
+		realm.setCurrent(false);
+		bean.setArray(new Object[] { "element" });
+		assertEquals(0, tracker.count);
+
+		realm.setCurrent(true);
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_SET, Collections
+				.singleton("element"));
+	}
+
+	private static void assertDiff(SetDiff diff, Set oldSet, Set newSet) {
+		oldSet = new HashSet(oldSet); // defensive copy in case arg is
+		// unmodifiable
+		diff.applyTo(oldSet);
+		assertEquals("applying diff to list did not produce expected result",
+				newSet, oldSet);
+	}
+
+	private static void assertPropertyChangeEvent(Bean bean, Runnable runnable) {
+		PropertyChangeTracker listener = new PropertyChangeTracker();
+		bean.addPropertyChangeListener(listener);
+
+		Object[] old = bean.getArray();
+		assertEquals(0, listener.count);
+
+		runnable.run();
+
+		PropertyChangeEvent event = listener.evt;
+		assertEquals("event did not fire", 1, listener.count);
+		assertEquals("array", event.getPropertyName());
+		assertTrue("old value", Arrays.equals(old, (Object[]) event
+				.getOldValue()));
+		assertTrue("new value", Arrays.equals(bean.getArray(), (Object[]) event
+				.getNewValue()));
+		assertFalse("sets are equal", Arrays.equals(bean.getArray(), old));
+	}
+
+	private static class PropertyChangeTracker implements
+			PropertyChangeListener {
+		int count;
+
+		PropertyChangeEvent evt;
+
+		public void propertyChange(PropertyChangeEvent evt) {
+			count++;
+			this.evt = evt;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				JavaBeanObservableArrayBasedSetTest.class.getName());
+		suite.addTestSuite(JavaBeanObservableArrayBasedSetTest.class);
+		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			String propertyName = "array";
+			Object bean = new Bean(new Object[0]);
+
+			IObservableSet set = BeansObservables.observeSet(realm, bean,
+					propertyName, String.class);
+			for (int i = 0; i < elementCount; i++)
+				set.add(createElement(set));
+			return set;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object().toString();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableSet set = (IObservableSet) observable;
+			set.add(createElement(set));
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableListTest.java
new file mode 100644
index 0000000..9fcb2a9
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableListTest.java
@@ -0,0 +1,659 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 213145, 244098, 246103, 194734, 268688
+ *     Ovidio Mallo - bug 301774
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.IBeanProperty;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+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.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 1.1
+ */
+public class JavaBeanObservableListTest extends AbstractDefaultRealmTestCase {
+	private IObservableList list;
+	private IBeanObservable beanObservable;
+
+	private PropertyDescriptor propertyDescriptor;
+
+	private Bean bean;
+
+	private String propertyName;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		propertyName = "list";
+		propertyDescriptor = ((IBeanProperty) BeanProperties.list(Bean.class,
+				propertyName)).getPropertyDescriptor();
+		bean = new Bean(new ArrayList());
+
+		list = BeansObservables.observeList(SWTObservables.getRealm(Display
+				.getDefault()), bean, propertyName);
+		beanObservable = (IBeanObservable) list;
+	}
+
+	public void testGetObserved() throws Exception {
+		assertEquals(bean, beanObservable.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertEquals(propertyDescriptor, beanObservable.getPropertyDescriptor());
+	}
+
+	public void testRegistersListenerAfterFirstListenerIsAdded()
+			throws Exception {
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+		list.addListChangeListener(new ListChangeEventTracker());
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testRemovesListenerAfterLastListenerIsRemoved()
+			throws Exception {
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+		list.removeListChangeListener(listener);
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testFiresListChangeEvents() throws Exception {
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		bean.setList(Arrays.asList(new String[] { "value" }));
+		assertEquals(1, listener.count);
+	}
+
+	public void testAddAddsElement() throws Exception {
+		int count = list.size();
+		String element = "1";
+
+		assertEquals(0, count);
+		list.add(element);
+		assertEquals(count + 1, list.size());
+		assertEquals(element, bean.getList().get(count));
+	}
+
+	public void testAddListChangeEvent() throws Exception {
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		String element = "1";
+
+		list.add(element);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+
+		assertSame(list, event.getObservableList());
+		assertDiff(event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList("1"));
+	}
+
+	public void testAddFiresPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.add("0");
+			}
+		});
+	}
+
+	public void testAddAtIndex() throws Exception {
+		String element = "1";
+		assertEquals(0, list.size());
+
+		list.add(0, element);
+		assertEquals(element, bean.getList().get(0));
+	}
+
+	public void testAddAtIndexListChangeEvent() throws Exception {
+		String element = "1";
+		assertEquals(0, list.size());
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		list.add(0, element);
+
+		ListChangeEvent event = listener.event;
+		assertDiff(event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList("1"));
+	}
+
+	public void testAddAtIndexPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.add(0, "0");
+			}
+		});
+	}
+
+	public void testClear() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, bean.getList().size());
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.clear();
+			}
+		});
+		assertEquals(0, bean.getList().size());
+	}
+
+	public void testRemove() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, bean.getList().size());
+		list.remove(element);
+		assertEquals(0, bean.getList().size());
+	}
+
+	public void testRemoveListChangeEvent() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, list.size());
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		list.remove(element);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.singletonList("1"),
+				Collections.EMPTY_LIST);
+	}
+
+	public void testRemovePropertyChangeEvent() throws Exception {
+		list.add("0");
+
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.remove("0");
+			}
+		});
+	}
+
+	public void testRemoveAtIndex() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(element, bean.getList().get(0));
+
+		list.remove(0);
+		assertEquals(0, bean.getList().size());
+	}
+
+	public void testRemoveAtIndexListChangeEvent() throws Exception {
+		String element = "1";
+		list.add(element);
+
+		assertEquals(1, list.size());
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		list.remove(0);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.singletonList(element),
+				Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAtIndexPropertyChangeEvent() throws Exception {
+		list.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.remove(0);
+			}
+		});
+	}
+
+	public void testAddAll() throws Exception {
+		Collection elements = Arrays.asList(new String[] { "1", "2" });
+		assertEquals(0, list.size());
+
+		list.addAll(elements);
+
+		assertEquals(2, bean.getList().size());
+	}
+
+	public void testAddAllListChangEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		assertEquals(0, list.size());
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+		assertEquals(0, listener.count);
+
+		list.addAll(elements);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.EMPTY_LIST, Arrays
+				.asList(new String[] { "1", "2" }));
+	}
+
+	public void testAddAllPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.addAll(Arrays.asList(new String[] { "0", "1" }));
+			}
+		});
+	}
+
+	public void testAddAllAtIndex() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		list.addAll(elements);
+
+		assertEquals(2, list.size());
+
+		list.addAll(2, elements);
+
+		assertEquals(4, bean.getList().size());
+		assertEquals(elements.get(0), bean.getList().get(0));
+		assertEquals(elements.get(1), bean.getList().get(1));
+	}
+
+	public void testAddAllAtIndexListChangeEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		list.addAll(elements);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+
+		list.addAll(2, elements);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Arrays.asList(new Object[] { "1", "2" }), Arrays
+				.asList(new Object[] { "1", "2", "1", "2" }));
+	}
+
+	public void testAddAllAtIndexPropertyChangeEvent() throws Exception {
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.addAll(0, Arrays.asList(new String[] { "1", "2" }));
+			}
+		});
+	}
+
+	public void testRemoveAll() throws Exception {
+		list.addAll(Arrays.asList(new String[] { "1", "2", "3", "4" }));
+		assertEquals(4, bean.getList().size());
+
+		list.removeAll(Arrays.asList(new String[] { "2", "4" }));
+
+		assertEquals(2, bean.getList().size());
+		assertEquals("1", bean.getList().get(0));
+		assertEquals("3", bean.getList().get(1));
+	}
+
+	public void testRemoveAllListChangeEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+		list.addAll(elements);
+		list.addAll(elements);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		list.removeAll(elements);
+
+		ListChangeEvent event = listener.event;
+		assertEquals(list, event.getObservableList());
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Arrays
+				.asList(new Object[] { "1", "2", "1", "2" }),
+				Collections.EMPTY_LIST);
+	}
+
+	public void testRemoveAllPropertyChangeEvent() throws Exception {
+		list.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.removeAll(Arrays.asList(new String[] { "0" }));
+			}
+		});
+	}
+
+	public void testRetailAll() throws Exception {
+		List elements = Arrays.asList(new String[] { "0", "1", "2", "3" });
+		list.addAll(elements);
+
+		assertEquals(4, bean.getList().size());
+
+		list.retainAll(elements.subList(0, 2));
+		assertEquals(2, bean.getList().size());
+
+		assertEquals(elements.get(0), bean.getList().get(0));
+		assertEquals(elements.get(1), bean.getList().get(1));
+	}
+
+	public void testRetainAllListChangeEvent() throws Exception {
+		List elements = Arrays.asList(new String[] { "0", "1", "2", "3" });
+		list.addAll(elements);
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		list.retainAll(elements.subList(0, 2));
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Arrays
+				.asList(new Object[] { "0", "1", "2", "3" }), Arrays
+				.asList(new Object[] { "0", "1" }));
+	}
+
+	public void testRetainAllPropertyChangeEvent() throws Exception {
+		list.addAll(Arrays.asList(new String[] { "0", "1" }));
+
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.retainAll(Arrays.asList(new String[] { "0" }));
+			}
+		});
+	}
+
+	public void testSet() throws Exception {
+		String oldElement = "old";
+		String newElement = "new";
+		list.add(oldElement);
+
+		assertEquals(oldElement, bean.getList().get(0));
+
+		list.set(0, newElement);
+		assertEquals(newElement, bean.getList().get(0));
+	}
+
+	public void testMove() throws Exception {
+		String element0 = "element0";
+		String element1 = "element1";
+		list.add(element0);
+		list.add(element1);
+
+		assertEquals(2, bean.getList().size());
+		assertEquals(element0, bean.getList().get(0));
+		assertEquals(element1, bean.getList().get(1));
+
+		list.move(0, 1);
+
+		assertEquals(2, bean.getList().size());
+		assertEquals(element1, bean.getList().get(0));
+		assertEquals(element0, bean.getList().get(1));
+	}
+
+	public void testSetListChangeEvent() throws Exception {
+		String oldElement = "old";
+		String newElement = "new";
+		list.add(oldElement);
+
+		ListChangeEventTracker listener = ListChangeEventTracker.observe(list);
+		assertEquals(0, listener.count);
+
+		list.set(0, newElement);
+
+		assertEquals(1, listener.count);
+		ListChangeEvent event = listener.event;
+		assertSame(list, event.getObservableList());
+
+		assertDiff(event.diff, Collections.singletonList(oldElement),
+				Collections.singletonList(newElement));
+	}
+
+	public void testSetPropertyChangeEvent() throws Exception {
+		list.add("0");
+		assertPropertyChangeEvent(bean, new Runnable() {
+			public void run() {
+				list.set(0, "1");
+			}
+		});
+	}
+
+	public void testListChangeEventFiresWhenNewListIsSet() throws Exception {
+		List elements = Arrays.asList(new String[] { "1", "2" });
+
+		ListChangeEventTracker listener = new ListChangeEventTracker();
+		list.addListChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		bean.setList(elements);
+		assertEquals(1, listener.count);
+	}
+
+	public void testConstructor_RegistersListener() throws Exception {
+		Bean bean = new Bean();
+		IObservableList observable = BeansObservables.observeList(Realm
+				.getDefault(), bean, "list");
+
+		assertFalse(bean.hasListeners("list"));
+		ChangeEventTracker.observe(observable);
+		assertTrue(bean.hasListeners("list"));
+	}
+
+	public void testConstructor_SkipsRegisterListener() throws Exception {
+		Bean bean = new Bean();
+		IObservableList observable = PojoObservables.observeList(Realm
+				.getDefault(), bean, "list");
+
+		assertFalse(bean.hasListeners("list"));
+		ChangeEventTracker.observe(observable);
+		assertFalse(bean.hasListeners("list"));
+	}
+
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to be null, which indicates that an unknown
+		// change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff even if the bean implementor is lazy :-P
+
+		Bean bean = new AnnoyingBean();
+		bean.setList(Collections.singletonList("old"));
+		IObservableList observable = BeansObservables.observeList(
+				new CurrentRealm(true), bean, "list");
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(observable);
+		bean.setList(Collections.singletonList("new"));
+
+		assertEquals(1, tracker.count);
+
+		List list = new ArrayList();
+		list.add("old");
+		tracker.event.diff.applyTo(list);
+		assertEquals(Collections.singletonList("new"), list);
+	}
+
+	public void testModifyObservableList_FiresListChange() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableList observable = BeansObservables.observeList(bean, "list");
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(observable);
+
+		Object element = new Object();
+		observable.add(element);
+
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList(element));
+	}
+
+	public void testSetBeanPropertyOutsideRealm_FiresEventInsideRealm() {
+		Bean bean = new Bean(Collections.EMPTY_LIST);
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableList observable = BeansObservables.observeList(realm, bean,
+				"list");
+		ListChangeEventTracker tracker = ListChangeEventTracker
+				.observe(observable);
+
+		realm.setCurrent(false);
+		bean.setList(Collections.singletonList("element"));
+		assertEquals(0, tracker.count);
+
+		realm.setCurrent(true);
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList("element"));
+	}
+
+	/**
+	 * Makes sure that the list set on the Bean model after changing the
+	 * observable list is modifiable (see bugs 285307 and 301774).
+	 */
+	public void testUpdatedBeanListIsModifiable() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableList observable = BeansObservables.observeList(bean, "list");
+
+		observable.add(new Object());
+		bean.getList().clear();
+	}
+
+	/**
+	 * Makes sure that the list set on the Pojo model after changing the
+	 * observable list is modifiable (see bugs 285307 and 301774).
+	 */
+	public void testUpdatedPojoListIsModifiable() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableList observable = PojoObservables.observeList(bean, "list");
+
+		observable.add(new Object());
+		bean.getList().clear();
+	}
+
+	private static void assertDiff(ListDiff diff, List oldList, List newList) {
+		oldList = new ArrayList(oldList); // defensive copy in case arg is
+		// unmodifiable
+		diff.applyTo(oldList);
+		assertEquals("applying diff to list did not produce expected result",
+				newList, oldList);
+	}
+
+	private static void assertPropertyChangeEvent(Bean bean, Runnable runnable) {
+		PropertyChangeTracker listener = new PropertyChangeTracker();
+		bean.addPropertyChangeListener(listener);
+
+		List old = bean.getList();
+		assertEquals(0, listener.count);
+
+		runnable.run();
+
+		PropertyChangeEvent event = listener.evt;
+		assertEquals("event did not fire", 1, listener.count);
+		assertEquals("list", event.getPropertyName());
+		assertEquals("old value", old, event.getOldValue());
+		assertEquals("new value", bean.getList(), event.getNewValue());
+		assertFalse("lists are equal", bean.getList().equals(old));
+	}
+
+	private static class PropertyChangeTracker implements
+			PropertyChangeListener {
+		int count;
+
+		PropertyChangeEvent evt;
+
+		public void propertyChange(PropertyChangeEvent evt) {
+			count++;
+			this.evt = evt;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(JavaBeanObservableListTest.class
+				.getName());
+		suite.addTestSuite(JavaBeanObservableListTest.class);
+		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			String propertyName = "list";
+			Object bean = new Bean(new ArrayList());
+
+			IObservableList list = BeansObservables.observeList(realm, bean,
+					propertyName, String.class);
+			for (int i = 0; i < elementCount; i++)
+				list.add(createElement(list));
+			return list;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object().toString();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableList list = (IObservableList) observable;
+			list.add(createElement(list));
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableMapTest.java
new file mode 100644
index 0000000..99d3d7d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableMapTest.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 213145, 241585, 246103, 194734, 268688
+ *     Ovidio Mallo - bug 247741
+ *******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.IBeanProperty;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.MapDiff;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.tests.databinding.observable.ThreadRealm;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class JavaBeanObservableMapTest extends AbstractDefaultRealmTestCase {
+	private Bean model1;
+
+	private Bean model2;
+
+	private WritableSet set;
+
+	private PropertyDescriptor propertyDescriptor;
+
+	private IObservableMap map;
+	private IBeanObservable beanObservable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		ThreadRealm realm = new ThreadRealm();
+		realm.init(Thread.currentThread());
+		model1 = new Bean("1");
+		model2 = new Bean("2");
+
+		set = new WritableSet(realm, new HashSet(), Bean.class);
+		set.add(model1);
+		set.add(model2);
+
+		String propertyName = "value";
+		propertyDescriptor = ((IBeanProperty) BeanProperties.value(Bean.class,
+				propertyName)).getPropertyDescriptor();
+		map = BeansObservables.observeMap(set, Bean.class, propertyName);
+		beanObservable = (IBeanObservable) map;
+	}
+
+	public void testGetValue() throws Exception {
+		assertEquals(
+				"The 'value' from the map should be the value of the property of the model.",
+				model1.getValue(), map.get(model1));
+	}
+
+	public void testGetValue_KeyOutOfDomain() {
+		Bean model3 = new Bean("3");
+		assertFalse(map.containsKey(model3));
+		assertFalse(model3.getValue().equals(map.get(model3)));
+	}
+
+	public void testSetValueNotifications() throws Exception {
+		String oldValue = model1.getValue();
+		String newValue = model1.getValue() + model1.getValue();
+		MapChangeEventTracker listener = new MapChangeEventTracker();
+
+		map.addMapChangeListener(listener);
+		assertEquals(0, listener.count);
+		model1.setValue(newValue);
+		assertEquals(1, listener.count);
+		assertTrue(listener.event.diff.getChangedKeys().contains(model1));
+		assertEquals(newValue, listener.event.diff.getNewValue(model1));
+		assertEquals(oldValue, listener.event.diff.getOldValue(model1));
+		assertFalse(listener.event.diff.getAddedKeys().contains(model1));
+		assertFalse(listener.event.diff.getRemovedKeys().contains(model1));
+	}
+
+	public void testPutValue() throws Exception {
+		String oldValue = model1.getValue();
+		String newValue = model1.getValue() + model1.getValue();
+		MapChangeEventTracker listener = new MapChangeEventTracker();
+		map.addMapChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		map.put(model1, newValue);
+		assertEquals(1, listener.count);
+		assertEquals(newValue, model1.getValue());
+		assertEquals(oldValue, listener.event.diff.getOldValue(model1));
+		assertEquals(newValue, listener.event.diff.getNewValue(model1));
+		assertFalse(listener.event.diff.getAddedKeys().contains(model1));
+		assertTrue(listener.event.diff.getChangedKeys().contains(model1));
+		assertFalse(listener.event.diff.getRemovedKeys().contains(model1));
+	}
+
+	public void testAddKey() throws Exception {
+		MapChangeEventTracker listener = new MapChangeEventTracker();
+		map.addMapChangeListener(listener);
+
+		Bean model3 = new Bean("3");
+
+		assertEquals(0, listener.count);
+		set.add(model3);
+		assertEquals(1, listener.count);
+		assertTrue(listener.event.diff.getAddedKeys().contains(model3));
+		assertEquals(model3.getValue(), map.get(model3));
+
+		String newValue = model3.getValue() + model3.getValue();
+		model3.setValue(newValue);
+		assertEquals(2, listener.count);
+		assertEquals(3, map.size());
+	}
+
+	public void testRemoveKey() throws Exception {
+		MapChangeEventTracker listener = new MapChangeEventTracker();
+		map.addMapChangeListener(listener);
+
+		assertEquals(0, listener.count);
+		set.remove(model1);
+		assertEquals(1, listener.count);
+		assertFalse(listener.event.diff.getAddedKeys().contains(model1));
+		assertFalse(listener.event.diff.getChangedKeys().contains(model1));
+		assertTrue(listener.event.diff.getRemovedKeys().contains(model1));
+		assertEquals(1, map.size());
+	}
+
+	public void testGetObserved() throws Exception {
+		assertEquals(set, beanObservable.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertEquals(propertyDescriptor, beanObservable.getPropertyDescriptor());
+	}
+
+	public void testConstructor_SkipRegisterListeners() throws Exception {
+		Realm realm = new CurrentRealm(true);
+		WritableSet set = new WritableSet(realm);
+		Bean bean = new Bean();
+		set.add(bean);
+
+		IObservableMap observable = PojoObservables.observeMap(set, Bean.class,
+				"value");
+		assertFalse(bean.hasListeners("value"));
+		ChangeEventTracker.observe(observable);
+
+		assertFalse(bean.hasListeners("value"));
+	}
+
+	public void testConstructor_RegistersListeners() throws Exception {
+		Realm realm = new CurrentRealm(true);
+		WritableSet set = new WritableSet(realm);
+		Bean bean = new Bean();
+		set.add(bean);
+
+		IObservableMap observable = BeansObservables.observeMap(set,
+				Bean.class, "value");
+		assertFalse(bean.hasListeners("value"));
+		ChangeEventTracker.observe(observable);
+
+		assertTrue(bean.hasListeners("value"));
+	}
+
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to be null, which indicates that an unknown
+		// change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff even if the bean implementor is lazy :-P
+
+		WritableSet set = new WritableSet(new CurrentRealm(true));
+
+		Bean bean = new AnnoyingBean();
+		bean.setValue("old");
+		set.add(bean);
+
+		IObservableMap map = BeansObservables.observeMap(set, Bean.class,
+				"value");
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		bean.setValue("new");
+
+		assertEquals(1, tracker.count);
+
+		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getAddedKeys());
+		assertEquals(Collections.singleton(bean), tracker.event.diff
+				.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getRemovedKeys());
+
+		assertEquals("old", tracker.event.diff.getOldValue(bean));
+		assertEquals("new", tracker.event.diff.getNewValue(bean));
+	}
+
+	public void testModifyObservableMap_FiresMapChange() {
+		Bean bean = new Bean(Collections.singletonMap("key", "oldValue"));
+		IObservableMap observable = BeansObservables.observeMap(bean, "map");
+		MapChangeEventTracker tracker = MapChangeEventTracker
+				.observe(observable);
+
+		observable.put("key", "newValue");
+
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.singletonMap("key",
+				"oldValue"), Collections.singletonMap("key", "newValue"));
+	}
+
+	public void testSetBeanPropertyOutsideRealm_FiresEventInsideRealm() {
+		Bean bean = new Bean(Collections.EMPTY_MAP);
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableMap observable = BeansObservables.observeMap(realm, bean,
+				"map");
+		MapChangeEventTracker tracker = MapChangeEventTracker
+				.observe(observable);
+
+		realm.setCurrent(false);
+		bean.setMap(Collections.singletonMap("key", "value"));
+		assertEquals(0, tracker.count);
+
+		realm.setCurrent(true);
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_MAP, Collections
+				.singletonMap("key", "value"));
+	}
+
+	private static void assertDiff(MapDiff diff, Map oldMap, Map newMap) {
+		oldMap = new HashMap(oldMap); // defensive copy in case arg is
+		// unmodifiable
+		diff.applyTo(oldMap);
+		assertEquals("applying diff to list did not produce expected result",
+				newMap, oldMap);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(JavaBeanObservableMapTest.class
+				.getName());
+		suite.addTestSuite(JavaBeanObservableMapTest.class);
+		return suite;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableSetTest.java
new file mode 100644
index 0000000..cf0aec5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableSetTest.java
@@ -0,0 +1,246 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 213145, 244098, 246103, 194734, 268688
+ *     Ovidio Mallo - bugs 247741, 301774
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.IBeanProperty;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * @since 3.3
+ */
+public class JavaBeanObservableSetTest extends AbstractDefaultRealmTestCase {
+	private IObservableSet observableSet;
+	private IBeanObservable beanObservable;
+	private Bean bean;
+	private PropertyDescriptor propertyDescriptor;
+	private String propertyName;
+	private SetChangeEventTracker listener;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		bean = new Bean();
+		propertyName = "set";
+		propertyDescriptor = ((IBeanProperty) BeanProperties.set(Bean.class,
+				propertyName)).getPropertyDescriptor();
+
+		observableSet = BeansObservables
+				.observeSet(SWTObservables.getRealm(Display.getDefault()),
+						bean, propertyName, Bean.class);
+		beanObservable = (IBeanObservable) observableSet;
+		listener = new SetChangeEventTracker();
+	}
+
+	public void testGetObserved() throws Exception {
+		assertEquals(bean, beanObservable.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertEquals(propertyDescriptor, beanObservable.getPropertyDescriptor());
+	}
+
+	public void testGetElementType() throws Exception {
+		assertEquals(Bean.class, observableSet.getElementType());
+	}
+
+	public void testRegistersListenerAfterFirstListenerIsAdded()
+			throws Exception {
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+		observableSet.addSetChangeListener(new SetChangeEventTracker());
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testRemovesListenerAfterLastListenerIsRemoved()
+			throws Exception {
+		observableSet.addSetChangeListener(listener);
+
+		assertTrue(bean.changeSupport.hasListeners(propertyName));
+		observableSet.removeSetChangeListener(listener);
+		assertFalse(bean.changeSupport.hasListeners(propertyName));
+	}
+
+	public void testFiresChangeEvents() throws Exception {
+		observableSet.addSetChangeListener(listener);
+		assertEquals(0, listener.count);
+		bean.setSet(new HashSet(Arrays.asList(new String[] { "1" })));
+		assertEquals(1, listener.count);
+	}
+
+	public void testConstructor_RegisterListeners() throws Exception {
+		bean = new Bean();
+		observableSet = BeansObservables.observeSet(new CurrentRealm(true),
+				bean, propertyName);
+		assertFalse(bean.hasListeners(propertyName));
+		ChangeEventTracker.observe(observableSet);
+		assertTrue(bean.hasListeners(propertyName));
+	}
+
+	public void testConstructor_SkipsRegisterListeners() throws Exception {
+		bean = new Bean();
+
+		observableSet = PojoObservables.observeSet(new CurrentRealm(true),
+				bean, propertyName);
+		assertFalse(bean.hasListeners(propertyName));
+		ChangeEventTracker.observe(observableSet);
+		assertFalse(bean.hasListeners(propertyName));
+	}
+
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to be null, which indicates that an unknown
+		// change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff even if the bean implementor is lazy :-P
+
+		Bean bean = new AnnoyingBean();
+		bean.setSet(Collections.singleton("old"));
+		IObservableSet observable = BeansObservables.observeSet(
+				new CurrentRealm(true), bean, "set");
+		SetChangeEventTracker tracker = SetChangeEventTracker
+				.observe(observable);
+		bean.setSet(Collections.singleton("new"));
+		assertEquals(1, tracker.count);
+		assertEquals(Collections.singleton("old"), tracker.event.diff
+				.getRemovals());
+		assertEquals(Collections.singleton("new"), tracker.event.diff
+				.getAdditions());
+	}
+
+	public void testModifyObservableSet_FiresSetChange() {
+		Bean bean = new Bean(new HashSet());
+		IObservableSet observable = BeansObservables.observeSet(bean, "set");
+		SetChangeEventTracker tracker = SetChangeEventTracker
+				.observe(observable);
+
+		Object element = new Object();
+		observable.add(element);
+
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_SET, Collections
+				.singleton(element));
+	}
+
+	public void testSetBeanPropertyOutsideRealm_FiresEventInsideRealm() {
+		Bean bean = new Bean(Collections.EMPTY_SET);
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableSet observable = BeansObservables.observeSet(realm, bean,
+				"set");
+		SetChangeEventTracker tracker = SetChangeEventTracker
+				.observe(observable);
+
+		realm.setCurrent(false);
+		bean.setSet(Collections.singleton("element"));
+		assertEquals(0, tracker.count);
+
+		realm.setCurrent(true);
+		assertEquals(1, tracker.count);
+		assertDiff(tracker.event.diff, Collections.EMPTY_SET, Collections
+				.singleton("element"));
+	}
+
+	/**
+	 * Makes sure that the set set on the Bean model after changing the
+	 * observable set is modifiable (see bugs 285307 and 301774).
+	 */
+	public void testUpdatedBeanSetIsModifiable() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableSet observable = BeansObservables.observeSet(bean, "set");
+
+		observable.add(new Object());
+		bean.getSet().clear();
+	}
+
+	/**
+	 * Makes sure that the set set on the Pojo model after changing the
+	 * observable set is modifiable (see bugs 285307 and 301774).
+	 */
+	public void testUpdatedPojoSetIsModifiable() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableSet observable = PojoObservables.observeSet(bean, "set");
+
+		observable.add(new Object());
+		bean.getSet().clear();
+	}
+
+	private static void assertDiff(SetDiff diff, Set oldSet, Set newSet) {
+		oldSet = new HashSet(oldSet); // defensive copy in case arg is
+		// unmodifiable
+		diff.applyTo(oldSet);
+		assertEquals("applying diff to list did not produce expected result",
+				newSet, oldSet);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(JavaBeanObservableSetTest.class
+				.getName());
+		suite.addTestSuite(JavaBeanObservableSetTest.class);
+		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			Bean bean = new Bean();
+			String propertyName = "set";
+
+			IObservableSet set = BeansObservables.observeSet(realm, bean,
+					propertyName, String.class);
+			for (int i = 0; i < elementCount; i++)
+				set.add(createElement(set));
+			return set;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableSet set = (IObservableSet) observable;
+			set.add(createElement(set));
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableValueTest.java
new file mode 100644
index 0000000..76be49b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableValueTest.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 171616
+ *     Katarzyna Marszalek - test case for bug 198519
+ *     Matthew Hall - bug 213145, 246103, 194734, 268688
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.beans.PropertyDescriptor;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.IBeanObservable;
+import org.eclipse.core.databinding.beans.IBeanProperty;
+import org.eclipse.core.databinding.beans.PojoObservables;
+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.Realm;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ */
+public class JavaBeanObservableValueTest extends AbstractDefaultRealmTestCase {
+	private Bean bean;
+	private IObservableValue observableValue;
+	private IBeanObservable beanObservable;
+	private PropertyDescriptor propertyDescriptor;
+	private String propertyName;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		bean = new Bean();
+		propertyName = "value";
+		propertyDescriptor = ((IBeanProperty) BeanProperties.value(Bean.class,
+				propertyName)).getPropertyDescriptor();
+		observableValue = BeansObservables.observeValue(bean, propertyName);
+		beanObservable = (IBeanObservable) observableValue;
+	}
+
+	public void testGetObserved() throws Exception {
+		assertEquals(bean, beanObservable.getObserved());
+	}
+
+	public void testGetPropertyDescriptor() throws Exception {
+		assertEquals(propertyDescriptor, beanObservable.getPropertyDescriptor());
+	}
+
+	public void testSetValueThrowsExceptionThrownByBean() throws Exception {
+		ThrowsSetException temp = new ThrowsSetException();
+		IObservableValue observable = BeansObservables.observeValue(temp,
+				"value");
+
+		try {
+			observable.setValue("");
+			fail("exception should have been thrown");
+		} catch (RuntimeException e) {
+			assertEquals(temp.thrownException, e.getCause());
+		}
+	}
+
+	public void testGetValueThrowsExceptionThrownByBean() throws Exception {
+		ThrowsGetException temp = new ThrowsGetException();
+		IObservableValue observable = BeansObservables.observeValue(temp,
+				"value");
+
+		try {
+			observable.getValue();
+			fail("exception should have been thrown");
+		} catch (RuntimeException e) {
+			assertEquals(temp.thrownException, e.getCause());
+		}
+	}
+
+	public void testBug198519() {
+		final SimplePerson person = new SimplePerson();
+		final ComputedValue cv = new ComputedValue() {
+			final IObservableValue name = BeansObservables.observeValue(person,
+					"name"); //$NON-NLS-1$
+
+			protected Object calculate() {
+				return Boolean.valueOf(name.getValue() != null);
+			}
+		};
+		cv.addChangeListener(new IChangeListener() {
+			public void handleChange(ChangeEvent event) {
+				cv.getValue();
+			}
+		});
+		person.setName("foo");
+	}
+
+	public void testConstructor_RegistersListeners() throws Exception {
+		IObservableValue observable = BeansObservables.observeValue(bean,
+				propertyName);
+		ChangeEventTracker.observe(observable);
+
+		assertTrue(bean.hasListeners(propertyName));
+	}
+
+	public void testConstructor_SkipRegisterListeners() throws Exception {
+		IObservableValue observable = PojoObservables.observeValue(bean,
+				propertyName);
+		ChangeEventTracker.observe(observable);
+
+		assertFalse(bean.hasListeners(propertyName));
+	}
+
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to
+		// be null, which indicates that an unknown change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff
+		// even if the bean implementor is lazy :-P
+
+		Bean bean = new AnnoyingBean();
+		bean.setValue("old");
+		IObservableValue observable = BeansObservables.observeValue(bean,
+				"value");
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(observable);
+		bean.setValue("new");
+		assertEquals(1, tracker.count);
+		assertEquals("old", tracker.event.diff.getOldValue());
+		assertEquals("new", tracker.event.diff.getNewValue());
+	}
+
+	public void testSetBeanPropertyOutsideRealm_FiresEventInsideRealm() {
+		Bean bean = new Bean("old");
+		CurrentRealm realm = new CurrentRealm(true);
+		IObservableValue observable = BeansObservables.observeValue(realm,
+				bean, "value");
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(observable);
+
+		realm.setCurrent(false);
+		bean.setValue("new");
+		assertEquals(0, tracker.count);
+
+		realm.setCurrent(true);
+		assertEquals(1, tracker.count);
+		assertEquals(Diffs.createValueDiff("old", "new"), tracker.event.diff);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(JavaBeanObservableValueTest.class
+				.getName());
+		suite.addTestSuite(JavaBeanObservableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Bean bean;
+
+		public void setUp() {
+			super.setUp();
+
+			bean = new Bean("");
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return BeansObservables.observeValue(realm, bean, "value");
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return observable.getValue() + "a";
+		}
+	}
+
+	/**
+	 * Throws an exception when the value is set.
+	 * 
+	 * @since 3.2
+	 */
+	/* package */class ThrowsSetException {
+		private String value;
+
+		/* package */NullPointerException thrownException;
+
+		public void setValue(String value) {
+			throw thrownException = new NullPointerException();
+		}
+
+		public String getValue() {
+			return value;
+		}
+	}
+
+	/* package */class ThrowsGetException {
+		public String value;
+
+		/* package */NullPointerException thrownException;
+
+		public String getValue() {
+			throw thrownException = new NullPointerException();
+		}
+
+		public void setValue(String value) {
+			this.value = value;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanPropertyObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanPropertyObservableMapTest.java
new file mode 100644
index 0000000..92dbd25
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanPropertyObservableMapTest.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 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 246103)
+ *     Ovidio Mallo - bug 301774
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.beans;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class JavaBeanPropertyObservableMapTest extends
+		AbstractDefaultRealmTestCase {
+	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
+		// The java bean spec allows the old and new values in a
+		// PropertyChangeEvent to be null, which indicates that an unknown
+		// change occured.
+
+		// This test ensures that JavaBeanObservableValue fires the correct
+		// value diff even if the bean implementor is lazy :-P
+
+		Bean bean = new AnnoyingBean();
+		bean.setMap(Collections.singletonMap("key", "old"));
+
+		IObservableMap map = BeansObservables.observeMap(
+				new CurrentRealm(true), bean, "map");
+		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
+
+		bean.setMap(Collections.singletonMap("key", "new"));
+
+		assertEquals(1, tracker.count);
+
+		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getAddedKeys());
+		assertEquals(Collections.singleton("key"), tracker.event.diff
+				.getChangedKeys());
+		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getRemovedKeys());
+
+		assertEquals("old", tracker.event.diff.getOldValue("key"));
+		assertEquals("new", tracker.event.diff.getNewValue("key"));
+	}
+
+	/**
+	 * Makes sure that the map set on the Bean model after changing the
+	 * observable map is modifiable (see bugs 285307 and 301774).
+	 */
+	public void testUpdatedBeanMapIsModifiable() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableMap observable = BeansObservables.observeMap(bean, "map");
+
+		observable.put(new Object(), new Object());
+		bean.getMap().clear();
+	}
+
+	/**
+	 * Makes sure that the map set on the Pojo model after changing the
+	 * observable map is modifiable (see bugs 285307 and 301774).
+	 */
+	public void testUpdatedPojoMapIsModifiable() {
+		Bean bean = new Bean(new ArrayList());
+		IObservableMap observable = PojoObservables.observeMap(bean, "map");
+
+		observable.put(new Object(), new Object());
+		bean.getMap().clear();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/DateConversionSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/DateConversionSupportTest.java
new file mode 100644
index 0000000..09a6bdb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/DateConversionSupportTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 121110
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.BindingMessages;
+import org.eclipse.core.internal.databinding.conversion.DateConversionSupport;
+
+import com.ibm.icu.text.DateFormat;
+import com.ibm.icu.text.SimpleDateFormat;
+
+/**
+ * @since 1.1
+ */
+public class DateConversionSupportTest extends TestCase {
+	public void testDatePatternIsExternalized() throws Exception {
+		StubConverter stub = new StubConverter();
+		String key = "DateFormat_DateTime";
+		String format = BindingMessages.getString(key);
+		
+		assertFalse("format is defined", key.equals(format));
+		SimpleDateFormat dateFormat = (SimpleDateFormat) stub.getDateFormat(0);
+		assertEquals(format, dateFormat.toPattern());
+	}
+	
+	public void testTimePatternIsExternalized() throws Exception {
+		StubConverter stub = new StubConverter();
+		String key = "DateFormat_Time";
+		String format = BindingMessages.getString(key);
+		
+		assertFalse("format is defined", key.equals(format));
+		SimpleDateFormat dateFormat = (SimpleDateFormat) stub.getDateFormat(1);
+		assertEquals(format, dateFormat.toPattern());
+	}
+	
+	public void testFormat_NullDate() {
+		StubConverter stub = new StubConverter();
+		assertNull(stub.format(null));
+	}
+	
+	static class StubConverter extends DateConversionSupport {
+		protected DateFormat getDateFormat(int index) {
+			return super.getDateFormat(index);
+		}
+		
+		protected String format(Date date) {
+			return super.format(date);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/IdentityConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/IdentityConverterTest.java
new file mode 100755
index 0000000..8076183
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/IdentityConverterTest.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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 116920
+ *     Matt Carter - bug 197679
+ *******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.BindingException;
+import org.eclipse.core.internal.databinding.conversion.IdentityConverter;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class IdentityConverterTest extends TestCase {
+
+	private IdentityConverter c;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		c = new IdentityConverter(Integer.TYPE, Integer.TYPE);
+	}
+
+	public void testIsPrimitiveTypeMatchedWithBoxed() throws Exception {
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Integer.class,
+				Integer.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Integer.TYPE,
+				Integer.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Byte.class, Byte.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Byte.TYPE, Byte.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Short.class, Short.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Short.TYPE, Short.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Long.class, Long.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Long.TYPE, Long.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Float.class, Float.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Float.TYPE, Float.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Double.class, Double.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Double.TYPE, Double.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Boolean.class,
+				Boolean.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Boolean.TYPE,
+				Boolean.class));
+
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Character.class,
+				Character.TYPE));
+		assertTrue(c.isPrimitiveTypeMatchedWithBoxed(Character.TYPE,
+				Character.class));
+
+		assertFalse(c.isPrimitiveTypeMatchedWithBoxed(Boolean.class,
+				Integer.TYPE));
+	}
+
+	public void testConvert_NullToPrimitive() {
+		IdentityConverter p2b = new IdentityConverter(Float.TYPE, Float.TYPE);
+		try {
+			p2b.convert(null);
+			fail("Should have thrown an exception");
+		} catch (BindingException b) {
+			// success
+		}
+	}
+
+	public void testConvert_PrimitiveToBoxed() throws Exception {
+		IdentityConverter p2b = new IdentityConverter(Float.TYPE, Float.class);
+		assertEquals("4.2", new Float(4.2), p2b.convert(new Float(4.2)));
+	}
+
+	public void testConvert_BoxedToPrimitive() throws Exception {
+		IdentityConverter p2b = new IdentityConverter(Float.class, Float.TYPE);
+		assertEquals("4.2", new Float(4.2), p2b.convert(new Float(4.2)));
+	}
+
+	public void testConvert_PrimitiveToPrimitive() throws Exception {
+		IdentityConverter p2b = new IdentityConverter(Float.TYPE, Float.TYPE);
+		assertEquals("4.2", new Float(4.2), p2b.convert(new Float(4.2)));
+	}
+
+	public void testConvert_BoxedToBoxed() throws Exception {
+		IdentityConverter p2b = new IdentityConverter(Float.class, Float.class);
+		assertEquals("4.2", new Float(4.2), p2b.convert(new Float(4.2)));
+	}
+
+	public static class Person {
+		public String foo = "blah";
+	}
+
+	public static class Animal {
+		public String name = "fido";
+	}
+
+	public void test_Convert_ValidAssignment() throws Exception {
+		IdentityConverter pc = new IdentityConverter(Object.class, Person.class);
+		Person orig = new Person();
+		Object person = pc.convert(orig);
+		assertTrue("Person class", person.getClass().equals(Person.class));
+		assertTrue("Need correct Person", person.equals(orig));
+	}
+
+	public void test_Convert_ValidAssignment2() throws Exception {
+		IdentityConverter pc = new IdentityConverter(Person.class, Object.class);
+		Person orig = new Person();
+		Object person = pc.convert(orig);
+		assertTrue("Person class", person.getClass().equals(Person.class));
+		assertTrue("Need correct Person", person.equals(orig));
+	}
+
+	public void testConvert_InvalidAssignment() throws Exception {
+		IdentityConverter pc = new IdentityConverter(Object.class, Person.class);
+		try {
+			pc.convert(new Animal());
+			fail("Should have gotten an exception");
+		} catch (Exception e) {
+			// success
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/IntegerToStringConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/IntegerToStringConverterTest.java
new file mode 100644
index 0000000..a86de2d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/IntegerToStringConverterTest.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.conversion.IntegerToStringConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class IntegerToStringConverterTest extends TestCase {
+	private NumberFormat integerFormat;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		integerFormat = NumberFormat.getIntegerInstance();
+	}
+
+	public void testFromTypeShort() throws Exception {
+		assertEquals(Short.class, IntegerToStringConverter.fromShort(false)
+				.getFromType());
+		assertEquals(Short.TYPE, IntegerToStringConverter.fromShort(true)
+				.getFromType());
+		assertEquals(Byte.class, IntegerToStringConverter.fromByte(false)
+				.getFromType());
+		assertEquals(Byte.TYPE, IntegerToStringConverter.fromByte(true)
+				.getFromType());
+	}
+
+	public void testToTypeIsStringClass() throws Exception {
+		assertEquals(String.class, IntegerToStringConverter.fromShort(false)
+				.getToType());
+	}
+
+	public void testConvertShortToString() throws Exception {
+		Short value = new Short((short) 1);
+		String expected = integerFormat.format(value);
+
+		IntegerToStringConverter converter = IntegerToStringConverter
+				.fromShort(integerFormat, false);
+		String result = (String) converter.convert(value);
+		assertEquals(expected, result);
+	}
+
+	public void testConvertByteToString() throws Exception {
+		Byte value = new Byte((byte) 1);
+		String expected = integerFormat.format(value);
+
+		IntegerToStringConverter converter = IntegerToStringConverter.fromByte(
+				integerFormat, false);
+		String result = (String) converter.convert(value);
+		assertEquals(expected, result);
+	}
+
+	public void testNullSourceConvertsToEmptyString() throws Exception {
+		IntegerToStringConverter converter = IntegerToStringConverter
+				.fromByte(false);
+		assertEquals("", converter.convert(null));
+	}
+
+	public void testIllegalArgumentExceptionIfSourceIsNotExpectedType() throws Exception {
+		IntegerToStringConverter converter = IntegerToStringConverter.fromByte(false);
+		try {
+			converter.convert(new Integer(1));
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToBigDecimalTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToBigDecimalTest.java
new file mode 100644
index 0000000..9cdabd9
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToBigDecimalTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import java.math.BigDecimal;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToBigDecimalTest extends NumberToNumberTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return null; //does not exist
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToBigDecimalConverter(NumberFormat.getInstance(), fromType);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return null; // does not exist
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? null : BigDecimal.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToBigIntegerConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToBigIntegerConverterTest.java
new file mode 100644
index 0000000..2d41a6a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToBigIntegerConverterTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToBigIntegerConverterTest extends NumberToNumberTestHarness {
+	private NumberFormat numberFormat;
+	
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToBigIntegerConverter(numberFormat, fromType);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return null;  //no such thing
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? null : BigInteger.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToByteConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToByteConverterTest.java
new file mode 100644
index 0000000..551b684
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToByteConverterTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToByteConverterTest extends NumberToNumberTestHarness {
+	private NumberFormat numberFormat;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		numberFormat = NumberFormat.getInstance();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Integer(Byte.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToByteConverter(numberFormat, fromType, false);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return new NumberToByteConverter(numberFormat, fromType, true);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType()
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? Byte.TYPE : Byte.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToDoubleConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToDoubleConverterTest.java
new file mode 100644
index 0000000..2a5a480
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToDoubleConverterTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import java.math.BigDecimal;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToDoubleConverterTest extends NumberToNumberTestHarness {
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Double.MAX_VALUE));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToDoubleConverter(NumberFormat.getInstance(), fromType, false);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return new NumberToDoubleConverter(NumberFormat.getInstance(), fromType, true);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? Double.TYPE : Double.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToFloatConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToFloatConverterTest.java
new file mode 100644
index 0000000..17752f6
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToFloatConverterTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToFloatConverterTest extends NumberToNumberTestHarness {
+	private NumberFormat numberFormat;
+	
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		numberFormat = NumberFormat.getInstance();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToFloatConverter(numberFormat, fromType, false);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return new NumberToFloatConverter(numberFormat, fromType, true);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? Float.TYPE : Float.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToIntegerConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToIntegerConverterTest.java
new file mode 100644
index 0000000..0734677
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToIntegerConverterTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToIntegerConverterTest extends NumberToNumberTestHarness {
+	private NumberFormat numberFormat;
+	
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		numberFormat = NumberFormat.getInstance();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Long((long) Integer.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToIntegerConverter(numberFormat, fromType, false);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return new NumberToIntegerConverter(numberFormat, fromType, true);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? Integer.TYPE : Integer.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToLongConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToLongConverterTest.java
new file mode 100644
index 0000000..aff8120
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToLongConverterTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToLongConverterTest extends NumberToNumberTestHarness {
+	private NumberFormat numberFormat;
+	
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		numberFormat = NumberFormat.getInstance();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToLongConverter(numberFormat, fromType, false);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return new NumberToLongConverter(numberFormat, fromType, true);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? Long.TYPE : Long.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToNumberTestHarness.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToNumberTestHarness.java
new file mode 100644
index 0000000..d6373b3
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToNumberTestHarness.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+
+/**
+ * @since 1.1
+ */
+public abstract class NumberToNumberTestHarness extends TestCase {
+
+	/**
+	 * Invoked when a to primitive validator is needed.
+	 * 
+	 * @param fromType
+	 * @return validator, <code>null</code> if the type does not have a primitive form
+	 */
+	protected abstract IConverter doGetToPrimitiveValidator(Class fromType);
+
+	/**
+	 * Invoked when a to boxed validator is needed.
+	 * 
+	 * @param fromType
+	 * @return
+	 */
+	protected abstract IConverter doGetToBoxedTypeValidator(Class fromType);
+
+	/**
+	 * Invoked when the type is needed.
+	 * 
+	 * @param primitive
+	 * @return type, <code>null</code> if the type does not have a primitive form
+	 */
+	protected abstract Class doGetToType(boolean primitive);
+
+	/**
+	 * Invoked when an out of range number is needed to use for conversion.
+	 * 
+	 * @return out of range number of <code>null</code> if the type has no bounds
+	 */
+	protected abstract Number doGetOutOfRangeNumber();
+
+	public void testFromType() throws Exception {
+		Class from = Integer.class;
+		assertEquals(from, doGetToBoxedTypeValidator(from).getFromType());
+	}
+
+	public void testToTypeIsPrimitive() throws Exception {
+		Class toType = doGetToType(true);
+		
+		if (toType == null) {
+			//return if there is no primitive type
+			return;
+		}
+		assertEquals("to type was not of the correct type", toType, doGetToPrimitiveValidator(Integer.class)
+				.getToType());
+		assertTrue("to type was not primitive", toType.isPrimitive());
+	}
+
+	public void testToTypeIsBoxedType() throws Exception {
+		Class toType = doGetToType(false);
+		assertEquals(toType, doGetToBoxedTypeValidator(Integer.class)
+				.getToType());
+		assertFalse(toType.isPrimitive());
+	}
+
+	public void testValidConversion() throws Exception {
+		Integer value = new Integer(1);
+		Number result = (Number) doGetToBoxedTypeValidator(Integer.class)
+				.convert(value);
+		
+		assertNotNull("result was null", result);
+		
+		// regardless if the converter is for the primitive value the returned
+		// value will be the boxed type
+		assertEquals(doGetToType(false), result.getClass());
+		assertEquals(value, new Integer(result.intValue()));
+	}
+
+	public void testOutOfRangeConversion() throws Exception {
+		Number outOfRange = doGetOutOfRangeNumber();
+		
+		if (outOfRange == null) {
+			//the number does not have bounds (e.g. BigInteger or BigDecimal)
+			return;
+		}
+		try {
+			doGetToBoxedTypeValidator(Integer.class).convert(outOfRange);
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testConvertNullValueForPrimitiveThrowsIllegalArgumentException()
+			throws Exception {
+		if (doGetToType(true) == null) {
+			//return if primitive is not supported
+			
+			return;
+		}
+		
+		try {
+			doGetToPrimitiveValidator(Integer.class).convert(null);
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testConvertNullValueForBoxedTypeReturnsNull() throws Exception {
+		assertNull(doGetToBoxedTypeValidator(Integer.class).convert(null));
+	}
+
+	public void testNonNumberThrowsIllegalArgumentException() throws Exception {
+		try {
+			doGetToBoxedTypeValidator(Integer.class).convert("");
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToShortConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToShortConverterTest.java
new file mode 100644
index 0000000..4c00cfc
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/NumberToShortConverterTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToShortConverterTest extends NumberToNumberTestHarness {
+	private NumberFormat numberFormat;
+	
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		numberFormat = NumberFormat.getInstance();
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Integer(Short.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToBoxedTypeValidator(Class fromType) {
+		return new NumberToShortConverter(numberFormat, fromType, false);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected IConverter doGetToPrimitiveValidator(Class fromType) {
+		return new NumberToShortConverter(numberFormat, fromType, true);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.NumberToNumberTestHarness#doGetToType(boolean)
+	 */
+	protected Class doGetToType(boolean primitive) {
+		return (primitive) ? Short.TYPE : Short.class;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/ObjectToPrimitiveValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/ObjectToPrimitiveValidatorTest.java
new file mode 100755
index 0000000..9fbfb83
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/ObjectToPrimitiveValidatorTest.java
@@ -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
+ *******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.validation.ObjectToPrimitiveValidator;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ObjectToPrimitiveValidatorTest extends TestCase {
+
+	private ObjectToPrimitiveValidator objectToPrimitiveValidator;
+
+	protected void setUp() throws Exception {
+		this.objectToPrimitiveValidator = new ObjectToPrimitiveValidator(
+				Integer.TYPE);
+	}
+
+	/**
+	 * Test method for
+	 * {@link org.eclipse.jface.internal.databinding.provisional.validation.ObjectToPrimitiveValidator#isValid(java.lang.Object)}.
+	 */
+	public void testIsValid() {
+		IStatus result = this.objectToPrimitiveValidator.validate(null);
+		assertEquals("The wrong validation error was found.", result
+				.getMessage(), this.objectToPrimitiveValidator.getNullHint());
+
+		result = this.objectToPrimitiveValidator.validate(new Integer(1));
+		assertTrue("No validation error should be found.", result.isOK());
+
+		result = this.objectToPrimitiveValidator.validate(new Object());
+		assertEquals("The wrong validation error was found.", result
+				.getMessage(), this.objectToPrimitiveValidator.getClassHint());
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StatusToStringConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StatusToStringConverterTest.java
new file mode 100644
index 0000000..51bb872
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StatusToStringConverterTest.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.conversion.StatusToStringConverter;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.1
+ */
+public class StatusToStringConverterTest extends TestCase {
+	private StatusToStringConverter converter;
+
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		converter = new StatusToStringConverter();
+	}
+	
+	public void testConvertedValueIsMessageOfStatus() throws Exception {
+		String message = "this is my message";
+		IStatus status = ValidationStatus.error(message);
+		assertEquals(message, converter.convert(status));
+	}
+	
+	public void testFromTypeIsIStatus() throws Exception {
+		assertEquals(IStatus.class, converter.getFromType());
+	}
+	
+	public void testToTypeIsString() throws Exception {
+		assertEquals(String.class, converter.getToType());
+	}
+	
+	public void testIllegalArgumentExceptionIsThrownWithNullInput() throws Exception {
+		try {
+			converter.convert(null);
+			fail();
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToBooleanConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToBooleanConverterTest.java
new file mode 100644
index 0000000..b1534ec
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToBooleanConverterTest.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+import org.eclipse.core.internal.databinding.BindingMessages;
+import org.eclipse.core.internal.databinding.conversion.StringToBooleanConverter;
+
+/**
+ * @since 1.1
+ */
+public class StringToBooleanConverterTest extends TestCase {
+	private StringToBooleanConverter converter;
+
+	private List trueValues;
+
+	private List falseValues;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#run(junit.framework.TestResult)
+	 */
+	public void run(TestResult result) {
+		trueValues = Collections.unmodifiableList(toValues(BindingMessages
+				.getString("ValueDelimiter"), BindingMessages
+				.getString("TrueStringValues")));
+		falseValues = Collections.unmodifiableList(toValues(BindingMessages
+				.getString("ValueDelimiter"), BindingMessages
+				.getString("FalseStringValues")));
+
+		super.run(result);
+	}
+
+	private List toValues(String delimiter, String values) {
+		StringTokenizer tokenizer = new StringTokenizer(values, delimiter);
+		List result = new LinkedList();
+
+		while (tokenizer.hasMoreTokens()) {
+			result.add(tokenizer.nextToken());
+		}
+
+		return result;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		converter = new StringToBooleanConverter();
+		assertTrue(trueValues.size() > 0);
+		assertTrue(falseValues.size() > 0);
+	}
+
+	public void testConvertsToTrue() throws Exception {
+		Boolean result = (Boolean) converter.convert(trueValues.get(0));
+		assertTrue(result.booleanValue());
+	}
+
+	public void testConvertsToFalse() throws Exception {
+		Boolean result = (Boolean) converter.convert(falseValues.get(0));
+		assertFalse(result.booleanValue());
+	}
+
+	public void testUpperCaseStringConvertsToTrue() throws Exception {
+		Boolean result = (Boolean) converter.convert(((String) trueValues.get(0))
+				.toUpperCase());
+		assertTrue(result.booleanValue());
+	}
+
+	public void testUpperCaseStringConvertsToFalse() throws Exception {
+		Boolean result = (Boolean) converter.convert(((String) falseValues.get(0))
+				.toUpperCase());
+		assertFalse(result.booleanValue());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToByteConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToByteConverterTest.java
new file mode 100644
index 0000000..1c61468
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToByteConverterTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.conversion.StringToByteConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToByteConverterTest extends TestCase {
+	private NumberFormat numberFormat;
+	private StringToByteConverter converter;
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		numberFormat = NumberFormat.getIntegerInstance();
+		converter = StringToByteConverter.toByte(numberFormat, false);
+	}
+
+	public void testConvertsToByte() throws Exception {
+		Byte value = new Byte((byte) 1);
+		Byte result = (Byte) converter.convert(numberFormat.format(value));
+		
+		assertEquals(value, result);
+	}
+
+	public void testConvertsToBytePrimitive() throws Exception {
+		converter = StringToByteConverter.toByte(numberFormat, true);
+		Byte value = new Byte((byte) 1);
+		Byte result = (Byte) converter.convert(numberFormat.format(value));
+		assertEquals(value, result);
+	}
+
+	public void testFromTypeIsString() throws Exception {
+		assertEquals(String.class, converter.getFromType());
+	}
+
+	public void testToTypeIsShort() throws Exception {
+		assertEquals(Byte.class, converter.getToType());
+	}
+	
+	public void testToTypeIsBytePrimitive() throws Exception {
+		converter = StringToByteConverter.toByte(true);
+		assertEquals(Byte.TYPE, converter.getToType());
+	}
+	
+	public void testReturnsNullBoxedTypeForEmptyString() throws Exception {
+		assertNull(converter.convert(""));
+	}
+
+	public void testThrowsIllegalArgumentExceptionIfAskedToConvertNonString()
+			throws Exception {
+		try {
+			converter.convert(new Integer(1));
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToCharacterConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToCharacterConverterTest.java
new file mode 100644
index 0000000..ee04f1b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToCharacterConverterTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter;
+
+/**
+ * @since 1.1
+ */
+public class StringToCharacterConverterTest extends TestCase {
+
+	private StringToCharacterConverter converter;
+	private StringToCharacterConverter primitiveConverter;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		converter = StringToCharacterConverter.toCharacter(false);
+		primitiveConverter = StringToCharacterConverter.toCharacter(true);
+	}
+
+	public void testConvertsToCharacter() throws Exception {
+		Character value = new Character('X');
+		Character result = (Character) converter.convert(Character
+				.toString(value.charValue()));
+
+		assertEquals(value, result);
+	}
+
+	public void testConvertsToCharacterPrimitive() throws Exception {
+		Character value = new Character('Y');
+		Character result = (Character) primitiveConverter.convert(String
+				.valueOf(value.charValue()));
+		assertEquals(value, result);
+	}
+
+	public void testFromTypeIsString() throws Exception {
+		assertEquals(String.class, converter.getFromType());
+	}
+
+	public void testToTypeIsCharacter() throws Exception {
+		assertEquals(Character.class, converter.getToType());
+	}
+
+	public void testToTypeIsCharacterPrimitive() throws Exception {
+		assertEquals(Character.TYPE, primitiveConverter.getToType());
+	}
+
+	public void testReturnsNullBoxedTypeForEmptyString() throws Exception {
+		assertNull(converter.convert(""));
+	}
+
+	public void testNullCharacterIsOK() throws Exception {
+		assertNull(converter.convert(null));
+	}
+
+	public void testNullCharacterIsNotOKForPrimitive() throws Exception {
+		try {
+			primitiveConverter.convert(null);
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testThrowsIllegalArgumentExceptionIfAskedToConvertNonString()
+			throws Exception {
+		try {
+			converter.convert(new Integer(1));
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserByteTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserByteTest.java
new file mode 100644
index 0000000..dd8b65b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserByteTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 3.2
+ *
+ */
+public class StringToNumberParserByteTest extends
+		StringToNumberParserTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#assertValid(java.lang.Number)
+	 */
+	protected boolean assertValid(Number number) {
+		return StringToNumberParser.inByteRange(number);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMax()
+	 */
+	protected Number getValidMax() {
+		return new Byte(Byte.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMin()
+	 */
+	protected Number getValidMin() {
+		return new Byte(Byte.MIN_VALUE);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserDoubleTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserDoubleTest.java
new file mode 100644
index 0000000..485d059
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserDoubleTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 3.2
+ *
+ */
+public class StringToNumberParserDoubleTest extends
+		StringToNumberParserTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#assertValid(java.lang.Number)
+	 */
+	protected boolean assertValid(Number number) {
+		return StringToNumberParser.inDoubleRange(number);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMax()
+	 */
+	protected Number getValidMax() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMin()
+	 */
+	protected Number getValidMin() {
+		return new Double(-Double.MAX_VALUE);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserFloatTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserFloatTest.java
new file mode 100644
index 0000000..8a286eb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserFloatTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 1.1
+ *
+ */
+public class StringToNumberParserFloatTest extends
+		StringToNumberParserTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#assertValid(java.lang.Number)
+	 */
+	protected boolean assertValid(Number number) {
+		return StringToNumberParser.inFloatRange(number);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMax()
+	 */
+	protected Number getValidMax() {
+		return new Float(Float.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMin()
+	 */
+	protected Number getValidMin() {
+		return new Float(-Float.MAX_VALUE);
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserIntegerTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserIntegerTest.java
new file mode 100644
index 0000000..cd37511
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserIntegerTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 1.1
+ */
+public class StringToNumberParserIntegerTest extends
+		StringToNumberParserTestHarness {
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#assertValid(java.lang.Number)
+	 */
+	protected boolean assertValid(Number number) {
+		return StringToNumberParser.inIntegerRange(number);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMax()
+	 */
+	protected Number getValidMax() {
+		return new Integer(Integer.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMin()
+	 */
+	protected Number getValidMin() {
+		return new Integer(Integer.MIN_VALUE);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserLongTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserLongTest.java
new file mode 100644
index 0000000..3ecef0d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserLongTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+/**
+ * @since 1.1
+ *
+ */
+public class StringToNumberParserLongTest extends
+		StringToNumberParserTestHarness {
+
+	protected boolean assertValid(Number number) {
+		return StringToNumberParser.inLongRange(number);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMax()
+	 */
+	protected Number getValidMax() {
+		return new Long(Long.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMin()
+	 */
+	protected Number getValidMin() {
+		return new Long(Long.MIN_VALUE);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserShortTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserShortTest.java
new file mode 100644
index 0000000..cf0c0fa
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserShortTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+
+/**
+ * @since 1.1
+ */
+public class StringToNumberParserShortTest extends StringToNumberParserTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#assertValid(java.lang.Number)
+	 */
+	protected boolean assertValid(Number number) {
+		return StringToNumberParser.inShortRange(number);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMax()
+	 */
+	protected Number getValidMax() {
+		return new Short(Short.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTestHarness#getValidMin()
+	 */
+	protected Number getValidMin() {
+		return new Short(Short.MIN_VALUE);
+	}	
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserTest.java
new file mode 100644
index 0000000..a1329a0
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser.ParseResult;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToNumberParserTest extends TestCase {
+	private NumberFormat integerFormat;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		integerFormat = NumberFormat.getIntegerInstance();
+	}
+
+	public void testParseNonStringThrowsIllegalArgumentException()
+			throws Exception {
+		try {
+			StringToNumberParser.parse(new Integer(0), integerFormat, false);
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testEmptyStringReturnsNullIfNotPrimitive() throws Exception {
+		ParseResult result = StringToNumberParser.parse("",
+				integerFormat, false);
+		assertNull(result.getNumber());
+	}
+
+	public void testReturnsParsePositionWhenValueCannotBeParsed()
+			throws Exception {
+		ParseResult result = StringToNumberParser.parse("adsf",
+				integerFormat, false);
+		assertNotNull(result.getPosition());
+		assertNull(result.getNumber());
+	}
+
+	public void testReturnsNumberWhenSuccessfullyParsed() throws Exception {
+		Integer number = new Integer(5);
+		ParseResult result = StringToNumberParser.parse(integerFormat
+				.format(number.longValue()), integerFormat, false);
+		assertNull(result.getPosition());
+		assertEquals(number.intValue(), result.getNumber().intValue());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserTestHarness.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserTestHarness.java
new file mode 100644
index 0000000..476ba39
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToNumberParserTestHarness.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import org.eclipse.core.internal.databinding.conversion.StringToNumberParser;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 1.1
+ */
+public abstract class StringToNumberParserTestHarness extends TestCase {
+
+	protected abstract Number getValidMax();
+
+	protected abstract Number getValidMin();
+
+	protected abstract boolean assertValid(Number number);
+
+	public void testRanges() throws Exception {
+		Number min = getValidMin();
+		Number max = getValidMax();
+
+		double minDouble = min.doubleValue();
+		double maxDouble = max.doubleValue();
+
+		//test bytes
+		assertTrue("valid byte", assertValid(new Byte((byte) 1)));
+		assertTrue("valid byte min", assertValid(new Byte(Byte.MIN_VALUE)));
+		assertTrue("valid byte max", assertValid(new Byte(Byte.MAX_VALUE)));
+		
+		// test shorts
+		assertTrue("valid short", assertValid(new Short((short) 1)));
+		boolean result = assertValid(new Short(Short.MIN_VALUE));
+		if (minDouble > Short.MIN_VALUE) {
+			assertFalse("invalid short min", result);
+		} else {
+			assertTrue("valid short min", result);
+		}
+
+		result = assertValid(new Short(Short.MAX_VALUE));
+		if (maxDouble < Short.MAX_VALUE) {
+			assertFalse("invalid short max", result);
+		} else {
+			assertTrue("valid short max", result);
+		}
+
+		// test integers
+		assertTrue("valid Integer", assertValid(new Integer(1)));
+
+		result = assertValid(new Integer(Integer.MIN_VALUE));
+		if (minDouble > Integer.MIN_VALUE) {
+			assertFalse("invalid Integer min", result);
+		} else {
+			assertTrue("valid integer min", result);
+		}
+
+		result = assertValid(new Integer(Integer.MAX_VALUE));
+		if (maxDouble < Integer.MAX_VALUE) {
+			assertFalse("valid Integer max", result);
+		} else {
+			assertTrue("valid integer max", result);
+		}
+
+		// test longs
+		assertTrue("valid long", assertValid(new Long(1)));
+		result = assertValid(new Long(Long.MIN_VALUE));
+		if (minDouble > Long.MIN_VALUE) {
+			assertFalse("invalid long min", result);
+		} else {
+			assertTrue("valid long min", result);
+		}
+
+		result = assertValid(new Long(Long.MAX_VALUE));
+		if (maxDouble < Long.MAX_VALUE) {
+			assertFalse("invalid long max", result);
+		} else {
+			assertTrue("valid long max", result);
+		}
+
+		// test floats
+		assertTrue("valid float", assertValid(new Float(1)));
+		result = assertValid(new Float(-Float.MAX_VALUE));
+		if (minDouble > -Float.MAX_VALUE) {
+			assertFalse("invalid float min", result);
+		} else {
+			assertTrue("valid float min", result);
+		}
+
+		result = assertValid(new Float(Float.MAX_VALUE));
+		if (maxDouble < Float.MAX_VALUE) {
+			assertFalse("invalid float max", result);
+		} else {
+			assertTrue("valid float max", result);
+		}
+
+		assertFalse("invalid negative float infinity", assertValid(new Float(
+				Float.NEGATIVE_INFINITY)));
+		assertFalse("invalid positive float infinity", assertValid(new Float(
+				Float.POSITIVE_INFINITY)));
+		assertFalse("invalid float NaN", assertValid(new Float(Float.NaN)));
+
+		// test doubles
+		assertTrue("valid double", assertValid(new Double(1)));
+		result = assertValid(new Double(-Double.MAX_VALUE));
+		if (minDouble > -Double.MAX_VALUE) {
+			assertFalse("invalid double min", result);
+		} else {
+			assertTrue("valid double min", result);
+		}
+
+		result = assertValid(new Double(Double.MAX_VALUE));
+		if (maxDouble < Double.MAX_VALUE) {
+			assertFalse("invalid float max", result);
+		} else {
+			assertTrue("valid float max", result);
+		}
+
+		assertFalse("invalid negative double infinity", assertValid(new Double(
+				Double.NEGATIVE_INFINITY)));
+		assertFalse("invalid positive double infinity", assertValid(new Double(
+				Double.POSITIVE_INFINITY)));
+		assertFalse("invalid double NaN", assertValid(new Double(Double.NaN)));
+
+		// test BigIntegers
+		assertTrue("valid BigInteger", assertValid(BigInteger.valueOf(1)));
+		BigDecimal bigDecimalMin = new BigDecimal(min.doubleValue());
+		bigDecimalMin = bigDecimalMin.subtract(new BigDecimal(1));
+		assertFalse("invalid BigInteger min", assertValid(bigDecimalMin.toBigInteger()));
+
+		BigDecimal bigDecimalMax = new BigDecimal(max.doubleValue());
+		bigDecimalMax = bigDecimalMax.add(new BigDecimal(1));
+		assertFalse("invalid BigInteger max", assertValid(bigDecimalMax.toBigInteger()));
+		
+		// test BigDecimals
+		assertTrue("valid BigDecimal", assertValid(new BigDecimal(1)));
+		assertFalse("invalid BigDecimal min", assertValid(bigDecimalMin));
+		assertFalse("invalid BigDecimal max", assertValid(bigDecimalMax));
+
+		/**
+		 * The ICU4J plugin's NumberFormat will return it's own BigDecimal
+		 * implementation, com.ibm.icu.math.BigDecimal. The issue this causes is
+		 * that we can't reference this class as it's not part of the
+		 * replacement plugin. So in order to ensure that we handle Number's
+		 * that are not part of the JDK stub a number implemenation and ensure
+		 * that the double representation of this number is used.
+		 * 
+		 * @throws Exception
+		 */
+		class MyNumber extends Number {
+			double value;
+			int count;
+
+			MyNumber(double value) {
+				this.value = value;
+			}
+
+			private static final long serialVersionUID = 1L;
+
+			/*
+			 * (non-Javadoc)
+			 * 
+			 * @see java.lang.Number#doubleValue()
+			 */
+			public double doubleValue() {
+				count++;
+				return value;
+			}
+
+			/*
+			 * (non-Javadoc)
+			 * 
+			 * @see java.lang.Number#floatValue()
+			 */
+			public float floatValue() {
+				return 0;
+			}
+
+			/*
+			 * (non-Javadoc)
+			 * 
+			 * @see java.lang.Number#intValue()
+			 */
+			public int intValue() {
+				return 0;
+			}
+
+			/*
+			 * (non-Javadoc)
+			 * 
+			 * @see java.lang.Number#longValue()
+			 */
+			public long longValue() {
+				return 0;
+			}
+		}
+
+		MyNumber number = new MyNumber(1);
+		assertEquals(0, number.count);
+		assertTrue(StringToNumberParser.inIntegerRange(number));
+		assertTrue("double value retrieved", number.count > 0);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToShortConverterTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToShortConverterTest.java
new file mode 100644
index 0000000..b37fba1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/conversion/StringToShortConverterTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.conversion;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.conversion.StringToShortConverter;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToShortConverterTest extends TestCase {
+	private NumberFormat numberFormat;
+	private StringToShortConverter converter;
+	
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		numberFormat = NumberFormat.getIntegerInstance();
+		converter = StringToShortConverter.toShort(numberFormat, false);
+	}
+
+	public void testConvertsToShort() throws Exception {
+		Short value = new Short((short) 1);
+		Short result = (Short) converter.convert(numberFormat.format(value));
+		
+		assertEquals(value, result);
+	}
+
+	public void testConvertsToShortPrimitive() throws Exception {
+		converter = StringToShortConverter.toShort(numberFormat, true);
+		Short value = new Short((short) 1);
+		Short result = (Short) converter.convert(numberFormat.format(value));
+		assertEquals(value, result);
+	}
+
+	public void testFromTypeIsString() throws Exception {
+		assertEquals(String.class, converter.getFromType());
+	}
+
+	public void testToTypeIsShort() throws Exception {
+		assertEquals(Short.class, converter.getToType());
+	}
+	
+	public void testToTypeIsShortPrimitive() throws Exception {
+		converter = StringToShortConverter.toShort(true);
+		assertEquals(Short.TYPE, converter.getToType());
+	}
+	
+	public void testReturnsNullBoxedTypeForEmptyString() throws Exception {
+		assertNull(converter.convert(""));
+	}
+
+	public void testThrowsIllegalArgumentExceptionIfAskedToConvertNonString()
+			throws Exception {
+		try {
+			converter.convert(new Integer(1));
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ConstantObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ConstantObservableValueTest.java
new file mode 100644
index 0000000..abd1df4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ConstantObservableValueTest.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * 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 212518)
+ *     Matthew Hall - bug 146397
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.internal.databinding.observable.ConstantObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Tests for ConstantObservableValue
+ * 
+ * @since 1.1
+ */
+public class ConstantObservableValueTest extends AbstractDefaultRealmTestCase {
+	public void testConstructor_NullRealm() {
+		try {
+			new ConstantObservableValue(null, null, null);
+			fail("Constructor should throw an exception when null realm is passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite("ConstantValueTest");
+		suite.addTestSuite(ConstantObservableValueTest.class);
+		suite.addTest(UnchangeableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		public IObservableValue createObservableValue(Realm realm) {
+			return new ConstantObservableValue(realm, new Object(),
+					Object.class);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Object.class;
+		}
+	}
+
+	/**
+	 * Non-API--this class is public so that SuiteBuilder can access it.
+	 */
+	public static class UnchangeableObservableValueContractTest extends
+			ObservableValueContractTest {
+		public UnchangeableObservableValueContractTest(String name,
+				IObservableValueContractDelegate delegate) {
+			super(name, delegate);
+		}
+
+		public void testChange_OrderOfNotifications() {
+			// disabled
+		}
+
+		public void testChange_ValueChangeEvent() {
+			// disabled
+		}
+
+		public void testChange_ValueChangeEventDiff() {
+			// disabled
+		}
+
+		public void testChange_ValueChangeEventFiredAfterValueIsSet() {
+			// disabled
+		}
+
+		public void testRemoveValueChangeListener_RemovesListener()
+				throws Exception {
+			// disabled
+		}
+
+		public void testChange_ChangeEvent() {
+			// disabled
+		}
+
+		public void testChange_EventObservable() {
+			// disabled
+		}
+
+		public void testChange_ObservableRealmIsTheCurrentRealm() {
+			// disabled
+		}
+
+		public void testChange_RealmCheck() {
+			// disabled
+		}
+
+		public void testRemoveChangeListener_RemovesListener() {
+			// disabled
+		}
+
+		public void testIsStale_RealmChecks() {
+			// disabled
+		}
+
+		public void testIsStale_GetterCalled() throws Exception {
+			// disabled
+		}
+
+		public void testIsDisposed() throws Exception {
+			// disabled
+		}
+
+		public void testAddDisposeListener_HandleDisposeInvoked() {
+			// disabled
+		}
+
+		public static Test suite(IObservableValueContractDelegate delegate) {
+			return new SuiteBuilder().addObservableContractTest(
+					UnchangeableObservableValueContractTest.class, delegate)
+					.build();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/DelayedObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/DelayedObservableValueTest.java
new file mode 100644
index 0000000..fb4d42b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/DelayedObservableValueTest.java
@@ -0,0 +1,240 @@
+/*******************************************************************************
+ * 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 212223)
+ *     Matthew Hall - bug 213145, 245647
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Tests for DelayedObservableValue
+ * 
+ * @since 1.2
+ */
+public class DelayedObservableValueTest extends AbstractDefaultRealmTestCase {
+	private Object oldValue;
+	private Object newValue;
+	private ObservableValueStub target;
+	private IObservableValue delayed;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		target = new ObservableValueStub(Realm.getDefault());
+		oldValue = new Object();
+		newValue = new Object();
+		target.setValue(oldValue);
+		delayed = Observables.observeDelayedValue(1, target);
+	}
+
+	protected void tearDown() throws Exception {
+		target.dispose();
+		target = null;
+		super.tearDown();
+	}
+
+	public void testIsStale_WhenTargetIsStale() {
+		assertFalse(target.isStale());
+		assertFalse(delayed.isStale());
+
+		target.fireStale();
+
+		assertTrue(target.isStale());
+		assertTrue(delayed.isStale());
+	}
+
+	public void testIsStale_DuringDelay() {
+		assertFalse(target.isStale());
+		assertFalse(delayed.isStale());
+
+		target.setValue(newValue);
+
+		assertFalse(target.isStale());
+		assertTrue(delayed.isStale());
+	}
+
+	public void testGetValueType_SameAsTarget() {
+		assertEquals(target.getValueType(), delayed.getValueType());
+	}
+
+	public void testGetValue_FiresPendingValueChange() {
+		assertFiresPendingValueChange(new Runnable() {
+			public void run() {
+				final Object value = delayed.getValue();
+				assertEquals(newValue, value);
+			}
+		});
+	}
+
+	public void testSetValue_PropagatesToTarget() {
+		assertEquals(oldValue, delayed.getValue());
+		assertEquals(oldValue, target.getValue());
+
+		delayed.setValue(newValue);
+
+		assertEquals(newValue, target.getValue());
+		assertEquals(newValue, delayed.getValue());
+	}
+
+	public void testSetValue_CachesGetValueFromTarget() {
+		Object overrideValue = target.overrideValue = new Object();
+
+		assertEquals(oldValue, delayed.getValue());
+		assertEquals(oldValue, target.getValue());
+
+		delayed.setValue(newValue);
+
+		assertEquals(overrideValue, target.getValue());
+		assertEquals(overrideValue, delayed.getValue());
+	}
+
+	public void testSetValue_FiresValueChangeEvent() {
+		ValueChangeEventTracker targetTracker = ValueChangeEventTracker
+				.observe(target);
+		ValueChangeEventTracker delayedTracker = ValueChangeEventTracker
+				.observe(delayed);
+
+		delayed.setValue(newValue);
+
+		assertEquals(1, targetTracker.count);
+		assertEquals(oldValue, targetTracker.event.diff.getOldValue());
+		assertEquals(newValue, targetTracker.event.diff.getNewValue());
+
+		assertEquals(1, delayedTracker.count);
+		assertEquals(oldValue, delayedTracker.event.diff.getOldValue());
+		assertEquals(newValue, delayedTracker.event.diff.getNewValue());
+	}
+
+	public void testWait_FiresPendingValueChange() {
+		assertFiresPendingValueChange(new Runnable() {
+			public void run() {
+				// Give plenty of time for display to run timer task
+				long timeout = time() + 5000;
+				do {
+					sleep(10);
+					processDisplayEvents();
+				} while (delayed.isStale() && time() < timeout);
+			}
+
+			private void sleep(int delay) {
+				try {
+					Thread.sleep(delay);
+				} catch (InterruptedException e) {
+					Thread.currentThread().interrupt();
+				}
+			}
+
+			private void processDisplayEvents() {
+				Display display = Display.getCurrent();
+				while (display.readAndDispatch()) {
+				}
+			}
+
+			private long time() {
+				return System.currentTimeMillis();
+			}
+		});
+	}
+
+	private void assertFiresPendingValueChange(Runnable runnable) {
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(delayed);
+
+		target.setValue(newValue);
+		assertTrue(delayed.isStale());
+		assertEquals(0, tracker.count);
+
+		runnable.run();
+
+		assertFalse(delayed.isStale());
+		assertEquals(1, tracker.count);
+		assertEquals(oldValue, tracker.event.diff.getOldValue());
+		assertEquals(newValue, tracker.event.diff.getNewValue());
+	}
+
+	static class ObservableValueStub extends AbstractObservableValue {
+		private Object value;
+		private boolean stale;
+
+		Object overrideValue;
+
+		public ObservableValueStub(Realm realm) {
+			super(realm);
+		}
+
+		protected Object doGetValue() {
+			return value;
+		}
+
+		protected void doSetValue(Object value) {
+			Object oldValue = this.value;
+			if (overrideValue != null)
+				value = overrideValue;
+			this.value = value;
+			stale = false;
+			fireValueChange(Diffs.createValueDiff(oldValue, value));
+		}
+
+		public Object getValueType() {
+			return Object.class;
+		}
+
+		protected void fireStale() {
+			stale = true;
+			super.fireStale();
+		}
+
+		public boolean isStale() {
+			return stale;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DelayedObservableValueTest.class
+				.getName());
+		suite.addTestSuite(DelayedObservableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		public IObservableValue createObservableValue(Realm realm) {
+			return Observables.observeDelayedValue(0, new ObservableValueStub(
+					realm));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Object.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Object();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/EmptyObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/EmptyObservableListTest.java
new file mode 100644
index 0000000..59ca5be
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/EmptyObservableListTest.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * 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 208332)
+ *     Matthew Hall - bugs 213145, 146397
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.internal.databinding.observable.EmptyObservableList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class EmptyObservableListTest {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(EmptyObservableListTest.class.getName());
+		suite.addTest(ImmutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	public static class ImmutableObservableListContractTest extends
+			ObservableListContractTest {
+		public static Test suite(IObservableCollectionContractDelegate delegate) {
+			return new SuiteBuilder().addObservableContractTest(
+					ImmutableObservableListContractTest.class, delegate)
+					.build();
+		}
+
+		public ImmutableObservableListContractTest(
+				IObservableCollectionContractDelegate delegate) {
+			super(delegate);
+		}
+
+		public ImmutableObservableListContractTest(String name,
+				IObservableCollectionContractDelegate delegate) {
+			super(name, delegate);
+		}
+
+		public void testGet_GetterCalled() {
+			// disabled
+		}
+
+		public void testSubList_GetterCalled() {
+			// disabled
+		}
+
+		public void testChange_ChangeEvent() {
+			// disabled
+		}
+
+		public void testChange_EventObservable() {
+			// disabled
+		}
+
+		public void testChange_ObservableRealmIsTheCurrentRealm() {
+			// disabled
+		}
+
+		public void testChange_RealmCheck() {
+			// disabled
+		}
+
+		public void testRemoveChangeListener_RemovesListener() {
+			// disabled
+		}
+
+		public void testIndexOf_GetterCalled() {
+			// disabled
+		}
+
+		public void testLastIndexOf_GetterCalled() {
+			// disabled
+		}
+
+		public void testListIterator_GetterCalled() {
+			// disabled
+		}
+
+		public void testListIteratorAtIndex_GetterCalled() {
+			// disabled
+		}
+
+		public void testContains_GetterCalled() {
+			// disabled
+		}
+
+		public void testContainsAll_GetterCalled() {
+			// disabled
+		}
+
+		public void testEquals_GetterCalled() {
+			// disabled
+		}
+
+		public void testHashCode_GetterCalled() {
+			// disabled
+		}
+
+		public void testIsEmpty_GetterCalled() {
+			// disabled
+		}
+
+		public void testIterator_GetterCalled() {
+			// disabled
+		}
+
+		public void testSize_GetterCalled() throws Exception {
+			// disabled
+		}
+
+		public void testToArray_GetterCalled() throws Exception {
+			// disabled
+		}
+
+		public void testToArrayWithObjectArray_GetterCalled() throws Exception {
+			// disabled
+		}
+
+		public void testIsStale_GetterCalled() throws Exception {
+			// disabled
+		}
+
+		public void testIsDisposed() throws Exception {
+			// disabled
+		}
+
+		public void testAddDisposeListener_HandleDisposeInvoked() {
+			// disabled
+		}
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		private Object elementType = new Object();
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			return new EmptyObservableList(realm, elementType);
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/EmptyObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/EmptyObservableSetTest.java
new file mode 100644
index 0000000..79f2c06
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/EmptyObservableSetTest.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * 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 208332)
+ *     Matthew Hall - bugs 213145, 146397
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.internal.databinding.observable.EmptyObservableSet;
+import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.delegate.IObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.SuiteBuilder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class EmptyObservableSetTest {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(EmptyObservableSetTest.class.getName());
+		suite.addTest(ImmutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	public static class ImmutableObservableSetContractTest extends
+			ObservableCollectionContractTest {
+		public static Test suite(IObservableCollectionContractDelegate delegate) {
+			return new SuiteBuilder().addObservableContractTest(
+					ImmutableObservableSetContractTest.class, delegate).build();
+		}
+
+		public ImmutableObservableSetContractTest(
+				IObservableCollectionContractDelegate delegate) {
+			super(delegate);
+		}
+
+		public ImmutableObservableSetContractTest(String name,
+				IObservableCollectionContractDelegate delegate) {
+			super(name, delegate);
+		}
+
+		public void testChange_ChangeEvent() {
+			// disabled
+		}
+
+		public void testChange_EventObservable() {
+			// disabled
+		}
+
+		public void testChange_ObservableRealmIsTheCurrentRealm() {
+			// disabled
+		}
+
+		public void testChange_RealmCheck() {
+			// disabled
+		}
+
+		public void testRemoveChangeListener_RemovesListener() {
+			// disabled
+		}
+
+		public void testContains_GetterCalled() {
+			// disabled
+		}
+
+		public void testContainsAll_GetterCalled() {
+			// disabled
+		}
+
+		public void testEquals_GetterCalled() {
+			// disabled
+		}
+
+		public void testHashCode_GetterCalled() {
+			// disabled
+		}
+
+		public void testIsEmpty_GetterCalled() {
+			// disabled
+		}
+
+		public void testIterator_GetterCalled() {
+			// disabled
+		}
+
+		public void testSize_GetterCalled() {
+			// disabled
+		}
+
+		public void testToArray_GetterCalled() {
+			// disabled
+		}
+
+		public void testToArrayWithObjectArray_GetterCalled() {
+			// disabled
+		}
+
+		public void testIsStale_GetterCalled() throws Exception {
+			// disabled
+		}
+
+		public void testIsDisposed() throws Exception {
+			// disabled
+		}
+
+		public void testAddDisposeListener_HandleDisposeInvoked() {
+			// disabled
+		}
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		private Object elementType = new Object();
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			return new EmptyObservableSet(realm, elementType);
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/IdentityObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/IdentityObservableSetTest.java
new file mode 100644
index 0000000..585252a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/IdentityObservableSetTest.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 213145
+ *         (through ObservableViewerElementSetTest.java)
+ *     Matthew Hall - bug 262269
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.internal.databinding.identity.IdentityObservableSet;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+
+public class IdentityObservableSetTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(IdentityObservableSetTest.class
+				.getName());
+		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IdentityObservableSet set = new IdentityObservableSet(realm,
+					Object.class);
+			for (int i = 0; i < elementCount; i++)
+				set.add(createElement(set));
+			return set;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public void change(IObservable observable) {
+			IObservableSet set = (IObservableSet) observable;
+			set.add(createElement(set));
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return Object.class;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/MapEntryObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/MapEntryObservableValueTest.java
new file mode 100644
index 0000000..d27e02a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/MapEntryObservableValueTest.java
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * Copyright (c) 2007-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 - bugs 184830, 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.WritableMap;
+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.ValueDiff;
+import org.eclipse.core.internal.databinding.observable.MapEntryObservableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.ObservableStaleContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.2
+ */
+public class MapEntryObservableValueTest extends AbstractDefaultRealmTestCase
+		implements IValueChangeListener {
+	private static final String VALUE1 = "Value1";
+	private static final String VALUE2 = "Value2";
+
+	private final Object key = "mapKey";
+	private IObservableMap map;
+	private ValueDiff diff;
+	private MapEntryObservableValue observedValue;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.map = new WritableMap();
+		this.observedValue = (MapEntryObservableValue) Observables
+				.observeMapEntry(this.map, this.key, String.class);
+		observedValue.addValueChangeListener(this);
+	}
+
+	public void handleValueChange(ValueChangeEvent p_event) {
+		this.diff = p_event.diff;
+	}
+
+	public void testNullValue() {
+		// test entry added with value null
+		this.map.put(this.key, null);
+		assertNull(this.diff);
+		assertNull(this.observedValue.getValue());
+		// test value changed from null to null
+		this.map.put(this.key, null);
+		assertNull(this.diff);
+		assertNull(this.observedValue.getValue());
+		// test null-valued entry removed
+		this.map.remove(this.key);
+		assertNull(this.diff);
+		assertNull(this.observedValue.getValue());
+	}
+
+	public void testNonNullValue() {
+		// test add non-null value
+		this.map.put(this.key, VALUE1);
+		assertNotNull(this.diff);
+		assertNull(this.diff.getOldValue());
+		assertSame(VALUE1, this.diff.getNewValue());
+		assertSame(VALUE1, this.observedValue.getValue());
+
+		// test change to another non-null value
+		this.diff = null;
+		this.map.put(this.key, VALUE2);
+		assertNotNull(this.diff);
+		assertSame(VALUE1, this.diff.getOldValue());
+		assertSame(VALUE2, this.diff.getNewValue());
+		assertSame(VALUE2, this.observedValue.getValue());
+	}
+
+	public void testTransitionBetweenNullAndNonNull() {
+		this.map.put(this.key, null);
+
+		// test transition to non-null
+		this.diff = null;
+		this.map.put(this.key, VALUE1);
+		assertNotNull(this.diff);
+		assertNull(this.diff.getOldValue());
+		assertSame(VALUE1, this.diff.getNewValue());
+
+		// test transition to null
+		this.diff = null;
+		this.map.put(this.key, null);
+		assertNotNull(this.diff);
+		assertSame(VALUE1, this.diff.getOldValue());
+		assertNull(this.diff.getNewValue());
+	}
+
+	public void testRemoveKey() {
+		this.map.put(this.key, VALUE1);
+
+		this.diff = null;
+		this.map.remove(this.key);
+		assertNotNull(this.diff);
+		assertSame(VALUE1, this.diff.getOldValue());
+		assertNull(this.diff.getNewValue());
+	}
+
+	public void testGetAndSetValue() {
+		// test set null value
+		this.observedValue.setValue(null);
+		assertNull(this.observedValue.getValue());
+		assertNull(this.diff);
+
+		// test set non-null value
+		this.observedValue.setValue(VALUE1);
+		assertSame(VALUE1, this.observedValue.getValue());
+		assertNotNull(this.diff);
+		assertNull(this.diff.getOldValue());
+		assertSame(VALUE1, this.diff.getNewValue());
+
+		// test set another non-null value
+		this.diff = null;
+		this.observedValue.setValue(VALUE2);
+		assertSame(VALUE2, this.observedValue.getValue());
+		assertNotNull(this.diff);
+		assertSame(VALUE1, this.diff.getOldValue());
+		assertSame(VALUE2, this.diff.getNewValue());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(MapEntryObservableValueTest.class.getName());
+		suite.addTestSuite(MapEntryObservableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		suite.addTest(ObservableStaleContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Object valueType = new Object();
+
+		public IObservableValue createObservableValue(Realm realm) {
+			WritableMap map = new WritableMap(realm);
+			Object key = new Object();
+			map.put(key, new Object());
+			return new MapEntryObservableValueStub(map, key, valueType);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Object();
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return valueType;
+		}
+
+		public void change(IObservable observable) {
+			MapEntryObservableValueStub mapEntryValue = (MapEntryObservableValueStub) observable;
+			mapEntryValue.map
+					.put(mapEntryValue.key, createValue(mapEntryValue));
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			MapEntryObservableValueStub mapEntryValue = (MapEntryObservableValueStub) observable;
+			mapEntryValue.map.setStale(stale);
+		}
+	}
+
+	private static class MapEntryObservableValueStub extends
+			MapEntryObservableValue {
+		WritableMap map;
+		Object key;
+
+		MapEntryObservableValueStub(WritableMap map, Object key,
+				Object valueType) {
+			super(map, key, valueType);
+			this.map = map;
+			this.key = key;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/StalenessObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/StalenessObservableValueTest.java
new file mode 100644
index 0000000..fd2c73f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/StalenessObservableValueTest.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * 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 212468)
+ *     Matthew Hall - bug 213145
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import org.eclipse.core.databinding.observable.AbstractObservable;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.internal.databinding.observable.StalenessObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests for StalenessObservableValue
+ * 
+ * @since 1.1
+ */
+public class StalenessObservableValueTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(StalenessObservableValueTest.class.getName());
+		suite.addTest(ObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class ObservableStub extends AbstractObservable {
+		boolean stale;
+
+		public ObservableStub(Realm realm) {
+			super(realm);
+		}
+
+		public boolean isStale() {
+			return stale;
+		}
+
+		public void setStale(boolean stale) {
+			if (this.stale == stale)
+				return;
+
+			this.stale = stale;
+			if (stale) {
+				fireStale();
+			} else {
+				fireChange();
+			}
+		}
+	}
+
+	static class StalenessObservableValueStub extends StalenessObservableValue {
+		ObservableStub target;
+
+		StalenessObservableValueStub(ObservableStub target) {
+			super(target);
+			this.target = target;
+		}
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		public IObservableValue createObservableValue(Realm realm) {
+			return new StalenessObservableValueStub(new ObservableStub(realm));
+		}
+
+		public void change(IObservable observable) {
+			ObservableStub target = ((StalenessObservableValueStub) observable).target;
+			target.setStale(!target.isStale());
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Boolean.TYPE;
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableListTest.java
new file mode 100755
index 0000000..ef617bd
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableListTest.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 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 - bugs 208332, 213145, 237718
+ *     Ovidio Mallo - bug 247741
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+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.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.ObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class UnmodifiableObservableListTest extends
+		AbstractDefaultRealmTestCase {
+	IObservableList unmodifiable;
+	ObservableList mutable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		List list = new ArrayList();
+		list.add("1");
+		list.add("2");
+
+		mutable = new MutableObservableList(list, String.class);
+		unmodifiable = Observables.unmodifiableObservableList(mutable);
+	}
+
+	public void testFiresChangeEvents() throws Exception {
+		ChangeEventTracker mutableListener = new ChangeEventTracker();
+		ChangeEventTracker unmodifiableListener = new ChangeEventTracker();
+
+		mutable.addChangeListener(mutableListener);
+		unmodifiable.addChangeListener(unmodifiableListener);
+
+		assertEquals(0, mutableListener.count);
+		assertEquals(0, unmodifiableListener.count);
+		mutable.add("3");
+		assertEquals(1, mutableListener.count);
+		assertEquals(1, unmodifiableListener.count);
+	}
+
+	public void testFiresListChangeEvents() throws Exception {
+		ListChangeEventTracker mutableListener = new ListChangeEventTracker();
+		ListChangeEventTracker unmodifiableListener = new ListChangeEventTracker();
+
+		mutable.addListChangeListener(mutableListener);
+		unmodifiable.addListChangeListener(unmodifiableListener);
+
+		assertEquals(0, mutableListener.count);
+		assertEquals(0, unmodifiableListener.count);
+
+		String element = "3";
+		mutable.add(element);
+		assertEquals(1, mutableListener.count);
+		assertEquals(mutable, mutableListener.event.getObservableList());
+		assertEquals(1, mutableListener.event.diff.getDifferences().length);
+
+		ListDiffEntry difference = mutableListener.event.diff.getDifferences()[0];
+		assertEquals(element, difference.getElement());
+		assertTrue(difference.isAddition());
+		assertEquals(3, mutable.size());
+
+		assertEquals(1, unmodifiableListener.count);
+		assertEquals(unmodifiable, unmodifiableListener.event.getObservableList());
+		assertEquals(1, unmodifiableListener.event.diff.getDifferences().length);
+
+		difference = unmodifiableListener.event.diff.getDifferences()[0];
+		assertEquals(element, difference.getElement());
+		assertTrue(difference.isAddition());
+		assertEquals(3, unmodifiable.size());
+	}
+
+	public void testFiresStaleEvents() throws Exception {
+		StaleEventTracker mutableListener = new StaleEventTracker();
+		StaleEventTracker unmodifiableListener = new StaleEventTracker();
+
+		mutable.addStaleListener(mutableListener);
+		unmodifiable.addStaleListener(unmodifiableListener);
+
+		assertEquals(0, mutableListener.count);
+		assertEquals(0, unmodifiableListener.count);
+		mutable.setStale(true);
+		assertEquals(1, mutableListener.count);
+		assertEquals(mutable, mutableListener.event.getObservable());
+		assertTrue(mutable.isStale());
+		assertEquals(1, unmodifiableListener.count);
+		assertEquals(unmodifiable, unmodifiableListener.event.getObservable());
+		assertTrue(unmodifiable.isStale());
+	}
+
+	public void testIsStale() throws Exception {
+		assertFalse(mutable.isStale());
+		assertFalse(unmodifiable.isStale());
+		mutable.setStale(true);
+		assertTrue(mutable.isStale());
+		assertTrue(unmodifiable.isStale());
+	}
+
+	private static class MutableObservableList extends ObservableList {
+		/**
+		 * @param wrappedList
+		 * @param elementType
+		 */
+		public MutableObservableList(List wrappedList, Object elementType) {
+			super(wrappedList, elementType);
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.jface.internal.databinding.provisional.observable.list.ObservableList#add(java.lang.Object)
+		 */
+		public boolean add(Object o) {
+			boolean result = wrappedList.add(o);
+			fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(
+					wrappedList.size() - 1, true, o)));
+
+			return result;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UnmodifiableObservableListTest.class.getName());
+		suite.addTestSuite(UnmodifiableObservableListTest.class);
+		suite.addTest(ObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		private Object elementType = new Object();
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableList backingList = new WritableList(realm,
+					new ArrayList(), elementType);
+			IObservableList result = new UnmodifiableObservableListStub(
+					backingList);
+			for (int i = 0; i < elementCount; i++)
+				backingList.add(createElement(result));
+			return result;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			UnmodifiableObservableListStub unmodifiableList = (UnmodifiableObservableListStub) observable;
+			IObservableList wrappedList = unmodifiableList.wrappedList;
+			wrappedList.add(createElement(unmodifiableList));
+		}
+	}
+
+	private static class UnmodifiableObservableListStub extends
+			UnmodifiableObservableList {
+		IObservableList wrappedList;
+
+		UnmodifiableObservableListStub(IObservableList wrappedList) {
+			super(wrappedList);
+			this.wrappedList = wrappedList;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableSetTest.java
new file mode 100644
index 0000000..ede2d7a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableSetTest.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Matthew and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *         (through UnmodifiableObservableListTest.java)
+ *     Matthew Hall - bugs 208332, 213145, 237718
+ *     Ovidio Mallo - bug 247741
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+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.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.ObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableSet;
+import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class UnmodifiableObservableSetTest extends AbstractDefaultRealmTestCase {
+	UnmodifiableObservableSet unmodifiable;
+	MutableObservableSet mutable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		Set set = new HashSet();
+		set.add("1");
+		set.add("2");
+
+		mutable = new MutableObservableSet(set, String.class);
+		unmodifiable = (UnmodifiableObservableSet) Observables
+				.unmodifiableObservableSet(mutable);
+	}
+
+	public void testFiresChangeEvents() throws Exception {
+		ChangeEventTracker mutableListener = new ChangeEventTracker();
+		ChangeEventTracker unmodifiableListener = new ChangeEventTracker();
+
+		mutable.addChangeListener(mutableListener);
+		unmodifiable.addChangeListener(unmodifiableListener);
+
+		assertEquals(0, mutableListener.count);
+		assertEquals(0, unmodifiableListener.count);
+		mutable.add("3");
+		assertEquals(1, mutableListener.count);
+		assertEquals(1, unmodifiableListener.count);
+	}
+
+	public void testFiresSetChangeEvents() throws Exception {
+		SetChangeEventTracker mutableListener = new SetChangeEventTracker();
+		SetChangeEventTracker unmodifiableListener = new SetChangeEventTracker();
+
+		mutable.addSetChangeListener(mutableListener);
+		unmodifiable.addSetChangeListener(unmodifiableListener);
+
+		assertEquals(0, mutableListener.count);
+		assertEquals(0, unmodifiableListener.count);
+
+		String element = "3";
+		mutable.add(element);
+		assertEquals(1, mutableListener.count);
+		assertEquals(mutable, mutableListener.event.getObservableSet());
+		assertEquals(1, mutableListener.event.diff.getAdditions().size());
+
+		Object addition = mutableListener.event.diff.getAdditions().toArray()[0];
+		assertEquals(element, addition);
+		assertEquals(3, mutable.size());
+
+		assertEquals(1, unmodifiableListener.count);
+		assertEquals(unmodifiable, unmodifiableListener.event.getObservableSet());
+		assertEquals(1, unmodifiableListener.event.diff.getAdditions().size());
+
+		addition = unmodifiableListener.event.diff.getAdditions().toArray()[0];
+		assertEquals(element, addition);
+		assertEquals(3, unmodifiable.size());
+	}
+
+	public void testFiresStaleEvents() throws Exception {
+		StaleEventTracker mutableListener = new StaleEventTracker();
+		StaleEventTracker unmodifiableListener = new StaleEventTracker();
+
+		mutable.addStaleListener(mutableListener);
+		unmodifiable.addStaleListener(unmodifiableListener);
+
+		assertEquals(0, mutableListener.count);
+		assertEquals(0, unmodifiableListener.count);
+		mutable.setStale(true);
+		assertEquals(1, mutableListener.count);
+		assertEquals(mutable, mutableListener.event.getObservable());
+		assertTrue(mutable.isStale());
+		assertEquals(1, unmodifiableListener.count);
+		assertEquals(unmodifiable, unmodifiableListener.event.getObservable());
+		assertTrue(unmodifiable.isStale());
+	}
+
+	public void testIsStale() throws Exception {
+		assertFalse(mutable.isStale());
+		assertFalse(unmodifiable.isStale());
+		mutable.setStale(true);
+		assertTrue(mutable.isStale());
+		assertTrue(unmodifiable.isStale());
+	}
+
+	private static class MutableObservableSet extends ObservableSet {
+		/**
+		 * @param wrappedList
+		 * @param elementType
+		 */
+		public MutableObservableSet(Set wrappedSet, Object elementType) {
+			super(wrappedSet, elementType);
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.jface.internal.databinding.provisional.observable.list.ObservableList#add(java.lang.Object)
+		 */
+		public boolean add(Object o) {
+			boolean result = wrappedSet.add(o);
+			if (result)
+				fireSetChange(Diffs.createSetDiff(Collections.singleton(o),
+						Collections.EMPTY_SET));
+			return result;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UnmodifiableObservableSetTest.class.getName());
+		suite.addTestSuite(UnmodifiableObservableSetTest.class);
+		suite.addTest(ObservableCollectionContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		private Object elementType = new Object();
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableSet backingList = new WritableSet(realm, new HashSet(),
+					elementType);
+			IObservableSet result = new UnmodifiableObservableSetStub(
+					backingList);
+			for (int i = 0; i < elementCount; i++)
+				backingList.add(createElement(result));
+			return result;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			UnmodifiableObservableSetStub unmodifiableList = (UnmodifiableObservableSetStub) observable;
+			IObservableSet wrappedList = unmodifiableList.wrappedSet;
+			wrappedList.add(createElement(unmodifiableList));
+		}
+	}
+
+	private static class UnmodifiableObservableSetStub extends
+			UnmodifiableObservableSet {
+		IObservableSet wrappedSet;
+
+		UnmodifiableObservableSetStub(IObservableSet wrappedSet) {
+			super(wrappedSet);
+			this.wrappedSet = wrappedSet;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableValueTest.java
new file mode 100644
index 0000000..073caf1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/UnmodifiableObservableValueTest.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ *     Matthew Hall - bug 213145
+ *     Ovidio Mallo - bug 237163, 247741
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+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.WritableValue;
+import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class UnmodifiableObservableValueTest extends
+		AbstractDefaultRealmTestCase {
+
+	private UnmodifiableObservableValueStub unmodifiable;
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(UnmodifiableObservableValueTest.class.getName());
+		suite.addTestSuite(UnmodifiableObservableValueTest.class);
+		suite.addTest(ObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		WrappedObservableValue wrapped = new WrappedObservableValue(Realm
+				.getDefault(), null, String.class);
+		unmodifiable = new UnmodifiableObservableValueStub(wrapped);
+	}
+
+	public void testFiresStaleEvents() {
+		StaleEventTracker wrappedListener = new StaleEventTracker();
+		StaleEventTracker unmodifiableListener = new StaleEventTracker();
+
+		unmodifiable.wrappedValue.addStaleListener(wrappedListener);
+		unmodifiable.addStaleListener(unmodifiableListener);
+
+		assertEquals(0, wrappedListener.count);
+		assertEquals(0, unmodifiableListener.count);
+		unmodifiable.wrappedValue.setStale(true);
+		assertEquals(1, wrappedListener.count);
+		assertEquals(unmodifiable.wrappedValue, wrappedListener.event
+				.getObservable());
+		assertTrue(unmodifiable.wrappedValue.isStale());
+		assertEquals(1, unmodifiableListener.count);
+		assertEquals(unmodifiable, unmodifiableListener.event.getObservable());
+		assertTrue(unmodifiable.isStale());
+	}
+
+	public void testIsStale() {
+		assertFalse(unmodifiable.wrappedValue.isStale());
+		assertFalse(unmodifiable.isStale());
+		unmodifiable.wrappedValue.setStale(true);
+		assertTrue(unmodifiable.wrappedValue.isStale());
+		assertTrue(unmodifiable.isStale());
+	}
+
+	private static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Object valueType = new Object();
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return new UnmodifiableObservableValueStub(new WrappedObservableValue(realm,
+					null, valueType));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return valueType;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Object();
+		}
+
+		public void change(IObservable observable) {
+			UnmodifiableObservableValueStub wrapper = (UnmodifiableObservableValueStub) observable;
+			wrapper.wrappedValue.setValue(createValue(wrapper));
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			UnmodifiableObservableValueStub wrapper = (UnmodifiableObservableValueStub) observable;
+			wrapper.wrappedValue.setStale(stale);
+		}
+	}
+
+	private static class UnmodifiableObservableValueStub extends
+			UnmodifiableObservableValue {
+		WrappedObservableValue wrappedValue;
+
+		UnmodifiableObservableValueStub(WrappedObservableValue wrappedValue) {
+			super(wrappedValue);
+			this.wrappedValue = wrappedValue;
+		}
+	}
+
+	private static class WrappedObservableValue extends WritableValue {
+		private boolean stale = false;
+
+		public WrappedObservableValue(Realm realm, Object initialValue,
+				Object valueType) {
+			super(realm, initialValue, valueType);
+		}
+
+		public boolean isStale() {
+			ObservableTracker.getterCalled(this);
+			return stale;
+		}
+
+		public void setStale(boolean stale) {
+			if (this.stale != stale) {
+				this.stale = stale;
+				if (stale) {
+					fireStale();
+				} else {
+					fireValueChange(Diffs.createValueDiff(getValue(),
+							getValue()));
+				}
+			}
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableListTest.java
new file mode 100644
index 0000000..3272796
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableListTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import java.util.ArrayList;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+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.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableList;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ValidatedObservableListTest extends AbstractDefaultRealmTestCase {
+	public static Test suite() {
+		return MutableObservableListContractTest.suite(new Delegate());
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		private Object elementType = new Object();
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableList target = new WritableList(realm, new ArrayList(),
+					elementType);
+			for (int i = 0; i < elementCount; i++)
+				target.add(createElement(target));
+			IObservableValue validationStatus = new WritableValue(realm,
+					ValidationStatus.ok(), IStatus.class);
+			return new ValidatedObservableListStub(target, validationStatus);
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			ValidatedObservableListStub validated = (ValidatedObservableListStub) observable;
+			validated.target.add(createElement(validated));
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			ValidatedObservableListStub validated = (ValidatedObservableListStub) observable;
+			if (stale) {
+				validated.validationStatus.setValue(ValidationStatus
+						.error("error"));
+				validated.target.add(createElement(validated));
+			} else {
+				validated.validationStatus.setValue(ValidationStatus.ok());
+			}
+		}
+
+	}
+
+	static class ValidatedObservableListStub extends ValidatedObservableList {
+		IObservableList target;
+		IObservableValue validationStatus;
+
+		ValidatedObservableListStub(IObservableList target,
+				IObservableValue validationStatus) {
+			super(target, validationStatus);
+			this.target = target;
+			this.validationStatus = validationStatus;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableSetTest.java
new file mode 100644
index 0000000..9be44b2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableSetTest.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import java.util.Collections;
+
+import junit.framework.Test;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableSet;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ValidatedObservableSetTest extends AbstractDefaultRealmTestCase {
+	public static Test suite() {
+		return MutableObservableSetContractTest.suite(new Delegate());
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		private Object elementType = new Object();
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			IObservableSet target = new WritableSet(realm,
+					Collections.EMPTY_SET, elementType);
+			for (int i = 0; i < elementCount; i++)
+				target.add(createElement(target));
+			IObservableValue validationStatus = new WritableValue(realm,
+					ValidationStatus.ok(), IStatus.class);
+			return new ValidatedObservableSetStub(target, validationStatus);
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			ValidatedObservableSetStub validated = (ValidatedObservableSetStub) observable;
+			validated.target.add(createElement(validated));
+		}
+
+		public void setStale(IObservable observable, boolean stale) {
+			ValidatedObservableSetStub validated = (ValidatedObservableSetStub) observable;
+			if (stale) {
+				validated.validationStatus.setValue(ValidationStatus
+						.error("error"));
+				validated.target.add(createElement(validated));
+			} else {
+				validated.validationStatus.setValue(ValidationStatus.ok());
+			}
+		}
+
+	}
+
+	static class ValidatedObservableSetStub extends ValidatedObservableSet {
+		IObservableSet target;
+		IObservableValue validationStatus;
+
+		ValidatedObservableSetStub(IObservableSet target,
+				IObservableValue validationStatus) {
+			super(target, validationStatus);
+			this.target = target;
+			this.validationStatus = validationStatus;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableValueTest.java
new file mode 100644
index 0000000..c60e5b5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableValueTest.java
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.validation.ValidatedObservableValue;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ValidatedObservableValueTest extends AbstractDefaultRealmTestCase {
+	private ValidatedObservableValue validated;
+	private ObservableValueStub target;
+	private IObservableValue validationStatus;
+
+	private Object oldValue;
+	private Object newValue;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		oldValue = new Object();
+		newValue = new Object();
+		target = new ObservableValueStub(Realm.getDefault());
+		target.setValue(oldValue);
+		validationStatus = new WritableValue(ValidationStatus.ok(),
+				IStatus.class);
+		validated = new ValidatedObservableValue(target, validationStatus);
+	}
+
+	public void testConstructor_RequireObservablesOnSameRealm() {
+		CurrentRealm realm1 = new CurrentRealm(true);
+		CurrentRealm realm2 = new CurrentRealm(true);
+		target = new ObservableValueStub(realm1);
+		validationStatus = new WritableValue(realm2);
+		try {
+			new ValidatedObservableValue(target, validationStatus);
+			fail("Expected exception--target and validation status should have the same realm");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testIsStale_WhenTargetIsStale() {
+		assertFalse(target.isStale());
+		assertFalse(validated.isStale());
+
+		target.fireStale();
+
+		assertTrue(target.isStale());
+		assertTrue(validated.isStale());
+	}
+
+	public void testIsStale_WhileChangesPending() {
+		assertFalse(target.isStale());
+		assertFalse(validated.isStale());
+
+		validationStatus.setValue(ValidationStatus.error("error"));
+
+		// The validated observable goes stale only when the target changes
+		// value but the validation status is not OK.
+		assertFalse(target.isStale());
+		assertFalse(validated.isStale());
+
+		target.setValue(newValue);
+
+		assertFalse(target.isStale());
+		assertTrue(validated.isStale());
+
+		validationStatus.setValue(ValidationStatus.ok());
+
+		assertFalse(validated.isStale());
+	}
+
+	public void testGetValueType_SameAsTarget() {
+		assertEquals(target.getValueType(), validated.getValueType());
+	}
+
+	public void testGetValue_InitialValue() {
+		assertEquals(oldValue, target.getValue());
+		assertEquals(oldValue, validated.getValue());
+	}
+
+	public void testGetValue_WhileChangesPending() {
+		assertEquals(oldValue, target.getValue());
+		assertEquals(oldValue, validated.getValue());
+
+		validationStatus.setValue(ValidationStatus.error("error"));
+
+		assertEquals(oldValue, target.getValue());
+		assertEquals(oldValue, validated.getValue());
+
+		target.setValue(newValue);
+
+		assertEquals(newValue, target.getValue());
+		assertEquals(oldValue, validated.getValue());
+
+		validationStatus.setValue(ValidationStatus.ok());
+
+		assertEquals(newValue, validated.getValue());
+	}
+
+	public void testSetValue_PropagatesToTarget() {
+		validated.setValue(newValue);
+
+		assertEquals(newValue, validated.getValue());
+		assertEquals(newValue, target.getValue());
+	}
+
+	public void testSetValue_PropagatesToTargetWhileStatusNotOK() {
+		validationStatus.setValue(ValidationStatus.error("error"));
+
+		validated.setValue(newValue);
+
+		assertEquals(newValue, validated.getValue());
+		assertEquals(newValue, target.getValue());
+		assertFalse(validated.isStale());
+	}
+
+	public void testSetValue_CachesGetValueFromTarget() {
+		Object overrideValue = target.overrideValue = new Object();
+
+		assertEquals(oldValue, validated.getValue());
+		assertEquals(oldValue, target.getValue());
+
+		validationStatus.setValue(ValidationStatus.error("error"));
+
+		validated.setValue(newValue);
+
+		assertEquals(overrideValue, target.getValue());
+		assertEquals(overrideValue, validated.getValue());
+	}
+
+	public void testSetValue_SingleValueChangeEvent() {
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(validated);
+
+		validated.setValue(newValue);
+		assertEquals(1, tracker.count);
+		assertEquals(oldValue, tracker.event.diff.getOldValue());
+		assertEquals(newValue, tracker.event.diff.getNewValue());
+	}
+
+	public void testSetValue_SingleValueChangeEventWhileInvalid() {
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(validated);
+
+		validationStatus.setValue(ValidationStatus.error("error"));
+		validated.setValue(newValue);
+		assertEquals(1, tracker.count);
+		assertEquals(oldValue, tracker.event.diff.getOldValue());
+		assertEquals(newValue, tracker.event.diff.getNewValue());
+	}
+
+	public void testSetValue_FiresSingleValueChangeEventWithTargetOverride() {
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(validated);
+
+		Object overrideValue = new Object();
+		target.overrideValue = overrideValue;
+		validated.setValue(newValue);
+
+		assertEquals(1, tracker.count);
+		assertEquals(oldValue, tracker.event.diff.getOldValue());
+		assertEquals(overrideValue, tracker.event.diff.getNewValue());
+	}
+
+	public void testSetValue_FiresValueChangeEvent() {
+		ValueChangeEventTracker targetTracker = ValueChangeEventTracker
+				.observe(target);
+		ValueChangeEventTracker validatedTracker = ValueChangeEventTracker
+				.observe(validated);
+
+		validated.setValue(newValue);
+
+		assertEquals(1, targetTracker.count);
+		assertEquals(oldValue, targetTracker.event.diff.getOldValue());
+		assertEquals(newValue, targetTracker.event.diff.getNewValue());
+
+		assertEquals(1, validatedTracker.count);
+		assertEquals(oldValue, validatedTracker.event.diff.getOldValue());
+		assertEquals(newValue, validatedTracker.event.diff.getNewValue());
+	}
+
+	public void testIsStale_MatchTargetStaleness() {
+		target.forceStale = true;
+		target.fireStale();
+
+		assertTrue(target.isStale());
+		assertTrue(validated.isStale());
+
+		target.setValue(newValue);
+
+		assertTrue(target.isStale());
+		assertTrue(validated.isStale());
+	}
+
+	static class ObservableValueStub extends AbstractObservableValue {
+		private Object value;
+		private boolean stale;
+		private boolean forceStale;
+
+		Object overrideValue;
+
+		public ObservableValueStub(Realm realm) {
+			super(realm);
+		}
+
+		protected Object doGetValue() {
+			return value;
+		}
+
+		protected void doSetValue(Object value) {
+			Object oldValue = this.value;
+			if (overrideValue != null)
+				value = overrideValue;
+			this.value = value;
+			stale = forceStale;
+			fireValueChange(Diffs.createValueDiff(oldValue, value));
+		}
+
+		public Object getValueType() {
+			return Object.class;
+		}
+
+		protected void fireStale() {
+			stale = true;
+			super.fireStale();
+		}
+
+		public boolean isStale() {
+			return stale;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ValidatedObservableValueTest.class.getName());
+		suite.addTestSuite(ValidatedObservableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		private Object valueType = new Object();
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return new ValidatedObservableValueStub(realm, valueType);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Object();
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return valueType;
+		}
+
+		public void change(IObservable observable) {
+			ValidatedObservableValueStub validated = (ValidatedObservableValueStub) observable;
+			IObservableValue target = validated.target;
+			target.setValue(createValue(validated));
+		}
+	}
+
+	static class ValidatedObservableValueStub extends ValidatedObservableValue {
+		final IObservableValue target;
+		final IObservableValue validationStatus;
+
+		ValidatedObservableValueStub(Realm realm, Object valueType) {
+			this(new WritableValue(realm, null, valueType), new WritableValue(
+					realm, ValidationStatus.ok(), IStatus.class));
+		}
+
+		private ValidatedObservableValueStub(IObservableValue target,
+				IObservableValue validationStatus) {
+			super(target, validationStatus);
+			this.target = target;
+			this.validationStatus = validationStatus;
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableListTest.java
new file mode 100644
index 0000000..e37dc7d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableListTest.java
@@ -0,0 +1,222 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 208858, 221351, 213145
+ *     Ovidio Mallo - bug 241318
+ *     Tom Schindl - bug 287601
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+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.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+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.internal.databinding.observable.masterdetail.DetailObservableList;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ */
+public class DetailObservableListTest extends AbstractDefaultRealmTestCase {
+	/**
+	 * Asserts the use case of specifying null on construction for the detail
+	 * type of the detail list.
+	 * 
+	 * @throws Exception
+	 */
+	public void testElementTypeNull() throws Exception {
+		WritableValue observableValue = new WritableValue(new WritableList(
+				new ArrayList(), Object.class), null);
+
+		WritableListFactory factory = new WritableListFactory();
+		DetailObservableList detailObservable = new DetailObservableList(
+				factory, observableValue, null);
+		assertNull(detailObservable.getElementType());
+
+		// change the type returned from the factory
+		factory.type = String.class;
+		observableValue
+				.setValue(new WritableList(new ArrayList(), String.class));
+		assertNull("element type not null", detailObservable.getElementType());
+	}
+
+	/**
+	 * Asserts that you can't change the type across multiple inner observables.
+	 * 
+	 * @throws Exception
+	 */
+	public void testElementTypeNotNull() throws Exception {
+		WritableValue observableValue = new WritableValue(new WritableList(
+				new ArrayList(), Object.class), null);
+
+		WritableListFactory factory = new WritableListFactory();
+		DetailObservableList detailObservable = new DetailObservableList(
+				factory, observableValue, Object.class);
+		assertEquals(factory.type, detailObservable.getElementType());
+
+		try {
+			factory.type = String.class;
+			observableValue.setValue(new WritableList(Arrays
+					.asList(new Object[] { new Object() }), String.class));
+			fail("if an element type is set this cannot be changed");
+		} catch (AssertionFailedException e) {
+		}
+	}
+
+	/**
+	 * Asserts that the master observable value is not disposed upon disposing
+	 * its detail observable value (bug 241318).
+	 */
+	public void testMasterNotDisposedWhenDetailDisposed() {
+		class OuterObservable extends WritableValue {
+			boolean disposed = false;
+
+			public synchronized void dispose() {
+				disposed = true;
+				super.dispose();
+			}
+		}
+
+		OuterObservable outerObservable = new OuterObservable();
+		WritableListFactory factory = new WritableListFactory();
+		DetailObservableList detailObservable = new DetailObservableList(
+				factory, outerObservable, null);
+
+		assertFalse(outerObservable.disposed);
+
+		detailObservable.dispose();
+		assertFalse(outerObservable.disposed);
+	}
+
+	public void testDisposeMasterDisposesDetail() {
+		IObservableValue master = new WritableValue();
+		WritableListFactory factory = new WritableListFactory();
+		master.setValue("");
+
+		IObservableList detailObservable = MasterDetailObservables.detailList(
+				master, factory, null);
+		DisposeEventTracker tracker = DisposeEventTracker
+				.observe(detailObservable);
+
+		master.dispose();
+
+		assertEquals(1, tracker.count);
+		assertTrue(detailObservable.isDisposed());
+	}
+
+	public void testDisposeWhileFiringEvents() {
+		IObservableValue master = new WritableValue();
+		WritableListFactory factory = new WritableListFactory();
+		master.setValue("");
+
+		final IObservableList[] detailObservable = new IObservableList[1];
+
+		master.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				detailObservable[0].dispose();
+			}
+		});
+
+		detailObservable[0] = MasterDetailObservables.detailList(master,
+				factory, null);
+
+		master.setValue("New Value");
+	}
+
+	private static class WritableListFactory implements IObservableFactory {
+		Object type = Object.class;
+
+		public IObservable createObservable(Object target) {
+			return new WritableList(new ArrayList(), type);
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DetailObservableListTest.class
+				.getName());
+		suite.addTestSuite(DetailObservableListTest.class);
+		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		Object elementType = Object.class;
+
+		public IObservableCollection createObservableCollection(
+				final Realm realm, final int elementCount) {
+
+			IObservableValue master = new WritableValue(realm, new Integer(
+					elementCount), Integer.class);
+			IObservableFactory factory = new FactoryStub(realm, elementType);
+			return new DetailObservableListStub(factory, master, elementType);
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			final IObservableValue master = ((DetailObservableListStub) observable).master;
+			master.setValue(new Integer(((Integer) master.getValue())
+					.intValue() + 1));
+		}
+	}
+
+	static class FactoryStub implements IObservableFactory {
+		private Realm realm;
+		private Object elementType;
+
+		FactoryStub(Realm realm, Object elementType) {
+			this.realm = realm;
+			this.elementType = elementType;
+		}
+
+		Object type = Object.class;
+
+		public IObservable createObservable(Object target) {
+			int elementCount = ((Integer) target).intValue();
+			final ArrayList wrappedList = new ArrayList();
+			for (int i = 0; i < elementCount; i++)
+				wrappedList.add(new Object());
+			return new WritableList(realm, wrappedList, elementType);
+		}
+	}
+
+	static class DetailObservableListStub extends DetailObservableList {
+		IObservableValue master;
+
+		DetailObservableListStub(IObservableFactory factory,
+				IObservableValue master, Object elementType) {
+			super(factory, master, elementType);
+			this.master = master;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableMapTest.java
new file mode 100644
index 0000000..019ed7b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableMapTest.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 213145
+ *     Ovidio Mallo - bug 241318
+ *     Tom Schindl - bug 287601
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.map.WritableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+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.internal.databinding.observable.masterdetail.DetailObservableMap;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DetailObservableMapTest extends AbstractDefaultRealmTestCase {
+	/**
+	 * Asserts the use case of specifying null on construction for the detail
+	 * type of the detail set.
+	 * 
+	 * @throws Exception
+	 */
+	public void testKeyValueTypeNull() throws Exception {
+		WritableValue observableValue = new WritableValue();
+
+		WritableMapFactory factory = new WritableMapFactory();
+		DetailObservableMap detailObservable = new DetailObservableMap(factory,
+				observableValue, null, null);
+		assertNull(detailObservable.getKeyType());
+		assertNull(detailObservable.getValueType());
+
+		factory.keyType = Object.class;
+		factory.valueType = Object.class;
+		observableValue.setValue(new Object());
+		assertNull("key type not null", detailObservable.getKeyType());
+		assertNull("value type not null", detailObservable.getValueType());
+
+		factory.keyType = String.class;
+		factory.valueType = String.class;
+		// set the value again to ensure that the observable doesn't update the
+		// element type with that of the new element type
+		observableValue.setValue(new Object());
+		assertNull("key type not null", detailObservable.getKeyType());
+		assertNull("value type not null", detailObservable.getValueType());
+	}
+
+	/**
+	 * Asserts that you can't change the type across multiple inner observables.
+	 * 
+	 * @throws Exception
+	 */
+	public void testKeyValueTypeNotNull() throws Exception {
+		WritableValue observableValue = new WritableValue();
+
+		WritableMapFactory factory = new WritableMapFactory();
+		DetailObservableMap detailObservable = new DetailObservableMap(factory,
+				observableValue, Object.class, Object.class);
+		assertEquals(Object.class, detailObservable.getKeyType());
+		assertEquals(Object.class, detailObservable.getValueType());
+
+		try {
+			factory.keyType = String.class;
+			factory.valueType = String.class;
+			observableValue.setValue(new Object());
+			fail("if an element type is set this cannot be changed");
+		} catch (AssertionFailedException e) {
+		}
+	}
+
+	/**
+	 * Asserts that the master observable value is not disposed upon disposing
+	 * its detail observable value (bug 241318).
+	 */
+	public void testMasterNotDisposedWhenDetailDisposed() {
+		class OuterObservable extends WritableValue {
+			boolean disposed = false;
+
+			public synchronized void dispose() {
+				disposed = true;
+				super.dispose();
+			}
+		}
+
+		OuterObservable outerObservable = new OuterObservable();
+		WritableMapFactory factory = new WritableMapFactory();
+		DetailObservableMap detailObservable = new DetailObservableMap(factory,
+				outerObservable, null, null);
+
+		assertFalse(outerObservable.disposed);
+
+		detailObservable.dispose();
+		assertFalse(outerObservable.disposed);
+	}
+
+	public void testDisposeMasterDisposesDetail() {
+		IObservableValue master = new WritableValue();
+		WritableMapFactory factory = new WritableMapFactory();
+		master.setValue("");
+
+		IObservableMap detailObservable = MasterDetailObservables.detailMap(
+				master, factory, null, null);
+		DisposeEventTracker tracker = DisposeEventTracker
+				.observe(detailObservable);
+
+		master.dispose();
+
+		assertEquals(1, tracker.count);
+		assertTrue(detailObservable.isDisposed());
+	}
+
+	public void testDisposeWhileFiringEvents() {
+		IObservableValue master = new WritableValue();
+		WritableMapFactory factory = new WritableMapFactory();
+		master.setValue("");
+
+		final IObservableMap[] detailObservable = new IObservableMap[1];
+
+		master.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				detailObservable[0].dispose();
+			}
+		});
+
+		detailObservable[0] = MasterDetailObservables.detailMap(master,
+				factory, null, null);
+
+		master.setValue("New Value");
+	}
+
+	private static class WritableMapFactory implements IObservableFactory {
+		Object keyType = Object.class;
+		Object valueType = Object.class;
+
+		public IObservable createObservable(Object target) {
+			return new WritableMap(keyType, valueType);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableSetTest.java
new file mode 100644
index 0000000..854d2ca
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableSetTest.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 221351, 213145
+ *     Ovidio Mallo - bug 241318
+ *     Tom Schindl - bug 287601
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+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.set.WritableSet;
+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.internal.databinding.observable.masterdetail.DetailObservableSet;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DetailObservableSetTest extends AbstractDefaultRealmTestCase {
+	/**
+	 * Asserts the use case of specifying null on construction for the detail
+	 * type of the detail set.
+	 * 
+	 * @throws Exception
+	 */
+	public void testElementTypeNull() throws Exception {
+		WritableValue observableValue = new WritableValue(new WritableSet(
+				new HashSet(), Object.class), null);
+
+		WritableSetFactory factory = new WritableSetFactory();
+		DetailObservableSet detailObservable = new DetailObservableSet(factory,
+				observableValue, null);
+		assertNull(detailObservable.getElementType());
+
+		factory.type = Object.class;
+		observableValue.setValue(new WritableSet(Arrays
+				.asList(new Object[] { new Object() }), String.class));
+		assertNull("element type not null", detailObservable.getElementType());
+
+		factory.type = String.class;
+		// set the value again to ensure that the observable doesn't update the
+		// element type with that of the new element type
+		observableValue.setValue(new WritableSet(Arrays
+				.asList(new String[] { "1" }), Object.class));
+		assertNull("element type not null", detailObservable.getElementType());
+	}
+
+	/**
+	 * Asserts that you can't change the type across multiple inner observables.
+	 * 
+	 * @throws Exception
+	 */
+	public void testElementTypeNotNull() throws Exception {
+		WritableValue observableValue = new WritableValue(new WritableSet(
+				new HashSet(), Object.class), null);
+
+		WritableSetFactory factory = new WritableSetFactory();
+		DetailObservableSet detailObservable = new DetailObservableSet(factory,
+				observableValue, Object.class);
+		assertEquals(factory.type, detailObservable.getElementType());
+
+		try {
+			factory.type = String.class;
+			observableValue.setValue(new WritableSet(Arrays
+					.asList(new Object[] { new Object() }), String.class));
+			fail("if an element type is set this cannot be changed");
+		} catch (AssertionFailedException e) {
+		}
+	}
+
+	/**
+	 * Asserts that the master observable value is not disposed upon disposing
+	 * its detail observable value (bug 241318).
+	 */
+	public void testMasterNotDisposedWhenDetailDisposed() {
+		class OuterObservable extends WritableValue {
+			boolean disposed = false;
+
+			public synchronized void dispose() {
+				disposed = true;
+				super.dispose();
+			}
+		}
+
+		OuterObservable outerObservable = new OuterObservable();
+		WritableSetFactory factory = new WritableSetFactory();
+		DetailObservableSet detailObservable = new DetailObservableSet(factory,
+				outerObservable, null);
+
+		assertFalse(outerObservable.disposed);
+
+		detailObservable.dispose();
+		assertFalse(outerObservable.disposed);
+	}
+
+	public void testDisposeMasterDisposesDetail() {
+		IObservableValue master = new WritableValue();
+		WritableSetFactory factory = new WritableSetFactory();
+		master.setValue("");
+
+		IObservableSet detailObservable = MasterDetailObservables.detailSet(
+				master, factory, null);
+		DisposeEventTracker tracker = DisposeEventTracker
+				.observe(detailObservable);
+
+		master.dispose();
+
+		assertEquals(1, tracker.count);
+		assertTrue(detailObservable.isDisposed());
+	}
+
+	public void testDisposeWhileFiringEvents() {
+		IObservableValue master = new WritableValue();
+		WritableSetFactory factory = new WritableSetFactory();
+		master.setValue("");
+
+		final IObservableSet[] detailObservable = new IObservableSet[1];
+
+		master.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				detailObservable[0].dispose();
+			}
+		});
+
+		detailObservable[0] = MasterDetailObservables.detailSet(master,
+				factory, null);
+
+		master.setValue("New Value");
+	}
+
+	private static class WritableSetFactory implements IObservableFactory {
+		Object type = Object.class;
+
+		public IObservable createObservable(Object target) {
+			return new WritableSet(new HashSet(), type);
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DetailObservableSetTest.class.getName());
+		suite.addTestSuite(DetailObservableSetTest.class);
+		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableCollectionContractDelegate {
+		Object elementType = Object.class;
+
+		public IObservableCollection createObservableCollection(
+				final Realm realm, final int elementCount) {
+
+			IObservableValue master = new WritableValue(realm, new Integer(
+					elementCount), Integer.class);
+			IObservableFactory factory = new FactoryStub(realm, elementType);
+			return new DetailObservableSetStub(factory, master, elementType);
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return elementType;
+		}
+
+		public void change(IObservable observable) {
+			final IObservableValue master = ((DetailObservableSetStub) observable).master;
+			master.setValue(new Integer(((Integer) master.getValue())
+					.intValue() + 1));
+		}
+	}
+
+	static class FactoryStub implements IObservableFactory {
+		private Realm realm;
+		private Object elementType;
+
+		FactoryStub(Realm realm, Object elementType) {
+			this.realm = realm;
+			this.elementType = elementType;
+		}
+
+		Object type = Object.class;
+
+		public IObservable createObservable(Object target) {
+			int elementCount = ((Integer) target).intValue();
+			final Set wrappedSet = new HashSet();
+			for (int i = 0; i < elementCount; i++)
+				wrappedSet.add(new Object());
+			return new WritableSet(realm, wrappedSet, elementType);
+		}
+	}
+
+	static class DetailObservableSetStub extends DetailObservableSet {
+		IObservableValue master;
+
+		DetailObservableSetStub(IObservableFactory factory,
+				IObservableValue master, Object elementType) {
+			super(factory, master, elementType);
+			this.master = master;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableValueTest.java
new file mode 100755
index 0000000..6d37240
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/DetailObservableValueTest.java
@@ -0,0 +1,227 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 147515
+ *     Matthew Hall - bugs 221351, 213145
+ *     Ovidio Mallo - bug 241318
+ *     Tom Schindl - bug 287601
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+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.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.internal.databinding.observable.masterdetail.DetailObservableValue;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 3.2
+ */
+public class DetailObservableValueTest extends AbstractDefaultRealmTestCase {
+	private WritableValue outerObservable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		outerObservable = new WritableValue();
+	}
+
+	public void testSetValue() throws Exception {
+		WritableValueFactory factory = new WritableValueFactory();
+		outerObservable.setValue("");
+
+		IObservableValue detailObservable = MasterDetailObservables
+				.detailValue(outerObservable, factory, null);
+		WritableValue innerObservable = factory.innerObservable;
+		Object value = new Object();
+
+		assertFalse(value.equals(innerObservable.getValue()));
+		detailObservable.setValue(value);
+		assertEquals("inner value", value, innerObservable.getValue());
+	}
+
+	public void testGetValueType() throws Exception {
+		DetailObservableValue detailObservable = new DetailObservableValue(
+				outerObservable, null, String.class);
+		assertEquals(String.class, detailObservable.getValueType());
+	}
+
+	/**
+	 * Asserts that when a null value type is set for the detail observable no
+	 * type checking is performed and the value type is always <code>null</code>
+	 * .
+	 */
+	public void testGetValueTypeNullValueType() throws Exception {
+		WritableValueFactory factory = new WritableValueFactory();
+		DetailObservableValue detailObservable = new DetailObservableValue(
+				outerObservable, factory, null);
+		assertNull(detailObservable.getValueType());
+		factory.type = String.class;
+
+		// force the inner observable to be recreated
+		outerObservable.setValue("1");
+		assertNull("value type should be ignored", detailObservable
+				.getValueType());
+
+		factory.type = Object.class;
+
+		// force the inner observable to be recreated
+		outerObservable.setValue("2");
+		assertNull("value type should be ignored", detailObservable
+				.getValueType());
+	}
+
+	/**
+	 * Asserts that the master observable value is not disposed upon disposing
+	 * its detail observable value (bug 241318).
+	 */
+	public void testMasterNotDisposedWhenDetailDisposed() {
+		class OuterObservable extends WritableValue {
+			boolean disposed = false;
+
+			public synchronized void dispose() {
+				disposed = true;
+				super.dispose();
+			}
+		}
+
+		OuterObservable outerObservable = new OuterObservable();
+		WritableValueFactory factory = new WritableValueFactory();
+		DetailObservableValue detailObservable = new DetailObservableValue(
+				outerObservable, factory, null);
+
+		assertFalse(outerObservable.disposed);
+
+		detailObservable.dispose();
+		assertFalse(outerObservable.disposed);
+	}
+
+	public void testDisposeMasterDisposesDetail() {
+		IObservableValue master = new WritableValue();
+		WritableValueFactory factory = new WritableValueFactory();
+		master.setValue("");
+
+		IObservableValue detailObservable = MasterDetailObservables
+				.detailValue(master, factory, null);
+		DisposeEventTracker tracker = DisposeEventTracker
+				.observe(detailObservable);
+
+		master.dispose();
+
+		assertEquals(1, tracker.count);
+		assertTrue(detailObservable.isDisposed());
+	}
+
+	public void testDisposeWhileFiringEvents() {
+		IObservableValue master = new WritableValue();
+		WritableValueFactory factory = new WritableValueFactory();
+		master.setValue("");
+
+		final IObservableValue[] detailObservable = new IObservableValue[1];
+
+		master.addValueChangeListener(new IValueChangeListener() {
+			public void handleValueChange(ValueChangeEvent event) {
+				detailObservable[0].dispose();
+			}
+		});
+
+		detailObservable[0] = MasterDetailObservables.detailValue(master,
+				factory, null);
+
+		master.setValue("New Value");
+	}
+
+	/**
+	 * Factory that creates WritableValues with the target as the value.
+	 */
+	static class WritableValueFactory implements IObservableFactory {
+		Realm realm;
+		WritableValue innerObservable;
+		Object type;
+
+		public IObservable createObservable(Object target) {
+			return innerObservable = new WritableValue(realm == null ? Realm
+					.getDefault() : realm, target, type);
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(DetailObservableValueTest.class
+				.getName());
+		suite.addTestSuite(DetailObservableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class DetailObservableValueStub extends
+			DetailObservableValue {
+		IObservableValue outerObservableValue;
+
+		DetailObservableValueStub(IObservableValue outerObservableValue,
+				IObservableFactory valueFactory, Object detailType) {
+			super(outerObservableValue, valueFactory, detailType);
+			this.outerObservableValue = outerObservableValue;
+		}
+	}
+
+	private static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Object valueType;
+		private Realm previousRealm;
+
+		public void setUp() {
+			super.setUp();
+			valueType = new Object();
+			previousRealm = Realm.getDefault();
+
+			RealmTester.setDefault(new CurrentRealm());
+		}
+
+		public void tearDown() {
+			RealmTester.setDefault(previousRealm);
+			super.tearDown();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			WritableValueFactory valueFactory = new WritableValueFactory();
+			valueFactory.realm = realm;
+			valueFactory.type = valueType;
+			WritableValue masterObservableValue = new WritableValue(realm,
+					new Object(), null);
+			return new DetailObservableValueStub(masterObservableValue,
+					valueFactory, valueType);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Object();
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return valueType;
+		}
+
+		public void change(IObservable observable) {
+			DetailObservableValueStub value = (DetailObservableValueStub) observable;
+			value.outerObservableValue.setValue(createValue(value));
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java
new file mode 100644
index 0000000..eefa802
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailValueObservableListTest.java
@@ -0,0 +1,391 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 305367)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.3
+ */
+public class ListDetailValueObservableListTest extends
+		AbstractDefaultRealmTestCase {
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				ListDetailValueObservableListTest.class.getName());
+		suite.addTestSuite(ListDetailValueObservableListTest.class);
+		suite.addTest(ObservableListContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	public void testUnmodifiability() {
+		WritableList masterObservableList = new WritableList();
+		masterObservableList.add(new SimplePerson());
+		masterObservableList.add(new SimplePerson());
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterObservableList, BeansObservables.valueFactory("name"),
+				null);
+
+		try {
+			ldol.add("name");
+			fail("ListDetailValueObservableList must not be modifiable.");
+		} catch (UnsupportedOperationException e) {
+			// expected exception
+		}
+
+		try {
+			ldol.remove(masterObservableList.get(0));
+			fail("ListDetailValueObservableList must not be modifiable.");
+		} catch (UnsupportedOperationException e) {
+			// expected exception
+		}
+
+		try {
+			ldol.removeAll(Collections.singleton(masterObservableList.get(0)));
+			fail("ListDetailValueObservableList must not be modifiable.");
+		} catch (UnsupportedOperationException e) {
+			// expected exception
+		}
+
+		try {
+			ldol.retainAll(Collections.EMPTY_LIST);
+			fail("ListDetailValueObservableList must not be modifiable.");
+		} catch (UnsupportedOperationException e) {
+			// expected exception
+		}
+
+		try {
+			ldol.move(0, 1);
+			fail("ListDetailValueObservableList must not be modifiable.");
+		} catch (UnsupportedOperationException e) {
+			// expected exception
+		}
+	}
+
+	public void testGetElementType() {
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				new WritableList(), BeansObservables.valueFactory("name"),
+				String.class);
+
+		assertSame(String.class, ldol.getElementType());
+	}
+
+	public void testGetObserved() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		// The observed object is the master list.
+		assertSame(masterList, ldol.getObserved());
+	}
+
+	public void testMasterListInitiallyNotEmpty() {
+		WritableList masterList = new WritableList();
+		SimplePerson person = new SimplePerson();
+		person.setName("name");
+		masterList.add(person);
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		// Make sure that a non-empty master list is initialized correctly.
+		assertEquals(masterList.size(), ldol.size());
+		assertEquals(person.getName(), ldol.get(0));
+	}
+
+	public void testAddRemove() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		// Initially, the detail list is empty.
+		assertTrue(ldol.isEmpty());
+
+		// Add a first person and check that its name is in the detail list.
+		SimplePerson p1 = new SimplePerson();
+		p1.setName("name1");
+		masterList.add(p1);
+		assertEquals(masterList.size(), ldol.size());
+		assertEquals(p1.getName(), ldol.get(0));
+
+		// Add a second person and check that it's name is in the detail list.
+		SimplePerson p2 = new SimplePerson();
+		p2.setName("name2");
+		masterList.add(p2);
+		assertEquals(masterList.size(), ldol.size());
+		assertEquals(p2.getName(), ldol.get(1));
+
+		// Remove the first person from the master list and check that we still
+		// have the name of the second person in the detail list.
+		masterList.remove(0);
+		assertEquals(masterList.size(), ldol.size());
+		assertEquals(p2.getName(), ldol.get(0));
+
+		// Remove the second person as well.
+		masterList.remove(0);
+		assertTrue(ldol.isEmpty());
+	}
+
+	public void testChangeDetail() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		// Change the detail attribute explicitly.
+		SimplePerson p1 = new SimplePerson();
+		p1.setName("name1");
+		masterList.add(p1);
+		assertEquals(p1.getName(), ldol.get(0));
+		p1.setName("name2");
+		assertEquals(p1.getName(), ldol.get(0));
+
+		// Change the detail attribute by changing the master.
+		SimplePerson p2 = new SimplePerson();
+		p2.setName("name3");
+		masterList.set(0, p2);
+		assertEquals(p2.getName(), ldol.get(0));
+	}
+
+	public void testSet() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		// Change the detail attribute explicitly.
+		SimplePerson person = new SimplePerson();
+		person.setName("name1");
+		masterList.add(person);
+		assertEquals(person.getName(), ldol.get(0));
+
+		// Set a new name on the detail list.
+		ldol.set(0, "name2");
+		// Check that the name has been propagated to the master.
+		assertEquals("name2", person.getName());
+		assertEquals(person.getName(), ldol.get(0));
+	}
+
+	public void testDuplicateMasterElements() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		SimplePerson master = new SimplePerson();
+		master.setName("name1");
+
+		// Add the same master twice.
+		masterList.add(master);
+		masterList.add(master);
+
+		// Attach the change listener to the detail list.
+		ListChangeEventTracker changeTracker = ListChangeEventTracker
+				.observe(ldol);
+
+		// Setting the name on master should trigger an event on both
+		// occurrences of in the master list.
+		master.setName("name2");
+
+		// We should have 2 replace diffs, i.e. 4 diff entries.
+		assertEquals(1, changeTracker.count);
+		assertEquals(4, changeTracker.event.diff.getDifferences().length);
+		assertReplaceDiffAt(changeTracker.event.diff, 0, 0, "name1", "name2");
+		assertReplaceDiffAt(changeTracker.event.diff, 2, 0, "name1", "name2");
+
+		// Remove one instance of the master (one will remain).
+		masterList.remove(master);
+
+		// It should still be possible to work on the remaining master instance.
+		ldol.set(0, "name3");
+		assertEquals("name3", master.getName());
+	}
+
+	public void testDetailObservableChangeEvent() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		ListChangeEventTracker changeTracker = ListChangeEventTracker
+				.observe(ldol);
+
+		SimplePerson person = new SimplePerson();
+		person.setName("old name");
+
+		// Initially, we should not have received any event.
+		assertEquals(0, changeTracker.count);
+
+		// Add the person and check that we receive an addition event on the
+		// correct index and with the correct value.
+		masterList.add(person);
+		assertEquals(1, changeTracker.count);
+		assertEquals(1, changeTracker.event.diff.getDifferences().length);
+		assertTrue(changeTracker.event.diff.getDifferences()[0].isAddition());
+		assertEquals(0,
+				changeTracker.event.diff.getDifferences()[0].getPosition());
+		assertEquals(person.getName(),
+				changeTracker.event.diff.getDifferences()[0].getElement());
+
+		// Change the detail property and check that we receive a replace event.
+		person.setName("new name");
+		assertEquals(2, changeTracker.count);
+		assertIsSingleReplaceDiff(changeTracker.event.diff, 0, "old name",
+				"new name");
+	}
+
+	private void assertIsSingleReplaceDiff(ListDiff diff, int index,
+			Object oldElement, Object newElement) {
+		// We should have 2 diff entries.
+		assertEquals(2, diff.getDifferences().length);
+
+		// Check that it indeed is a replace diff.
+		assertReplaceDiffAt(diff, 0, index, oldElement, newElement);
+	}
+
+	private void assertReplaceDiffAt(ListDiff diff, int diffOffset, int index,
+			Object oldElement, Object newElement) {
+		ListDiffEntry entry1 = diff.getDifferences()[0];
+		ListDiffEntry entry2 = diff.getDifferences()[1];
+
+		// One diff entry must be an addition, the other a removal.
+		assertTrue(entry1.isAddition() != entry2.isAddition());
+
+		// Check for the index on the diff entries.
+		assertEquals(index, entry1.getPosition());
+		assertEquals(index, entry2.getPosition());
+
+		// Check for the old/new element values on both diff entries.
+		if (entry1.isAddition()) {
+			assertEquals(oldElement, entry2.getElement());
+			assertEquals(newElement, entry1.getElement());
+		} else {
+			assertEquals(oldElement, entry1.getElement());
+			assertEquals(newElement, entry2.getElement());
+		}
+	}
+
+	public void testMasterNull() {
+		WritableList masterObservableList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterObservableList, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Make sure null values are handled gracefully.
+		masterObservableList.add(null);
+		assertEquals(1, ldol.size());
+		assertNull(ldol.get(0));
+	}
+
+	public void testDetailObservableValuesAreDisposed() {
+		final List detailObservables = new ArrayList();
+		IObservableFactory detailValueFactory = new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				WritableValue detailObservable = new WritableValue();
+				// Remember the created observables.
+				detailObservables.add(detailObservable);
+				return detailObservable;
+			}
+		};
+
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, detailValueFactory, null);
+
+		masterList.add(new Object());
+		masterList.add(new Object());
+
+		assertEquals(ldol.size(), detailObservables.size());
+
+		// No detail observables should be disposed yet.
+		assertFalse(((WritableValue) detailObservables.get(0)).isDisposed());
+		assertFalse(((WritableValue) detailObservables.get(1)).isDisposed());
+
+		// Only the detail observable for the removed master should be disposed.
+		masterList.remove(1);
+		assertFalse(((WritableValue) detailObservables.get(0)).isDisposed());
+		assertTrue(((WritableValue) detailObservables.get(1)).isDisposed());
+
+		// After disposing the detail list, all detail observables should be
+		// disposed.
+		ldol.dispose();
+		assertTrue(((WritableValue) detailObservables.get(0)).isDisposed());
+		assertTrue(((WritableValue) detailObservables.get(1)).isDisposed());
+	}
+
+	public void testDisposeOnMasterDisposed() {
+		WritableList masterList = new WritableList();
+		ListDetailValueObservableList ldol = new ListDetailValueObservableList(
+				masterList, BeansObservables.valueFactory("name"), String.class);
+
+		// Initially, nothing should be disposed.
+		assertFalse(masterList.isDisposed());
+		assertFalse(ldol.isDisposed());
+
+		// Upon disposing the master list, the detail list should be disposed as
+		// well.
+		masterList.dispose();
+		assertTrue(masterList.isDisposed());
+		assertTrue(ldol.isDisposed());
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			WritableList masterList = new WritableList(realm);
+			for (int i = 0; i < elementCount; i++) {
+				masterList.add(new SimplePerson());
+			}
+
+			return new TestListDetailValueObservableList(masterList,
+					BeansObservables.valueFactory(realm, "name"), String.class);
+		}
+
+		public void change(IObservable observable) {
+			TestListDetailValueObservableList ldol = (TestListDetailValueObservableList) observable;
+			ldol.masterList.add(new SimplePerson());
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return String.class;
+		}
+	}
+
+	private static class TestListDetailValueObservableList extends
+			ListDetailValueObservableList {
+		final IObservableList masterList;
+
+		public TestListDetailValueObservableList(IObservableList masterList,
+				IObservableFactory detailValueFactory, Object detailType) {
+			super(masterList, detailValueFactory, detailType);
+			this.masterList = masterList;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java
new file mode 100644
index 0000000..5146b89
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/MapDetailValueObservableMapTest.java
@@ -0,0 +1,309 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 305367)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.map.WritableMap;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.internal.databinding.observable.masterdetail.MapDetailValueObservableMap;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.3
+ */
+public class MapDetailValueObservableMapTest extends
+		AbstractDefaultRealmTestCase {
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				MapDetailValueObservableMapTest.class.getName());
+		suite.addTestSuite(MapDetailValueObservableMapTest.class);
+		return suite;
+	}
+
+	public void testGetKeyType() {
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				new WritableMap(SimplePerson.class, SimplePerson.class),
+				BeansObservables.valueFactory("name"), String.class);
+
+		assertSame(SimplePerson.class, mdom.getKeyType());
+	}
+
+	public void testGetValueType() {
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				new WritableMap(), BeansObservables.valueFactory("name"),
+				String.class);
+
+		assertSame(String.class, mdom.getValueType());
+	}
+
+	public void testGetObserved() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// The observed object is the master key set.
+		assertSame(masterMap, mdom.getObserved());
+	}
+
+	public void testMasterSetInitiallyNotEmpty() {
+		WritableMap masterMap = new WritableMap();
+		SimplePerson person = new SimplePerson();
+		person.setName("name");
+		masterMap.put(person, person);
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Make sure that a non-empty master key set is initialized correctly.
+		assertEquals(masterMap.size(), mdom.size());
+		assertEquals(person.getName(), mdom.get(person));
+	}
+
+	public void testAddRemove() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Initially, the detail map is empty.
+		assertTrue(mdom.isEmpty());
+
+		// Add a first person and check that its name is in the detail map.
+		SimplePerson p1 = new SimplePerson();
+		p1.setName("name1");
+		masterMap.put(p1, p1);
+		assertEquals(masterMap.size(), mdom.size());
+		assertEquals(p1.getName(), mdom.get(p1));
+
+		// Add a second person and check that it's name is in the detail map.
+		SimplePerson p2 = new SimplePerson();
+		p2.setName("name2");
+		masterMap.put(p2, p2);
+		assertEquals(masterMap.size(), mdom.size());
+		assertEquals(p2.getName(), mdom.get(p2));
+
+		// Remove the first person from the master map and check that we still
+		// have the name of the second person in the detail map.
+		masterMap.remove(p1);
+		assertEquals(masterMap.size(), mdom.size());
+		assertEquals(p2.getName(), mdom.get(p2));
+
+		// Remove the second person as well.
+		masterMap.remove(p2);
+		assertTrue(mdom.isEmpty());
+	}
+
+	public void testChangeDetail() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Change the detail attribute explicitly.
+		SimplePerson p1 = new SimplePerson();
+		p1.setName("name1");
+		masterMap.put(p1, p1);
+		assertEquals(p1.getName(), mdom.get(p1));
+		p1.setName("name2");
+		assertEquals(p1.getName(), mdom.get(p1));
+
+		// Change the detail attribute by changing the master.
+		SimplePerson p2 = new SimplePerson();
+		p2.setName("name3");
+		masterMap.put(p1, p2);
+		assertEquals(p2.getName(), mdom.get(p1));
+	}
+
+	public void testPut() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Change the detail attribute explicitly.
+		SimplePerson person = new SimplePerson();
+		person.setName("name1");
+		masterMap.put(person, person);
+		assertEquals(person.getName(), mdom.get(person));
+
+		// Set a new name on the detail map.
+		mdom.put(person, "name2");
+		// Check that the name has been propagated to the master.
+		assertEquals("name2", person.getName());
+		assertEquals(person.getName(), mdom.get(person));
+	}
+
+	public void testContainsValue() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Add a person with a given name.
+		SimplePerson person = new SimplePerson();
+		person.setName("name");
+		masterMap.put(person, person);
+
+		// Make sure the name of the person is contained.
+		assertTrue(mdom.containsValue(person.getName()));
+
+		// Remove the person and make sure that it's name cannot be found
+		// anymore.
+		masterMap.remove(person);
+		assertFalse(mdom.containsValue(person.getName()));
+	}
+
+	public void testRemove() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Add two person objects to the map.
+		SimplePerson p1 = new SimplePerson();
+		SimplePerson p2 = new SimplePerson();
+		masterMap.put(p1, p1);
+		masterMap.put(p2, p2);
+
+		// Initially, both person objects should be contained in the detail map.
+		assertTrue(mdom.containsKey(p1));
+		assertTrue(mdom.containsKey(p2));
+
+		// Remove one person and check that it is not contained anymore.
+		mdom.remove(p1);
+		assertFalse(mdom.containsKey(p1));
+		assertTrue(mdom.containsKey(p2));
+
+		// Trying to remove a non-existent is allowed but has no effect.
+		mdom.remove(p1);
+		assertFalse(mdom.containsKey(p1));
+		assertTrue(mdom.containsKey(p2));
+	}
+
+	public void testDetailObservableChangeEvent() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		MapChangeEventTracker changeTracker = MapChangeEventTracker
+				.observe(mdom);
+
+		SimplePerson person = new SimplePerson();
+		person.setName("old name");
+
+		// Initially, we should not have received any event.
+		assertEquals(0, changeTracker.count);
+
+		// Add the person and check that we receive an addition event on the
+		// correct index and with the correct value.
+		masterMap.put(person, person);
+		assertEquals(1, changeTracker.count);
+		assertEquals(1, changeTracker.event.diff.getAddedKeys().size());
+		assertEquals(0, changeTracker.event.diff.getRemovedKeys().size());
+		assertEquals(0, changeTracker.event.diff.getChangedKeys().size());
+		assertSame(person, changeTracker.event.diff.getAddedKeys().iterator()
+				.next());
+		assertNull(changeTracker.event.diff.getOldValue(person));
+		assertEquals("old name", changeTracker.event.diff.getNewValue(person));
+
+		// Change the detail property and check that we receive a replace
+		person.setName("new name");
+		assertEquals(2, changeTracker.count);
+		assertEquals(0, changeTracker.event.diff.getAddedKeys().size());
+		assertEquals(0, changeTracker.event.diff.getRemovedKeys().size());
+		assertEquals(1, changeTracker.event.diff.getChangedKeys().size());
+		assertSame(person, changeTracker.event.diff.getChangedKeys().iterator()
+				.next());
+		assertEquals("old name", changeTracker.event.diff.getOldValue(person));
+		assertEquals("new name", changeTracker.event.diff.getNewValue(person));
+	}
+
+	public void testMasterNull() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Make sure null values are handled gracefully.
+		masterMap.put(null, null);
+		assertEquals(1, mdom.size());
+		assertNull(mdom.get(null));
+	}
+
+	public void testDetailObservableValuesAreDisposed() {
+		final Map detailObservables = new HashMap();
+		IObservableFactory detailValueFactory = new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				WritableValue detailObservable = new WritableValue();
+				// Remember the created observables.
+				detailObservables.put(target, detailObservable);
+				return detailObservable;
+			}
+		};
+
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, detailValueFactory, null);
+
+		Object master1 = new Object();
+		Object master2 = new Object();
+		masterMap.put(master1, master1);
+		masterMap.put(master2, master2);
+
+		// Attach a listener in order to ensure that all detail observables are
+		// actually created.
+		MapChangeEventTracker.observe(mdom);
+
+		assertEquals(mdom.size(), detailObservables.size());
+
+		// No detail observables should be disposed yet.
+		assertFalse(((WritableValue) detailObservables.get(master1))
+				.isDisposed());
+		assertFalse(((WritableValue) detailObservables.get(master2))
+				.isDisposed());
+
+		// Only the detail observable for the removed master should be disposed.
+		masterMap.remove(master2);
+		assertFalse(((WritableValue) detailObservables.get(master1))
+				.isDisposed());
+		assertTrue(((WritableValue) detailObservables.get(master2))
+				.isDisposed());
+
+		// After disposing the detail map, all detail observables should be
+		// disposed.
+		mdom.dispose();
+		assertTrue(((WritableValue) detailObservables.get(master1))
+				.isDisposed());
+		assertTrue(((WritableValue) detailObservables.get(master2))
+				.isDisposed());
+	}
+
+	public void testDisposeOnMasterDisposed() {
+		WritableMap masterMap = new WritableMap();
+		MapDetailValueObservableMap mdom = new MapDetailValueObservableMap(
+				masterMap, BeansObservables.valueFactory("name"), String.class);
+
+		// Initially, nothing should be disposed.
+		assertFalse(masterMap.isDisposed());
+		assertFalse(mdom.isDisposed());
+
+		// Upon disposing the master map, the detail map should be disposed as
+		// well.
+		masterMap.dispose();
+		assertTrue(masterMap.isDisposed());
+		assertTrue(mdom.isDisposed());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java
new file mode 100644
index 0000000..26bcf1b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/SetDetailValueObservableMapTest.java
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 305367)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.internal.databinding.observable.masterdetail.SetDetailValueObservableMap;
+import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.3
+ */
+public class SetDetailValueObservableMapTest extends
+		AbstractDefaultRealmTestCase {
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SetDetailValueObservableMapTest.class
+				.getName());
+		suite.addTestSuite(SetDetailValueObservableMapTest.class);
+		return suite;
+	}
+
+	public void testGetValueType() {
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				new WritableSet(), BeansObservables.valueFactory("name"),
+				String.class);
+
+		assertSame(String.class, sdom.getValueType());
+	}
+
+	public void testGetObserved() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// The observed object is the master key set.
+		assertSame(masterKeySet, sdom.getObserved());
+	}
+
+	public void testMasterSetInitiallyNotEmpty() {
+		WritableSet masterKeySet = new WritableSet();
+		SimplePerson person = new SimplePerson();
+		person.setName("name");
+		masterKeySet.add(person);
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Make sure that a non-empty master key set is initialized correctly.
+		assertEquals(masterKeySet.size(), sdom.size());
+		assertEquals(person.getName(), sdom.get(person));
+	}
+
+	public void testAddRemove() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Initially, the detail map is empty.
+		assertTrue(sdom.isEmpty());
+
+		// Add a first person and check that its name is in the detail list.
+		SimplePerson p1 = new SimplePerson();
+		p1.setName("name1");
+		masterKeySet.add(p1);
+		assertEquals(masterKeySet.size(), sdom.size());
+		assertEquals(p1.getName(), sdom.get(p1));
+
+		// Add a second person and check that it's name is in the detail list.
+		SimplePerson p2 = new SimplePerson();
+		p2.setName("name2");
+		masterKeySet.add(p2);
+		assertEquals(masterKeySet.size(), sdom.size());
+		assertEquals(p2.getName(), sdom.get(p2));
+
+		// Remove the first person from the master list and check that we still
+		// have the name of the second person in the detail list.
+		masterKeySet.remove(p1);
+		assertEquals(masterKeySet.size(), sdom.size());
+		assertEquals(p2.getName(), sdom.get(p2));
+
+		// Remove the second person as well.
+		masterKeySet.remove(p2);
+		assertTrue(sdom.isEmpty());
+	}
+
+	public void testChangeDetail() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Change the detail attribute explicitly.
+		SimplePerson p1 = new SimplePerson();
+		p1.setName("name1");
+		masterKeySet.add(p1);
+		assertEquals(p1.getName(), sdom.get(p1));
+		p1.setName("name2");
+		assertEquals(p1.getName(), sdom.get(p1));
+
+		// Change the detail attribute by changing the master.
+		SimplePerson p2 = new SimplePerson();
+		p2.setName("name3");
+		masterKeySet.add(p2);
+		assertEquals(p2.getName(), sdom.get(p2));
+	}
+
+	public void testPut() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Change the detail attribute explicitly.
+		SimplePerson person = new SimplePerson();
+		person.setName("name1");
+		masterKeySet.add(person);
+		assertEquals(person.getName(), sdom.get(person));
+
+		// Set a new name on the detail map.
+		sdom.put(person, "name2");
+		// Check that the name has been propagated to the master.
+		assertEquals("name2", person.getName());
+		assertEquals(person.getName(), sdom.get(person));
+	}
+
+	public void testContainsValue() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Add a person with a given name.
+		SimplePerson person = new SimplePerson();
+		person.setName("name");
+		masterKeySet.add(person);
+
+		// Make sure the name of the person is contained.
+		assertTrue(sdom.containsValue(person.getName()));
+
+		// Remove the person and make sure that it's name cannot be found
+		// anymore.
+		masterKeySet.remove(person);
+		assertFalse(sdom.containsValue(person.getName()));
+	}
+
+	public void testRemove() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Add two person objects to the map.
+		SimplePerson p1 = new SimplePerson();
+		SimplePerson p2 = new SimplePerson();
+		masterKeySet.add(p1);
+		masterKeySet.add(p2);
+
+		// Initially, both person objects should be contained in the detail map.
+		assertTrue(sdom.containsKey(p1));
+		assertTrue(sdom.containsKey(p2));
+
+		// Remove one person and check that it is not contained anymore.
+		sdom.remove(p1);
+		assertFalse(sdom.containsKey(p1));
+		assertTrue(sdom.containsKey(p2));
+
+		// Trying to remove a non-existent is allowed but has no effect.
+		sdom.remove(p1);
+		assertFalse(sdom.containsKey(p1));
+		assertTrue(sdom.containsKey(p2));
+	}
+
+	public void testDetailObservableChangeEvent() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		MapChangeEventTracker changeTracker = MapChangeEventTracker
+				.observe(sdom);
+
+		SimplePerson person = new SimplePerson();
+		person.setName("old name");
+
+		// Initially, we should not have received any event.
+		assertEquals(0, changeTracker.count);
+
+		// Add the person and check that we receive an addition event on the
+		// correct index and with the correct value.
+		masterKeySet.add(person);
+		assertEquals(1, changeTracker.count);
+		assertEquals(1, changeTracker.event.diff.getAddedKeys().size());
+		assertEquals(0, changeTracker.event.diff.getRemovedKeys().size());
+		assertEquals(0, changeTracker.event.diff.getChangedKeys().size());
+		assertSame(person, changeTracker.event.diff.getAddedKeys().iterator()
+				.next());
+		assertNull(changeTracker.event.diff.getOldValue(person));
+		assertEquals("old name", changeTracker.event.diff.getNewValue(person));
+
+		// Change the detail property and check that we receive a replace
+		person.setName("new name");
+		assertEquals(2, changeTracker.count);
+		assertEquals(0, changeTracker.event.diff.getAddedKeys().size());
+		assertEquals(0, changeTracker.event.diff.getRemovedKeys().size());
+		assertEquals(1, changeTracker.event.diff.getChangedKeys().size());
+		assertSame(person, changeTracker.event.diff.getChangedKeys().iterator()
+				.next());
+		assertEquals("old name", changeTracker.event.diff.getOldValue(person));
+		assertEquals("new name", changeTracker.event.diff.getNewValue(person));
+	}
+
+	public void testMasterNull() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Make sure null values are handled gracefully.
+		masterKeySet.add(null);
+		assertEquals(1, sdom.size());
+		assertNull(sdom.get(null));
+	}
+
+	public void testDetailObservableValuesAreDisposed() {
+		final Map detailObservables = new HashMap();
+		IObservableFactory detailValueFactory = new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				WritableValue detailObservable = new WritableValue();
+				// Remember the created observables.
+				detailObservables.put(target, detailObservable);
+				return detailObservable;
+			}
+		};
+
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, detailValueFactory, null);
+
+		Object master1 = new Object();
+		Object master2 = new Object();
+		masterKeySet.add(master1);
+		masterKeySet.add(master2);
+
+		// Attach a listener in order to ensure that all detail observables are
+		// actually created.
+		MapChangeEventTracker.observe(sdom);
+
+		assertEquals(sdom.size(), detailObservables.size());
+
+		// No detail observables should be disposed yet.
+		assertFalse(((WritableValue) detailObservables.get(master1))
+				.isDisposed());
+		assertFalse(((WritableValue) detailObservables.get(master2))
+				.isDisposed());
+
+		// Only the detail observable for the removed master should be disposed.
+		masterKeySet.remove(master2);
+		assertFalse(((WritableValue) detailObservables.get(master1))
+				.isDisposed());
+		assertTrue(((WritableValue) detailObservables.get(master2))
+				.isDisposed());
+
+		// After disposing the detail map, all detail observables should be
+		// disposed.
+		sdom.dispose();
+		assertTrue(((WritableValue) detailObservables.get(master1))
+				.isDisposed());
+		assertTrue(((WritableValue) detailObservables.get(master2))
+				.isDisposed());
+	}
+
+	public void testDisposeOnMasterDisposed() {
+		WritableSet masterKeySet = new WritableSet();
+		SetDetailValueObservableMap sdom = new SetDetailValueObservableMap(
+				masterKeySet, BeansObservables.valueFactory("name"),
+				String.class);
+
+		// Initially, nothing should be disposed.
+		assertFalse(masterKeySet.isDisposed());
+		assertFalse(sdom.isDisposed());
+
+		// Upon disposing the master list, the detail list should be disposed as
+		// well.
+		masterKeySet.dispose();
+		assertTrue(masterKeySet.isDisposed());
+		assertTrue(sdom.isDisposed());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/ListSimpleValueObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/ListSimpleValueObservableListTest.java
new file mode 100644
index 0000000..8a02d8f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/ListSimpleValueObservableListTest.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 301410)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.property.value;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class ListSimpleValueObservableListTest extends
+		AbstractDefaultRealmTestCase {
+
+	public void testBug301410() {
+		BeanProperties.value(Bean.class, "value").observeDetail(
+				new WritableList()).dispose();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/MapSimpleValueObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/MapSimpleValueObservableMapTest.java
new file mode 100644
index 0000000..e3bbf51
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/MapSimpleValueObservableMapTest.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 299619)
+ *     Ovidio Mallo - bug 301370
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.map.WritableMap;
+import org.eclipse.core.internal.databinding.property.value.MapSimpleValueObservableMap;
+import org.eclipse.core.internal.databinding.property.value.SelfValueProperty;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class MapSimpleValueObservableMapTest extends
+		AbstractDefaultRealmTestCase {
+
+	public void testGetKeyValueType() {
+		WritableMap masterMap = new WritableMap(String.class, Integer.class);
+		SelfValueProperty detailProperty = new SelfValueProperty(Object.class);
+
+		MapSimpleValueObservableMap detailMap = new MapSimpleValueObservableMap(
+				masterMap, detailProperty);
+
+		assertEquals(masterMap.getKeyType(), detailMap.getKeyType());
+		assertEquals(detailProperty.getValueType(), detailMap.getValueType());
+	}
+
+	public void testPut_ReplacedOldValue() {
+		// Create any simple master map and detail property.
+		WritableMap masterMap = new WritableMap(String.class, Integer.class);
+		SelfValueProperty detailProperty = new SelfValueProperty(Integer.class);
+
+		MapSimpleValueObservableMap detailMap = new MapSimpleValueObservableMap(
+				masterMap, detailProperty);
+
+		// Our common key.
+		String key = "key";
+
+		// Add an entry on the master map for our key.
+		Integer oldValue = new Integer(111);
+		masterMap.put(key, oldValue);
+
+		// Replace the entry on the detail map for our key.
+		Integer newValue = new Integer(777);
+		Object returnedOldValue = detailMap.put(key, newValue);
+
+		// Check that the replaced old value is our original value.
+		assertSame(oldValue, returnedOldValue);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/SetSimpleValueObservableMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/SetSimpleValueObservableMapTest.java
new file mode 100644
index 0000000..c92e9b3
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/property/value/SetSimpleValueObservableMapTest.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 299619)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.internal.databinding.property.value.SelfValueProperty;
+import org.eclipse.core.internal.databinding.property.value.SetSimpleValueObservableMap;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+public class SetSimpleValueObservableMapTest extends
+		AbstractDefaultRealmTestCase {
+
+	public void testGetKeyValueType() {
+		WritableSet masterSet = WritableSet.withElementType(String.class);
+		SelfValueProperty detailProperty = new SelfValueProperty(Object.class);
+
+		SetSimpleValueObservableMap detailMap = new SetSimpleValueObservableMap(
+				masterSet, detailProperty);
+
+		assertEquals(masterSet.getElementType(), detailMap.getKeyType());
+		assertEquals(detailProperty.getValueType(), detailMap.getValueType());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/AbstractStringToNumberValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/AbstractStringToNumberValidatorTest.java
new file mode 100644
index 0000000..cf58d35
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/AbstractStringToNumberValidatorTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator;
+import org.eclipse.core.internal.databinding.validation.NumberFormatConverter;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Tests for AbstractStringToNumberValidator. Most tests should be included in
+ * StringToNumberValidatorTestHarness. This class is for the edge cases.
+ * 
+ * @since 3.2
+ */
+public class AbstractStringToNumberValidatorTest extends TestCase {
+	/**
+	 * Test for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=194353.
+	 * 
+	 * @throws Exception
+	 */
+	public void testErrorMessagesAreNotCached() throws Exception {
+		NumberFormatConverter c = StringToNumberConverter.toInteger(false);
+		ValidatorStub v = new ValidatorStub(c);
+
+		IStatus status1 = v.validate("1a");
+		assertEquals(IStatus.ERROR, status1.getSeverity());
+
+		IStatus status2 = v.validate("2b");
+		assertEquals(IStatus.ERROR, status2.getSeverity());
+
+		assertFalse("messages should not be equal", status1.getMessage().equals(status2.getMessage()));
+	}
+	
+	static class ValidatorStub extends AbstractStringToNumberValidator {
+		ValidatorStub(NumberFormatConverter c) {
+			super(c, new Integer(Integer.MIN_VALUE), new Integer(Integer.MAX_VALUE));
+		}
+		
+		protected boolean isInRange(Number number) {
+			return true;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToByteValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToByteValidatorTest.java
new file mode 100644
index 0000000..88f2750
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToByteValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToByteValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToByteValidatorTest extends NumberToNumberValidatorTestHarness {
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Integer(Byte.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator()
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToByteConverter converter = new NumberToByteConverter(NumberFormat.getInstance(),
+				fromType, false);
+		return new NumberToByteValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator()
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		NumberToByteConverter converter = new NumberToByteConverter(NumberFormat.getInstance(),
+				fromType, true);
+		return new NumberToByteValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToDoubleValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToDoubleValidatorTest.java
new file mode 100644
index 0000000..38c7a29
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToDoubleValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import java.math.BigDecimal;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToDoubleValidatorTest extends
+		NumberToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Double.MAX_VALUE));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToDoubleConverter converter = new NumberToDoubleConverter(NumberFormat.getInstance(), fromType, false);
+		return new NumberToDoubleValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		NumberToDoubleConverter converter = new NumberToDoubleConverter(NumberFormat.getInstance(), fromType, true);
+		return new NumberToDoubleValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToFloatValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToFloatValidatorTest.java
new file mode 100644
index 0000000..f2f8a58
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToFloatValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToFloatValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToFloatValidatorTest extends
+		NumberToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToFloatConverter converter = new NumberToFloatConverter(NumberFormat.getInstance(), fromType, false);
+		return new NumberToFloatValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		NumberToFloatConverter converter = new NumberToFloatConverter(NumberFormat.getInstance(), fromType, true);
+		return new NumberToFloatValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToIntegerValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToIntegerValidatorTest.java
new file mode 100644
index 0000000..37015a2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToIntegerValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToIntegerValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToIntegerValidatorTest extends
+		NumberToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Long((long) Integer.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToIntegerConverter converter = new NumberToIntegerConverter(NumberFormat.getInstance(), fromType, false);
+		return new NumberToIntegerValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		NumberToIntegerConverter converter = new NumberToIntegerConverter(NumberFormat.getInstance(), fromType, true);
+		return new NumberToIntegerValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToLongValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToLongValidatorTest.java
new file mode 100644
index 0000000..7821d75
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToLongValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToLongValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToLongValidatorTest extends
+		NumberToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToLongConverter converter = new NumberToLongConverter(NumberFormat.getInstance(), fromType, false);
+		return new NumberToLongValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		NumberToLongConverter converter = new NumberToLongConverter(NumberFormat.getInstance(), fromType, true);
+		return new NumberToLongValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToNumberValidatorTestHarness.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToNumberValidatorTestHarness.java
new file mode 100644
index 0000000..817241c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToNumberValidatorTestHarness.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.1
+ */
+public abstract class NumberToNumberValidatorTestHarness extends TestCase {
+	protected abstract NumberToNumberValidator doGetToPrimitiveValidator(Class fromType);
+	protected abstract NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType);
+	protected abstract Number doGetOutOfRangeNumber();
+	
+	public void testValidateNullForBoxedTypeIsOK() throws Exception {
+		IStatus status = doGetToBoxedTypeValidator(Integer.class).validate(null);
+		assertTrue(status.isOK());
+	}
+
+	public void testValidateNullForPrimitiveThrowsIllegalArgumentException()
+			throws Exception {
+		IValidator validator = doGetToPrimitiveValidator(Integer.class);
+		
+		if (validator == null) {
+			//return if a primitive validator does not exist (e.g. BigInteger, BigDecimal, etc.)
+			return;
+		}
+		
+		try {
+			doGetToPrimitiveValidator(Integer.class).validate(null);
+			
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testValidReturnsOK() throws Exception {
+		assertTrue(doGetToBoxedTypeValidator(Integer.class).validate(new Integer(1)).isOK());
+	}
+
+	public void testOutOfRangeReturnsError() throws Exception {
+		Number number = doGetOutOfRangeNumber();
+		
+		if (number == null) {
+			//return if there is no value out of range (e.g. BigInteger, BigDecimal, etc.)
+			return;
+		}
+		
+		IStatus status = doGetToBoxedTypeValidator(Integer.class).validate(number);
+		
+		assertEquals(IStatus.ERROR, status.getSeverity());
+		assertTrue(status.getMessage() != null);
+	}
+	
+	public void testValidateIncorrectTypeThrowsIllegalArgumentException() throws Exception {
+		try {
+			doGetToBoxedTypeValidator(Integer.class).validate("");
+			fail("exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToShortValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToShortValidatorTest.java
new file mode 100644
index 0000000..df91593
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToShortValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToShortValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToShortValidatorTest extends NumberToNumberValidatorTestHarness {
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return new Integer(Short.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToShortConverter converter = new NumberToShortConverter(NumberFormat.getInstance(),
+				Integer.class, false);
+		return new NumberToShortValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		NumberToShortConverter converter = new NumberToShortConverter(NumberFormat.getInstance(),
+				Integer.class, true);
+		return new NumberToShortValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToUnboundedNumberValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToUnboundedNumberValidatorTest.java
new file mode 100644
index 0000000..da5fa74
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/NumberToUnboundedNumberValidatorTest.java
@@ -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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToNumberValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToUnboundedNumberValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class NumberToUnboundedNumberValidatorTest extends
+		NumberToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetOutOfRangeNumber()
+	 */
+	protected Number doGetOutOfRangeNumber() {
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToBoxedTypeValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToBoxedTypeValidator(Class fromType) {
+		NumberToBigIntegerConverter converter = new NumberToBigIntegerConverter(NumberFormat.getInstance(), fromType);
+		return new NumberToUnboundedNumberValidator(converter);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.NumberToNumberValidatorTestHarness#doGetToPrimitiveValidator(java.lang.Class)
+	 */
+	protected NumberToNumberValidator doGetToPrimitiveValidator(Class fromType) {
+		return null;  // primitive BigInteger does not exist
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToByteValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToByteValidatorTest.java
new file mode 100644
index 0000000..7e33bc2
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToByteValidatorTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.conversion.StringToByteConverter;
+import org.eclipse.core.internal.databinding.validation.StringToByteValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToByteValidatorTest extends
+		StringToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInRangeNumber()
+	 */
+	protected Number getInRangeNumber() {
+		return new Byte(Byte.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInvalidString()
+	 */
+	protected String getInvalidString() {
+		return "1.1";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getOutOfRangeNumber()
+	 */
+	protected Number getOutOfRangeNumber() {
+		return new Integer(Byte.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupNumberFormat()
+	 */
+	protected NumberFormat setupNumberFormat() {
+		return NumberFormat.getIntegerInstance();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupValidator(com.ibm.icu.text.NumberFormat)
+	 */
+	protected IValidator setupValidator(NumberFormat numberFormat) {
+		StringToByteConverter converter = StringToByteConverter.toByte(numberFormat, false);
+		return new StringToByteValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToCharacterValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToCharacterValidatorTest.java
new file mode 100644
index 0000000..677142b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToCharacterValidatorTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter;
+import org.eclipse.core.internal.databinding.validation.StringToCharacterValidator;
+
+/**
+ * @since 1.1
+ */
+public class StringToCharacterValidatorTest extends TestCase {
+
+	private StringToCharacterValidator validator;
+	private StringToCharacterValidator primitiveValidator;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		StringToCharacterConverter converter = StringToCharacterConverter
+				.toCharacter(false);
+		StringToCharacterConverter primitiveConverter = StringToCharacterConverter
+				.toCharacter(true);
+		validator = new StringToCharacterValidator(converter);
+		primitiveValidator = new StringToCharacterValidator(primitiveConverter);
+	}
+
+	public void testValidatesCharacter() throws Exception {
+		assertTrue(validator.validate("X").isOK());
+	}
+
+	public void testValidatesCharacterPrimitive() throws Exception {
+		assertTrue(primitiveValidator.validate("X").isOK());
+	}
+
+	public void testNullCharacterIsValid() throws Exception {
+		assertTrue(validator.validate(null).isOK());
+	}
+
+	public void testEmptyStringCharacterIsValid() throws Exception {
+		assertTrue(validator.validate("").isOK());
+	}
+
+	public void testNullCharacterIsInvalidForPrimitive() throws Exception {
+		assertFalse(primitiveValidator.validate(null).isOK());
+	}
+
+	public void testNonStringIsInvalid() throws Exception {
+		assertFalse(primitiveValidator.validate(new Integer(4)).isOK());
+	}
+
+	public void testLongerThanOneCharacterIsInvalid() throws Exception {
+		assertFalse(primitiveValidator.validate("XYZ").isOK());
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToDoubleValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToDoubleValidatorTest.java
new file mode 100644
index 0000000..71d6f3f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToDoubleValidatorTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import java.math.BigDecimal;
+
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.validation.StringToDoubleValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToDoubleValidatorTest extends
+		StringToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInRangeNumber()
+	 */
+	protected Number getInRangeNumber() {
+		return new Double(1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInvalidString()
+	 */
+	protected String getInvalidString() {
+		return "1a";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getOutOfRangeNumber()
+	 */
+	protected Number getOutOfRangeNumber() {
+		BigDecimal decimal = new BigDecimal(Double.MAX_VALUE);
+		return decimal.add(new BigDecimal(Double.MAX_VALUE));
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupNumberFormat()
+	 */
+	protected NumberFormat setupNumberFormat() {
+		return NumberFormat.getInstance();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupValidator(com.ibm.icu.text.NumberFormat)
+	 */
+	protected IValidator setupValidator(NumberFormat numberFormat) {
+		StringToNumberConverter converter = StringToNumberConverter.toDouble(numberFormat, false);
+		return new StringToDoubleValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToFloatValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToFloatValidatorTest.java
new file mode 100644
index 0000000..d7ec032
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToFloatValidatorTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.validation.StringToFloatValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToFloatValidatorTest extends
+		StringToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInRangeNumber()
+	 */
+	protected Number getInRangeNumber() {
+		return new Float(1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInvalidString()
+	 */
+	protected String getInvalidString() {
+		return "1a";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getOutOfRangeNumber()
+	 */
+	protected Number getOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupNumberFormat()
+	 */
+	protected NumberFormat setupNumberFormat() {
+		return NumberFormat.getInstance();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupValidator(com.ibm.icu.text.NumberFormat)
+	 */
+	protected IValidator setupValidator(NumberFormat numberFormat) {
+		StringToNumberConverter converter = StringToNumberConverter.toFloat(numberFormat, false);
+		return new StringToFloatValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToIntegerValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToIntegerValidatorTest.java
new file mode 100644
index 0000000..d854d0b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToIntegerValidatorTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToIntegerValidatorTest extends
+		StringToNumberValidatorTestHarness {
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupNumberFormat()
+	 */
+	protected NumberFormat setupNumberFormat() {
+		return NumberFormat.getIntegerInstance();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupValidator(com.ibm.icu.text.NumberFormat)
+	 */
+	protected IValidator setupValidator(NumberFormat numberFormat) {
+		StringToNumberConverter converter = StringToNumberConverter.toInteger(
+				numberFormat, false);
+		return new StringToIntegerValidator(converter);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInRangeNumber()
+	 */
+	protected Number getInRangeNumber() {
+		return new Integer(1);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInvalidString()
+	 */
+	protected String getInvalidString() {
+		return "1.1";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getOutOfRangeNumber()
+	 */
+	protected Number getOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToLongValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToLongValidatorTest.java
new file mode 100644
index 0000000..97f7647
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToLongValidatorTest.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.validation.NumberFormatConverter;
+import org.eclipse.core.internal.databinding.validation.StringToLongValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToLongValidatorTest extends StringToNumberValidatorTestHarness {
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInRangeNumber()
+	 */
+	protected Number getInRangeNumber() {
+		return new Long(1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInvalidString()
+	 */
+	protected String getInvalidString() {
+		return "1.1";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getOutOfRangeNumber()
+	 */
+	protected Number getOutOfRangeNumber() {
+		return new Double(Double.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupNumberFormat()
+	 */
+	protected NumberFormat setupNumberFormat() {
+		return NumberFormat.getIntegerInstance();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupValidator()
+	 */
+	protected IValidator setupValidator(NumberFormat numberFormat) {
+		NumberFormatConverter converter = StringToNumberConverter.toInteger(numberFormat, false);
+		return new StringToLongValidator(converter);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToNumberValidatorTestHarness.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToNumberValidatorTestHarness.java
new file mode 100644
index 0000000..9e62ec3
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToNumberValidatorTestHarness.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.runtime.IStatus;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * A test harness for testing string to number validators.
+ * 
+ * @since 1.1
+ */
+public abstract class StringToNumberValidatorTestHarness extends TestCase {
+	private NumberFormat numberFormat;
+	private IValidator validator;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		numberFormat = setupNumberFormat();
+		validator = setupValidator(numberFormat);
+	}
+	
+	/**
+	 * Invoked during setup to instantiate the number format.
+	 * 
+	 * @return number format
+	 */
+	protected abstract NumberFormat setupNumberFormat();
+	
+	/**
+	 * Invoked during setup to instantiate the validator.
+	 * 
+	 * @param numberFormat
+	 * @return validator
+	 */
+	protected abstract IValidator setupValidator(NumberFormat numberFormat);
+
+	/**
+	 * Returns a string value that will not parse.
+	 * 
+	 * @return string
+	 */
+	protected abstract String getInvalidString();
+
+	/**
+	 * Returns a number value that is out of range for the validator.
+	 * 
+	 * @return number
+	 */
+	protected abstract Number getOutOfRangeNumber();
+	
+	/**
+	 * Returns a number that is in range for the validator.
+	 * 
+	 * @return number
+	 */
+	protected abstract Number getInRangeNumber();
+	
+	public void testInvalidValueReturnsError() throws Exception {
+		IStatus status = validator.validate(getInvalidString());
+		assertEquals("error severify", IStatus.ERROR, status.getSeverity());
+		assertNotNull("message not null", status.getMessage());
+	}
+	
+	public void testOutOfRangeValueReturnsError() throws Exception {
+		String string = numberFormat.format(getOutOfRangeNumber());
+		IStatus status = validator.validate(string);
+		assertEquals(IStatus.ERROR, status.getSeverity());
+		assertNotNull(status.getMessage());
+	}
+
+	public void testValidateValidValue() throws Exception {
+		String string = numberFormat.format(getInRangeNumber());
+		assertTrue(validator.validate(string).isOK());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToShortValidatorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToShortValidatorTest.java
new file mode 100644
index 0000000..9de5b38
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/internal/databinding/validation/StringToShortValidatorTest.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.validation;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.internal.databinding.conversion.StringToShortConverter;
+import org.eclipse.core.internal.databinding.validation.StringToShortValidator;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.1
+ */
+public class StringToShortValidatorTest extends
+		StringToNumberValidatorTestHarness {
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInRangeNumber()
+	 */
+	protected Number getInRangeNumber() {
+		return new Short(Short.MAX_VALUE);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getInvalidString()
+	 */
+	protected String getInvalidString() {
+		return "1.1";
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#getOutOfRangeNumber()
+	 */
+	protected Number getOutOfRangeNumber() {
+		return new Integer(Short.MAX_VALUE + 1);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupNumberFormat()
+	 */
+	protected NumberFormat setupNumberFormat() {
+		return NumberFormat.getIntegerInstance();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.tests.internal.databinding.validation.StringToNumberValidatorTestHarness#setupValidator(com.ibm.icu.text.NumberFormat)
+	 */
+	protected IValidator setupValidator(NumberFormat numberFormat) {
+		StringToShortConverter converter = StringToShortConverter.toShort(numberFormat, false);
+		return new StringToShortValidator(converter);
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/AbstractDefaultRealmTestCase.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/AbstractDefaultRealmTestCase.java
new file mode 100644
index 0000000..c6fd4a9
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/AbstractDefaultRealmTestCase.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Base class that sets the default realm to be the SWT realm.
+ * 
+ * @since 3.3
+ */
+public class AbstractDefaultRealmTestCase extends TestCase {
+	private Realm previousRealm;
+
+	/**
+	 * Sets the default realm to be the realm for the default display.
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		previousRealm = Realm.getDefault();
+
+		Display display = Display.getCurrent() != null
+				&& !Display.getCurrent().isDisposed() ? Display.getCurrent()
+				: Display.getDefault();
+		RealmTester.setDefault(SWTObservables.getRealm(display));
+	}
+
+	/**
+	 * Removes the default realm.
+	 */
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		RealmTester.setDefault(previousRealm);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/AbstractSWTTestCase.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/AbstractSWTTestCase.java
new file mode 100644
index 0000000..6b786dc
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/AbstractSWTTestCase.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Ashley Cambrell - bug 198904
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding;
+
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Abstract test case that handles disposing of the Shell after each test.
+ * 
+ * @since 1.1
+ */
+public abstract class AbstractSWTTestCase extends AbstractDefaultRealmTestCase {
+	private Shell shell;
+	
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+	}
+	
+	/*
+	 * (non-Javadoc)
+	 *
+	 * @see org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase#tearDown()
+	 */
+	protected void tearDown() throws Exception {
+		if (shell != null && !shell.isDisposed()) {
+			shell.dispose();
+		}
+		super.tearDown();
+	}
+	
+	/**
+	 * Returns a Shell to be used in a test.
+	 * 
+	 * @return shell
+	 */
+	protected Shell getShell() {
+		if (shell == null || shell.isDisposed()) {
+			shell = new Shell();
+		}
+		
+		return shell;
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSetup.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSetup.java
new file mode 100644
index 0000000..7ef5f67
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSetup.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding;
+
+import java.util.Locale;
+
+import org.eclipse.core.databinding.util.ILogger;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.runtime.IStatus;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class BindingTestSetup extends TestSetup {
+
+	private Locale oldLocale;
+	private ILogger oldLogger;
+	private org.eclipse.jface.util.ILogger oldJFaceLogger;
+
+	public BindingTestSetup(Test test) {
+		super(test);
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		oldLocale = Locale.getDefault();
+		Locale.setDefault(Locale.US);
+		oldLogger = Policy.getLog();
+		Policy.setLog(new ILogger() {
+			public void log(IStatus status) {
+				// we are not expecting anything in the log while we test.
+				if (status.getException() != null) {
+					throw new RuntimeException(status.getException());
+				}
+				fail();
+			}
+		});
+		oldJFaceLogger = org.eclipse.jface.util.Policy.getLog();
+		org.eclipse.jface.util.Policy.setLog(new org.eclipse.jface.util.ILogger(){
+			public void log(IStatus status) {
+				// we are not expecting anything in the log while we test.
+				if (status.getException() != null) {
+					throw new RuntimeException(status.getException());
+				}
+				fail();
+			}
+		});
+	}
+
+	protected void tearDown() throws Exception {
+		Locale.setDefault(oldLocale);
+		Policy.setLog(oldLogger);
+		org.eclipse.jface.util.Policy.setLog(oldJFaceLogger);
+		super.tearDown();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSuite.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSuite.java
new file mode 100644
index 0000000..56f2d52
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSuite.java
@@ -0,0 +1,525 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 137877, 152543, 152540, 116920, 164247, 164653,
+ *                     159768, 170848, 147515
+ *     Bob Smith - bug 198880
+ *     Ashley Cambrell - bugs 198903, 198904
+ *     Matthew Hall - bugs 210115, 212468, 212223, 206839, 208858, 208322,
+ *                    212518, 215531, 221351, 184830, 213145, 218269, 239015,
+ *                    237703, 237718, 222289, 247394, 233306, 247647, 254524,
+ *                    246103, 249992, 256150, 256543, 262269, 175735, 262946,
+ *                    255734, 263693, 169876, 266038, 268336, 270461, 271720,
+ *                    283204, 281723, 283428
+ *     Ovidio Mallo - bugs 237163, 235195, 299619, 306611, 305367
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.tests.databinding.AggregateValidationStatusTest;
+import org.eclipse.core.tests.databinding.BindingTest;
+import org.eclipse.core.tests.databinding.DatabindingContextTest;
+import org.eclipse.core.tests.databinding.ListBindingTest;
+import org.eclipse.core.tests.databinding.ObservablesManagerTest;
+import org.eclipse.core.tests.databinding.UpdateListStrategyTest;
+import org.eclipse.core.tests.databinding.UpdateSetStrategyTest;
+import org.eclipse.core.tests.databinding.UpdateStrategyTest;
+import org.eclipse.core.tests.databinding.UpdateValueStrategyTest;
+import org.eclipse.core.tests.databinding.ValueBindingTest;
+import org.eclipse.core.tests.databinding.beans.AnonymousBeanValuePropertyTest;
+import org.eclipse.core.tests.databinding.beans.AnonymousPojoValuePropertyTest;
+import org.eclipse.core.tests.databinding.beans.BeanPropertiesTest;
+import org.eclipse.core.tests.databinding.beans.BeansObservablesTest;
+import org.eclipse.core.tests.databinding.beans.PojoObservablesTest;
+import org.eclipse.core.tests.databinding.beans.PojoPropertiesTest;
+import org.eclipse.core.tests.databinding.conversion.NumberToStringConverterTest;
+import org.eclipse.core.tests.databinding.conversion.StringToNumberConverterTest;
+import org.eclipse.core.tests.databinding.observable.AbstractObservableTest;
+import org.eclipse.core.tests.databinding.observable.ChangeSupportTest;
+import org.eclipse.core.tests.databinding.observable.DecoratingObservableTest;
+import org.eclipse.core.tests.databinding.observable.DiffsTest;
+import org.eclipse.core.tests.databinding.observable.Diffs_ListDiffTests;
+import org.eclipse.core.tests.databinding.observable.ObservableTrackerTest;
+import org.eclipse.core.tests.databinding.observable.ObservablesTest;
+import org.eclipse.core.tests.databinding.observable.RealmTest;
+import org.eclipse.core.tests.databinding.observable.list.AbstractObservableListTest;
+import org.eclipse.core.tests.databinding.observable.list.ComputedListTest;
+import org.eclipse.core.tests.databinding.observable.list.DecoratingObservableListTest;
+import org.eclipse.core.tests.databinding.observable.list.ListDiffTest;
+import org.eclipse.core.tests.databinding.observable.list.ListDiffVisitorTest;
+import org.eclipse.core.tests.databinding.observable.list.MultiListTest;
+import org.eclipse.core.tests.databinding.observable.list.ObservableListTest;
+import org.eclipse.core.tests.databinding.observable.list.WritableListTest;
+import org.eclipse.core.tests.databinding.observable.map.AbstractObservableMapTest;
+import org.eclipse.core.tests.databinding.observable.map.BidiObservableMapTest;
+import org.eclipse.core.tests.databinding.observable.map.CompositeMapTest;
+import org.eclipse.core.tests.databinding.observable.map.ComputedObservableMapTest;
+import org.eclipse.core.tests.databinding.observable.map.ObservableMapTest;
+import org.eclipse.core.tests.databinding.observable.map.WritableMapTest;
+import org.eclipse.core.tests.databinding.observable.set.AbstractObservableSetTest;
+import org.eclipse.core.tests.databinding.observable.set.ComputedSetTest;
+import org.eclipse.core.tests.databinding.observable.set.DecoratingObservableSetTest;
+import org.eclipse.core.tests.databinding.observable.set.ObservableSetTest;
+import org.eclipse.core.tests.databinding.observable.set.UnionSetTest;
+import org.eclipse.core.tests.databinding.observable.set.WritableSetTest;
+import org.eclipse.core.tests.databinding.observable.value.AbstractObservableValueTest;
+import org.eclipse.core.tests.databinding.observable.value.AbstractVetoableValueTest;
+import org.eclipse.core.tests.databinding.observable.value.ComputedValueTest;
+import org.eclipse.core.tests.databinding.observable.value.DateAndTimeObservableValueTest;
+import org.eclipse.core.tests.databinding.observable.value.DecoratingObservableValueTest;
+import org.eclipse.core.tests.databinding.observable.value.DuplexingObservableValueTest;
+import org.eclipse.core.tests.databinding.observable.value.SelectObservableValueTest;
+import org.eclipse.core.tests.databinding.observable.value.WritableValueTest;
+import org.eclipse.core.tests.databinding.util.PolicyTest;
+import org.eclipse.core.tests.databinding.validation.MultiValidatorTest;
+import org.eclipse.core.tests.databinding.validation.ValidationStatusTest;
+import org.eclipse.core.tests.internal.databinding.BindingMessagesTest;
+import org.eclipse.core.tests.internal.databinding.BindingStatusTest;
+import org.eclipse.core.tests.internal.databinding.ConverterValuePropertyTest;
+import org.eclipse.core.tests.internal.databinding.IdentityMapTest;
+import org.eclipse.core.tests.internal.databinding.IdentitySetTest;
+import org.eclipse.core.tests.internal.databinding.QueueTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanObservableListDecoratorTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanObservableSetDecoratorTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanObservableValueDecoratorTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanPropertyHelperTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanPropertyListenerSupportTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanPropertyListenerTest;
+import org.eclipse.core.tests.internal.databinding.beans.BeanValuePropertyTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableArrayBasedListTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableArrayBasedSetTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableListTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableSetTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.beans.JavaBeanPropertyObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.conversion.DateConversionSupportTest;
+import org.eclipse.core.tests.internal.databinding.conversion.IdentityConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.IntegerToStringConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToBigDecimalTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToBigIntegerConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToByteConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToDoubleConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToFloatConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToIntegerConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToLongConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.NumberToShortConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.ObjectToPrimitiveValidatorTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StatusToStringConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToBooleanConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToByteConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToCharacterConverterTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserByteTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserDoubleTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserFloatTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserIntegerTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserLongTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserShortTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToNumberParserTest;
+import org.eclipse.core.tests.internal.databinding.conversion.StringToShortConverterTest;
+import org.eclipse.core.tests.internal.databinding.observable.ConstantObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.DelayedObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.EmptyObservableListTest;
+import org.eclipse.core.tests.internal.databinding.observable.EmptyObservableSetTest;
+import org.eclipse.core.tests.internal.databinding.observable.IdentityObservableSetTest;
+import org.eclipse.core.tests.internal.databinding.observable.MapEntryObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.StalenessObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.UnmodifiableObservableListTest;
+import org.eclipse.core.tests.internal.databinding.observable.UnmodifiableObservableSetTest;
+import org.eclipse.core.tests.internal.databinding.observable.UnmodifiableObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.ValidatedObservableListTest;
+import org.eclipse.core.tests.internal.databinding.observable.ValidatedObservableSetTest;
+import org.eclipse.core.tests.internal.databinding.observable.ValidatedObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableListTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableSetTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.ListDetailValueObservableListTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.MapDetailValueObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.SetDetailValueObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.property.value.ListSimpleValueObservableListTest;
+import org.eclipse.core.tests.internal.databinding.property.value.MapSimpleValueObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.property.value.SetSimpleValueObservableMapTest;
+import org.eclipse.core.tests.internal.databinding.validation.AbstractStringToNumberValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToByteValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToDoubleValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToFloatValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToIntegerValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToLongValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToShortValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.NumberToUnboundedNumberValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToByteValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToCharacterValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToDoubleValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToFloatValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToIntegerValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToLongValidatorTest;
+import org.eclipse.core.tests.internal.databinding.validation.StringToShortValidatorTest;
+import org.eclipse.jface.tests.databinding.preference.PreferencePageSupportTest;
+import org.eclipse.jface.tests.databinding.scenarios.BindingScenariosTestSuite;
+import org.eclipse.jface.tests.databinding.swt.SWTObservablesTest;
+import org.eclipse.jface.tests.databinding.swt.WidgetObservableThreadTest;
+import org.eclipse.jface.tests.databinding.swt.WidgetPropertiesTest;
+import org.eclipse.jface.tests.databinding.viewers.ObservableListContentProviderTest;
+import org.eclipse.jface.tests.databinding.viewers.ObservableListTreeContentProviderTest;
+import org.eclipse.jface.tests.databinding.viewers.ObservableMapLabelProviderTest;
+import org.eclipse.jface.tests.databinding.viewers.ObservableSetContentProviderTest;
+import org.eclipse.jface.tests.databinding.viewers.ObservableSetTreeContentProviderTest;
+import org.eclipse.jface.tests.databinding.viewers.ObservableValueEditingSupportTest;
+import org.eclipse.jface.tests.databinding.viewers.ViewerSupportTest;
+import org.eclipse.jface.tests.databinding.viewers.ViewersObservablesTest;
+import org.eclipse.jface.tests.databinding.wizard.WizardPageSupportTest;
+import org.eclipse.jface.tests.examples.databinding.mask.internal.EditMaskLexerAndTokenTest;
+import org.eclipse.jface.tests.examples.databinding.mask.internal.EditMaskParserTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ButtonObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.CComboObservableValueSelectionTest;
+import org.eclipse.jface.tests.internal.databinding.swt.CComboObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.CComboObservableValueTextTest;
+import org.eclipse.jface.tests.internal.databinding.swt.CComboSingleSelectionObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.CLabelObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ComboObservableValueSelectionTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ComboObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ComboObservableValueTextTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ComboSingleSelectionObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ControlObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.DateTimeCalendarObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.DateTimeDateObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.DateTimeSelectionPropertyTest;
+import org.eclipse.jface.tests.internal.databinding.swt.DateTimeTimeObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.LabelObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ListSingleSelectionObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.SWTDelayedObservableValueDecoratorTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ScaleObservableValueMaxTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ScaleObservableValueMinTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ScaleObservableValueSelectionTest;
+import org.eclipse.jface.tests.internal.databinding.swt.ShellObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.SpinnerObservableValueMaxTest;
+import org.eclipse.jface.tests.internal.databinding.swt.SpinnerObservableValueMinTest;
+import org.eclipse.jface.tests.internal.databinding.swt.SpinnerObservableValueSelectionTest;
+import org.eclipse.jface.tests.internal.databinding.swt.SpinnerObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.StyledTextObservableValueDefaultSelectionTest;
+import org.eclipse.jface.tests.internal.databinding.swt.StyledTextObservableValueFocusOutTest;
+import org.eclipse.jface.tests.internal.databinding.swt.StyledTextObservableValueModifyTest;
+import org.eclipse.jface.tests.internal.databinding.swt.StyledTextObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TableObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TableSingleSelectionObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TextEditableObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TextObservableValueDefaultSelectionTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TextObservableValueFocusOutTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TextObservableValueModifyTest;
+import org.eclipse.jface.tests.internal.databinding.swt.TextObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.CheckableCheckedElementsObservableSetTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ObservableCollectionContentProviderTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ObservableCollectionTreeContentProviderTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ObservableViewerElementSetTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.SelectionProviderMultiSelectionObservableListTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.SelectionProviderSingleSelectionObservableValueTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ViewerElementMapTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ViewerElementSetTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ViewerElementWrapperTest;
+import org.eclipse.jface.tests.internal.databinding.viewers.ViewerInputObservableValueTest;
+
+public class BindingTestSuite extends TestSuite {
+
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(suite());
+	}
+
+	public static Test suite() {
+		return new BindingTestSetup(new BindingTestSuite());
+	}
+
+	public BindingTestSuite() {
+
+		// org.eclipse.core.tests.databinding
+		addTestSuite(AggregateValidationStatusTest.class);
+		addTestSuite(BindingTest.class);
+		addTestSuite(DatabindingContextTest.class);
+		addTestSuite(ListBindingTest.class);
+		addTestSuite(UpdateStrategyTest.class);
+		addTestSuite(UpdateListStrategyTest.class);
+		addTestSuite(UpdateSetStrategyTest.class);
+		addTestSuite(UpdateValueStrategyTest.class);
+		addTestSuite(ValueBindingTest.class);
+		addTestSuite(ObservablesManagerTest.class);
+
+		// org.eclipse.core.tests.databinding.util
+		addTestSuite(PolicyTest.class);
+
+		// org.eclipse.core.tests.databinding.beans
+		addTestSuite(AnonymousBeanValuePropertyTest.class);
+		addTestSuite(AnonymousPojoValuePropertyTest.class);
+		addTestSuite(BeanPropertiesTest.class);
+		addTestSuite(BeansObservablesTest.class);
+		addTestSuite(PojoObservablesTest.class);
+		addTestSuite(PojoPropertiesTest.class);
+
+		// org.eclipse.core.tests.databinding.conversion
+		addTestSuite(NumberToStringConverterTest.class);
+		addTestSuite(StringToNumberConverterTest.class);
+
+		// org.eclipse.core.tests.databinding.observable
+		addTest(AbstractObservableTest.suite());
+		addTestSuite(ChangeSupportTest.class);
+		addTestSuite(DecoratingObservableTest.class);
+		addTestSuite(Diffs_ListDiffTests.class);
+		addTestSuite(DiffsTest.class);
+		addTestSuite(ObservablesTest.class);
+		addTestSuite(ObservableTrackerTest.class);
+		addTestSuite(RealmTest.class);
+
+		// org.eclipse.core.tests.databinding.observable.list
+		addTest(AbstractObservableListTest.suite());
+		addTest(ComputedListTest.suite());
+		addTest(DecoratingObservableListTest.suite());
+		addTestSuite(ListDiffTest.class);
+		addTestSuite(ListDiffVisitorTest.class);
+		addTest(MultiListTest.suite());
+		addTest(ObservableListTest.suite());
+		addTest(WritableListTest.suite());
+
+		// org.eclipse.core.tests.databinding.observable.map
+		addTestSuite(AbstractObservableMapTest.class);
+		addTestSuite(BidiObservableMapTest.class);
+		addTestSuite(ObservableMapTest.class);
+		addTestSuite(WritableMapTest.class);
+		addTestSuite(CompositeMapTest.class);
+		addTestSuite(ComputedObservableMapTest.class);
+
+		// org.eclipse.core.tests.databinding.observable.set
+		addTest(AbstractObservableSetTest.suite());
+		addTest(ComputedSetTest.suite());
+		addTest(DecoratingObservableSetTest.suite());
+		addTest(ObservableSetTest.suite());
+		addTest(UnionSetTest.suite());
+		addTest(WritableSetTest.suite());
+
+		// org.eclipse.core.tests.databinding.observable.value
+		addTestSuite(AbstractObservableValueTest.class);
+		addTestSuite(AbstractVetoableValueTest.class);
+		addTestSuite(ComputedValueTest.class);
+		addTestSuite(DateAndTimeObservableValueTest.class);
+		addTest(DecoratingObservableValueTest.suite());
+		addTestSuite(DuplexingObservableValueTest.class);
+		addTest(SelectObservableValueTest.suite());
+		addTest(WritableValueTest.suite());
+
+		// org.eclipse.core.tests.databinding.validation
+		addTestSuite(MultiValidatorTest.class);
+		addTestSuite(ValidationStatusTest.class);
+
+		// org.eclipse.core.tests.internal.databinding
+		addTestSuite(BindingMessagesTest.class);
+		addTestSuite(BindingStatusTest.class);
+		addTestSuite(ConverterValuePropertyTest.class);
+		addTestSuite(IdentityMapTest.class);
+		addTestSuite(IdentitySetTest.class);
+		addTestSuite(QueueTest.class);
+
+		// org.eclipse.core.tests.internal.databinding.conversion
+		addTestSuite(DateConversionSupportTest.class);
+		addTestSuite(IdentityConverterTest.class);
+		addTestSuite(IntegerToStringConverterTest.class);
+		addTestSuite(NumberToBigDecimalTest.class);
+		addTestSuite(NumberToBigIntegerConverterTest.class);
+		addTestSuite(NumberToByteConverterTest.class);
+		addTestSuite(NumberToDoubleConverterTest.class);
+		addTestSuite(NumberToFloatConverterTest.class);
+		addTestSuite(NumberToIntegerConverterTest.class);
+		addTestSuite(NumberToLongConverterTest.class);
+		addTestSuite(NumberToShortConverterTest.class);
+		addTestSuite(ObjectToPrimitiveValidatorTest.class);
+		addTestSuite(StatusToStringConverterTest.class);
+		addTestSuite(StringToBooleanConverterTest.class);
+		addTestSuite(StringToByteConverterTest.class);
+		addTestSuite(StringToCharacterConverterTest.class);
+		addTestSuite(StringToNumberParserByteTest.class);
+		addTestSuite(StringToNumberParserDoubleTest.class);
+		addTestSuite(StringToNumberParserFloatTest.class);
+		addTestSuite(StringToNumberParserIntegerTest.class);
+		addTestSuite(StringToNumberParserLongTest.class);
+		addTestSuite(StringToNumberParserShortTest.class);
+		addTestSuite(StringToNumberParserTest.class);
+		addTestSuite(StringToShortConverterTest.class);
+
+		// org.eclipse.core.tests.internal.databinding.beans
+		addTest(BeanObservableListDecoratorTest.suite());
+		addTestSuite(BeanObservableSetDecoratorTest.class);
+		addTestSuite(BeanObservableValueDecoratorTest.class);
+		addTestSuite(BeanObservableListDecoratorTest.class);
+		addTestSuite(BeanValuePropertyTest.class);
+		addTest(JavaBeanObservableArrayBasedListTest.suite());
+		addTest(JavaBeanObservableArrayBasedSetTest.suite());
+		addTest(JavaBeanObservableListTest.suite());
+		addTest(JavaBeanObservableMapTest.suite());
+		addTest(JavaBeanObservableSetTest.suite());
+		addTest(JavaBeanObservableValueTest.suite());
+		addTestSuite(JavaBeanPropertyObservableMapTest.class);
+		addTestSuite(BeanPropertyHelperTest.class);
+		addTestSuite(BeanPropertyListenerSupportTest.class);
+		addTestSuite(BeanPropertyListenerTest.class);
+
+		// org.eclipse.core.tests.internal.databinding.observable
+		addTest(ConstantObservableValueTest.suite());
+		addTest(DelayedObservableValueTest.suite());
+		addTest(EmptyObservableListTest.suite());
+		addTest(EmptyObservableSetTest.suite());
+		addTest(IdentityObservableSetTest.suite());
+		addTest(MapEntryObservableValueTest.suite());
+		addTest(StalenessObservableValueTest.suite());
+		addTest(UnmodifiableObservableValueTest.suite());
+		addTest(UnmodifiableObservableListTest.suite());
+		addTest(UnmodifiableObservableSetTest.suite());
+		addTest(ValidatedObservableValueTest.suite());
+		addTest(ValidatedObservableListTest.suite());
+		addTest(ValidatedObservableSetTest.suite());
+		// addTest(ValidatedObservableMapTest.suite());
+
+		// org.eclipse.core.tests.internal.databinding.observable.masterdetail
+		addTest(DetailObservableListTest.suite());
+		addTestSuite(DetailObservableMapTest.class);
+		addTest(DetailObservableSetTest.suite());
+		addTest(DetailObservableValueTest.suite());
+		addTest(ListDetailValueObservableListTest.suite());
+		addTest(MapDetailValueObservableMapTest.suite());
+		addTest(SetDetailValueObservableMapTest.suite());
+
+		// org.eclipse.core.tests.internal.databinding.property.value
+		addTestSuite(MapSimpleValueObservableMapTest.class);
+		addTestSuite(SetSimpleValueObservableMapTest.class);
+		addTestSuite(ListSimpleValueObservableListTest.class);
+
+		// org.eclipse.core.tests.internal.databinding.validation
+		addTestSuite(AbstractStringToNumberValidatorTest.class);
+		addTestSuite(NumberToByteValidatorTest.class);
+		addTestSuite(NumberToDoubleValidatorTest.class);
+		addTestSuite(NumberToFloatValidatorTest.class);
+		addTestSuite(NumberToIntegerValidatorTest.class);
+		addTestSuite(NumberToLongValidatorTest.class);
+		addTestSuite(NumberToShortValidatorTest.class);
+		addTestSuite(NumberToUnboundedNumberValidatorTest.class);
+		addTestSuite(StringToByteValidatorTest.class);
+		addTestSuite(StringToCharacterValidatorTest.class);
+		addTestSuite(StringToDoubleValidatorTest.class);
+		addTestSuite(StringToFloatValidatorTest.class);
+		addTestSuite(StringToIntegerValidatorTest.class);
+		addTestSuite(StringToLongValidatorTest.class);
+		addTestSuite(StringToShortValidatorTest.class);
+
+		// org.eclipse.jface.tests.databinding.scenarios
+		addTest(BindingScenariosTestSuite.suite());
+		// The files in this package are in the above test suite
+
+		// org.eclipse.jface.tests.databinding.swt
+		addTestSuite(SWTObservablesTest.class);
+		addTestSuite(WidgetPropertiesTest.class);
+		addTestSuite(WidgetObservableThreadTest.class);
+
+		// org.eclipse.jface.tests.databinding.preference
+		addTestSuite(PreferencePageSupportTest.class);
+
+		// org.eclipse.jface.tests.databinding.viewers
+		addTestSuite(ObservableListContentProviderTest.class);
+		addTestSuite(ObservableListTreeContentProviderTest.class);
+		addTestSuite(ObservableMapLabelProviderTest.class);
+		addTestSuite(ObservableSetContentProviderTest.class);
+		addTestSuite(ObservableSetTreeContentProviderTest.class);
+		addTestSuite(ObservableValueEditingSupportTest.class);
+		addTestSuite(ViewersObservablesTest.class);
+		addTestSuite(ViewerSupportTest.class);
+
+		// org.eclipse.jface.tests.databinding.wizard
+		addTestSuite(WizardPageSupportTest.class);
+
+		// org.eclipse.jface.tests.example.databinding.mask.internal
+		addTestSuite(EditMaskLexerAndTokenTest.class);
+		addTestSuite(EditMaskParserTest.class);
+
+		// org.eclipse.jface.tests.internal.databinding.swt
+		addTest(ButtonObservableValueTest.suite());
+		addTestSuite(CComboObservableValueTest.class);
+		addTest(CComboObservableValueSelectionTest.suite());
+		addTest(CComboObservableValueTextTest.suite());
+		addTestSuite(CComboSingleSelectionObservableValueTest.class);
+		addTest(CComboSingleSelectionObservableValueTest.suite());
+		addTest(CLabelObservableValueTest.suite());
+		addTestSuite(ComboObservableValueTest.class);
+		addTest(ComboObservableValueSelectionTest.suite());
+		addTest(ComboObservableValueTextTest.suite());
+		addTestSuite(ComboSingleSelectionObservableValueTest.class);
+		addTestSuite(DateTimeCalendarObservableValueTest.class);
+		addTestSuite(DateTimeDateObservableValueTest.class);
+		addTestSuite(DateTimeSelectionPropertyTest.class);
+		addTestSuite(DateTimeTimeObservableValueTest.class);
+		addTest(SWTDelayedObservableValueDecoratorTest.suite());
+
+		addTestSuite(ControlObservableValueTest.class);
+		addTest(LabelObservableValueTest.suite());
+		addTestSuite(ListSingleSelectionObservableValueTest.class);
+		addTest(ScaleObservableValueMinTest.suite());
+		addTest(ScaleObservableValueMaxTest.suite());
+		addTest(ScaleObservableValueSelectionTest.suite());
+
+		addTest(ShellObservableValueTest.suite());
+
+		addTestSuite(SpinnerObservableValueTest.class);
+		addTest(SpinnerObservableValueMinTest.suite());
+		addTest(SpinnerObservableValueMaxTest.suite());
+		addTest(SpinnerObservableValueSelectionTest.suite());
+
+		addTestSuite(TableObservableValueTest.class);
+		addTest(TableSingleSelectionObservableValueTest.suite());
+		addTest(TextEditableObservableValueTest.suite());
+		addTest(TextObservableValueDefaultSelectionTest.suite());
+		addTest(TextObservableValueFocusOutTest.suite());
+		addTest(TextObservableValueModifyTest.suite());
+		addTestSuite(TextObservableValueTest.class);
+		addTest(StyledTextObservableValueDefaultSelectionTest.suite());
+		addTest(StyledTextObservableValueFocusOutTest.suite());
+		addTest(StyledTextObservableValueModifyTest.suite());
+		addTestSuite(StyledTextObservableValueTest.class);
+
+		// org.eclipse.jface.tests.internal.databinding.viewers
+		addTestSuite(CheckableCheckedElementsObservableSetTest.class);
+		addTest(ObservableViewerElementSetTest.suite());
+		addTestSuite(ObservableCollectionContentProviderTest.class);
+		addTestSuite(ObservableCollectionTreeContentProviderTest.class);
+		addTestSuite(SelectionProviderMultiSelectionObservableListTest.class);
+		addTestSuite(SelectionProviderSingleSelectionObservableValueTest.class);
+		addTestSuite(ViewerElementMapTest.class);
+		addTestSuite(ViewerElementSetTest.class);
+		addTestSuite(ViewerElementWrapperTest.class);
+		addTest(ViewerInputObservableValueTest.suite());
+	}
+
+	/**
+	 * @param testCase
+	 *            TODO
+	 * @return true if the given test is temporarily disabled
+	 */
+	public static boolean failingTestsDisabled(TestCase testCase) {
+		System.out.println("Ignoring disabled test: "
+				+ testCase.getClass().getName() + "." + testCase.getName());
+		return true;
+	}
+
+	/**
+	 * @param testSuite
+	 *            TODO
+	 * @return true if the given test is temporarily disabled
+	 */
+	public static boolean failingTestsDisabled(TestSuite testSuite) {
+		System.out.println("Ignoring disabled test: "
+				+ testSuite.getClass().getName() + "." + testSuite.getName());
+		return true;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/preference/PreferencePageSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/preference/PreferencePageSupportTest.java
new file mode 100644
index 0000000..c593cc0
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/preference/PreferencePageSupportTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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 300232)
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.preference;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.jface.databinding.preference.PreferencePageSupport;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class PreferencePageSupportTest extends AbstractSWTTestCase {
+	private PreferencePageWithSupport page;
+
+	// private PreferenceDialog dialog;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		page = new PreferencePageWithSupport();
+		page.setControl(getShell());
+	}
+
+	public void testCreateAndDestroySupport() {
+		page.createContents(getShell());
+	}
+
+	public class PreferencePageWithSupport extends PreferencePage {
+		public void setControl(Control newControl) {
+			super.setControl(newControl);
+		}
+
+		public Control createContents(Composite parent) {
+			Composite contents = new Composite(parent, SWT.NONE);
+
+			PreferencePageSupport.create(this, new DataBindingContext());
+
+			return contents;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/AutomationUtil.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/AutomationUtil.java
new file mode 100644
index 0000000..9e881ae
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/AutomationUtil.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+
+/**
+ * COPIED FROM org.eclipse.ui.tests
+ * <code>AutomationUtil</code> contains utility methods to mimic key events.
+ * Mouse event methods can be added if needed to complete this class.
+ */
+
+public class AutomationUtil {
+
+	/**
+	 * Method to mimic a key code event on a display.
+	 * 
+	 * @param display
+	 *            The display.
+	 * @param eventType
+	 *            The event type.
+	 * @param keyCode
+	 *            The key code.
+	 */
+	public static void performKeyCodeEvent(Display display, int eventType,
+			int keyCode) {
+		Event event = new Event();
+		event.type = eventType;
+		event.keyCode = keyCode;
+		display.post(event);
+	}
+
+	/**
+	 * Method to mimic a character event on a display.
+	 * 
+	 * @param display
+	 *            The display.
+	 * @param eventType
+	 *            The event type.
+	 * @param character
+	 *            The character to mimic.
+	 */
+	public static void performCharacterEvent(Display display, int eventType,
+			char character) {
+		Event event = new Event();
+		event.type = eventType;
+		event.character = character;
+		display.post(event);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/BindingScenariosTestSuite.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/BindingScenariosTestSuite.java
new file mode 100644
index 0000000..93fb3cb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/BindingScenariosTestSuite.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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
+ *     Brad Reynolds - bug 116920
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * To run this test suite, right-click and select "Run As JUnit Plug-in Test".
+ * This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run. You can also run this class as an SWT
+ * application.
+ */
+public class BindingScenariosTestSuite extends TestSuite {
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    private static Display display;
+
+    private static Shell shell;
+
+    public static Test suite() {
+        return new TestSetup(new BindingScenariosTestSuite()) {
+            protected void setUp() throws Exception {
+                Display d = Display.getDefault();
+                shell = new Shell(d, SWT.SHELL_TRIM);
+                shell.setLayout(new FillLayout());
+            }
+
+            protected void tearDown() throws Exception {
+                shell.close();
+                shell.dispose();
+                if (display != null) {
+                    display.dispose();
+                }
+            }
+        };
+    }
+
+    public BindingScenariosTestSuite() {
+        addTestSuite(ButtonControlScenario.class);
+        addTestSuite(ComboScenarios.class);
+        addTestSuite(ComboUpdatingTest.class);
+        addTestSuite(ComboViewerScenario.class);
+        addTestSuite(CustomConverterScenarios.class);
+        addTestSuite(CustomScenarios.class);
+        addTestSuite(ListViewerScenario.class);
+        addTestSuite(MasterDetailScenarios.class);
+        addTestSuite(NewTableScenarios.class);
+        addTestSuite(NPETestScenario.class);
+        addTestSuite(PropertyScenarios.class);
+        addTestSuite(SpinnerControlScenario.class);
+        addTestSuite(TableScenarios.class);
+        addTestSuite(TextControlScenario.class);
+    }
+
+    public static Shell getShell() {
+        return shell;
+    }
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ButtonControlScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ButtonControlScenario.java
new file mode 100644
index 0000000..8aebef4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ButtonControlScenario.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class ButtonControlScenario extends ScenariosTestCase {
+
+    private Adventure adventure;
+
+    private Button button;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        // do any setup work here
+        button = new Button(getComposite(), SWT.CHECK);
+        adventure = SampleData.WINTER_HOLIDAY;
+    }
+
+    protected void tearDown() throws Exception {
+        button.dispose();
+        super.tearDown();
+    }
+
+    public void testScenario01() {
+        // Bind the button's selection to the adventure "isPetsAllowed"
+        getDbc().bindValue(SWTObservables.observeSelection(button),
+                BeansObservables.observeValue(adventure, "petsAllowed"));
+
+        // Check the model and GUI are in the same state
+        assertEquals(button.getSelection(), adventure.isPetsAllowed());
+        // Change the model and check the GUI is updated
+        boolean newBoolean = !adventure.isPetsAllowed();
+        adventure.setPetsAllowed(newBoolean);
+        assertEquals(newBoolean, adventure.isPetsAllowed());
+        assertEquals(button.getSelection(), newBoolean);
+        // Change the GUI and check the model
+        newBoolean = !newBoolean;
+        button.setSelection(newBoolean);
+        button.notifyListeners(SWT.Selection, null);
+        assertEquals(newBoolean, adventure.isPetsAllowed());
+        newBoolean = !newBoolean;
+        final boolean finalNewBoolean = newBoolean;
+        adventure.setPetsAllowed(finalNewBoolean);
+        spinEventLoop(0);
+        assertEquals(newBoolean, button.getSelection());
+
+    }
+
+    public void testScenario02() {
+        // Test with an SWT.Toggle button
+        button.dispose();
+        button = new Button(getComposite(), SWT.TOGGLE);
+        // Bind the button's selection to the adventure "isPetsAllowed"
+        getDbc().bindValue(SWTObservables.observeSelection(button),
+                BeansObservables.observeValue(adventure, "petsAllowed"));
+
+        // Check the model and GUI are in the same state
+        assertEquals(button.getSelection(), adventure.isPetsAllowed());
+        // Change the model and check the GUI is updated
+        boolean newBoolean = !adventure.isPetsAllowed();
+        adventure.setPetsAllowed(newBoolean);
+        assertEquals(newBoolean, adventure.isPetsAllowed());
+        assertEquals(button.getSelection(), newBoolean);
+        // Change the GUI and check the model
+        newBoolean = !newBoolean;
+        button.setSelection(newBoolean);
+        button.notifyListeners(SWT.Selection, null);
+        assertEquals(newBoolean, adventure.isPetsAllowed());
+    }
+
+    public void testScenario03() {
+        // Test with an SWT.Radio button
+        button.dispose();
+        button = new Button(getComposite(), SWT.RADIO);
+
+        // Bind the button's selection to the adventure "isPetsAllowed"
+        getDbc().bindValue(SWTObservables.observeSelection(button),
+                BeansObservables.observeValue(adventure, "petsAllowed"));
+
+        // Check the model and GUI are in the same state
+        assertEquals(button.getSelection(), adventure.isPetsAllowed());
+        // Change the model and check the GUI is updated
+        boolean newBoolean = !adventure.isPetsAllowed();
+        adventure.setPetsAllowed(newBoolean);
+        assertEquals(newBoolean, adventure.isPetsAllowed());
+        assertEquals(button.getSelection(), newBoolean);
+        // Change the GUI and check the model
+        newBoolean = !newBoolean;
+        button.setSelection(newBoolean);
+        button.notifyListeners(SWT.Selection, null);
+        assertEquals(newBoolean, adventure.isPetsAllowed());
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboScenarios.java
new file mode 100644
index 0000000..1090492
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboScenarios.java
@@ -0,0 +1,531 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 116920, 160000
+ *     Matthew Hall - bugs 260329, 260337
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+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.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.examples.databinding.model.Account;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.Catalog;
+import org.eclipse.jface.examples.databinding.model.Lodging;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Combo;
+
+public class ComboScenarios extends ScenariosTestCase {
+
+	protected ComboViewer cviewer = null;
+
+	protected Combo combo = null;
+
+	protected Catalog catalog = null;
+
+	ILabelProvider lodgingLabelProvider = new LabelProvider() {
+		public String getText(Object element) {
+			return ((Lodging) element).getName();
+		}
+	};
+
+	ILabelProvider accountLabelProvider = new LabelProvider() {
+		public String getText(Object element) {
+			return ((Account) element).getCountry();
+		}
+	};
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		getComposite().setLayout(new FillLayout());
+
+		combo = new Combo(getComposite(), SWT.READ_ONLY | SWT.DROP_DOWN);
+		cviewer = new ComboViewer(combo);
+
+		catalog = SampleData.CATALOG_2005; // Lodging source
+	}
+
+	protected void tearDown() throws Exception {
+		combo.dispose();
+		combo = null;
+		cviewer = null;
+		super.tearDown();
+	}
+
+	protected Object getViewerSelection() {
+		return ((IStructuredSelection) cviewer.getSelection())
+				.getFirstElement();
+	}
+
+	/**
+	 * @return the ComboViewer's domain object list
+	 */
+	protected List getViewerContent(ComboViewer cviewer) {
+		Object[] elements = ((IStructuredContentProvider) cviewer
+				.getContentProvider()).getElements(null);
+		if (elements != null)
+			return Arrays.asList(elements);
+		return null;
+	}
+
+	/**
+	 * 
+	 * @return the combo's items (String[]), which is the same thing as the
+	 *         Viewer's labels
+	 * 
+	 */
+	protected List getComboContent() {
+		String[] elements = combo.getItems();
+		if (elements != null)
+			return Arrays.asList(elements);
+		return null;
+	}
+
+	protected List getColumn(Object[] list, String feature) {
+		List result = new ArrayList();
+		if (list == null || list.length == 0)
+			return result;
+		String getterName = "get"
+				+ feature.substring(0, 1).toUpperCase(Locale.ENGLISH)
+				+ feature.substring(1);
+		try {
+			Method getter = list[0].getClass().getMethod(getterName,
+					new Class[0]);
+			try {
+				for (int i = 0; i < list.length; i++) {
+					result.add(getter.invoke(list[i], new Object[0]));
+				}
+			} catch (IllegalArgumentException e) {
+			} catch (IllegalAccessException e) {
+			} catch (InvocationTargetException e) {
+			}
+		} catch (SecurityException e) {
+		} catch (NoSuchMethodException e) {
+		}
+		return result;
+	}
+
+	/**
+	 * This test case deal with the 3rd scenario, using vanilla bindings: Ensure
+	 * a valid content and selection are bounded correctly Bind a collection of
+	 * Lodgings to a ComboViewer Bind the ComboViewer's selection to the
+	 * defaultLodging of an Adventure
+	 * 
+	 * This test does not deal with null values, empty content, changed content,
+	 * property change of content elements, etc.
+	 * 
+	 */
+	public void test_ROCombo_Scenario03_vanilla() {
+		IObservableList lodgings = BeansObservables.observeList(Realm
+				.getDefault(), catalog, "lodgings");
+		ViewerSupport.bind(cviewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		Adventure skiAdventure = SampleData.WINTER_HOLIDAY; // selection will
+
+		// Ensure that cv's content now has the catalog's lodgings
+		assertArrayEquals(catalog.getLodgings(), getViewerContent(cviewer)
+				.toArray());
+
+		// Ensure that the cv's labels are the same as the lodging descriptions
+		assertEquals(getColumn(catalog.getLodgings(), "name"),
+				getComboContent());
+
+		getDbc().bindValue(ViewersObservables.observeSingleSelection(cviewer),
+				BeansObservables.observeValue(skiAdventure, "defaultLodging"));
+
+		// Check to see that the initial selection is the currentDefault Lodging
+		assertEquals(getViewerSelection(), skiAdventure.getDefaultLodging());
+
+		// Change the selection of the ComboViewer to all possible lodgings, and
+		// verify that skiAdventure's default lodging was changed accordingly
+		for (int i = 0; i < catalog.getLodgings().length; i++) {
+			Object selection = catalog.getLodgings()[i];
+			cviewer.setSelection(new StructuredSelection(selection));
+			assertEquals(selection, skiAdventure.getDefaultLodging());
+			assertEquals(getViewerSelection(), skiAdventure.getDefaultLodging());
+		}
+
+	}
+
+	/**
+	 * This test case deal with the 3rd scenario, and focuses on the collection
+	 * binding to the combo. It will bind a collection, add/remove/change
+	 * elements in the collection, and change element's properties to ensure
+	 * that the combo's labels were updated appropriatly.
+	 * 
+	 * it also induce null values in properties, and elments.
+	 * 
+	 * This test does not deal with the combo's selection.
+	 */
+	public void test_ROCombo_Scenario03_collectionBindings() {
+		// column binding
+		// Bind the ComboViewer's content to the available lodging
+		IObservableList lodgings = BeansObservables.observeList(Realm
+				.getDefault(), catalog, "lodgings");
+		ViewerSupport.bind(cviewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		// Ensure that cv's content now has the catalog's lodgings
+		assertArrayEquals(catalog.getLodgings(), getViewerContent(cviewer)
+				.toArray());
+
+		// Ensure that the cv's labels are the same as the lodging descriptions
+		assertEquals(getColumn(catalog.getLodgings(), "name"),
+				getComboContent());
+
+		// Add a lodging in the middle (not supported by the model right now)
+		// Lodging lodging = SampleData.FACTORY.createLodging();
+		// lodging.setName("Middle Lodging");
+		// catalog.addLodging(lodging);
+		// assertEquals(getViewerContent(cviewer).get(2), lodging);
+
+		// Add a lodging at the end
+		Lodging lodging = SampleData.FACTORY.createLodging();
+		lodging.setName("End Lodging");
+		catalog.addLodging(lodging);
+		int index = getComboContent().size() - 1;
+		assertEquals(getViewerContent(cviewer).get(index), lodging);
+
+		// Delete the first Lodging
+		catalog.removeLodging(catalog.getLodgings()[0]);
+		// Ensure that the cv's labels are the same as the lodging descriptions
+		assertEquals(getColumn(catalog.getLodgings(), "name"),
+				getComboContent());
+
+		// Delete middle Lodging
+		catalog.removeLodging(catalog.getLodgings()[2]);
+		// Ensure that the cv's labels are the same as the lodging descriptions
+		assertEquals(getColumn(catalog.getLodgings(), "name"),
+				getComboContent());
+
+		// Change the names of all Lodging
+		for (int i = 0; i < catalog.getLodgings().length; i++) {
+			Lodging l = catalog.getLodgings()[i];
+			l.setName("Changed: " + l.getName());
+		}
+		spinEventLoop(0); // force Async. efforts
+		assertEquals(getColumn(catalog.getLodgings(), "name"),
+				getComboContent());
+
+		// Set to null value
+		Lodging l = catalog.getLodgings()[0];
+		assertEquals(combo.getItem(0), l.getName());
+		l.setName(null);
+		assertEquals("", combo.getItem(0));
+
+		// set to empty list
+		while (catalog.getLodgings().length > 0) {
+			catalog.removeLodging(catalog.getLodgings()[0]);
+			assertEquals(getColumn(catalog.getLodgings(), "name"),
+					getComboContent());
+		}
+	}
+
+	/**
+	 * This scenario tests a simple SWT combo with a set item list where the
+	 * selection is bouded to a String property
+	 */
+	// public void test_ROCombo_Scenario01() {
+	//
+	// // Read-Only Combo will not change its text property on a call to
+	// // setText()
+	//
+	// String[] items = new String[] { "FairyLand", "TuneLand", "NoWereLand",
+	// "TinkerLand", "DreamLand" };
+	// combo.setItems(items);
+	// Account account = catalog.getAccounts()[0];
+	//
+	// // simple Combo's selection bound to the Account's country property
+	// getDbc().bind(new Property(combo, SWTProperties.SELECTION),
+	// new Property(account, "country"), null);
+	//
+	// // Drive the combo selection
+	// int index = 3;
+	// combo.setText(items[index]); // this should drive the selection
+	// assertEquals(account.getCountry(), items[index]);
+	//
+	// // Set the country, and ensure selection is set property
+	// index = 1;
+	// account.setCountry(items[index]);
+	// assertEquals(index, combo.getSelectionIndex());
+	// assertEquals(combo.getText(), items[index]);
+	//
+	// index = combo.getSelectionIndex();
+	// String txt = combo.getText();
+	// // Set the country to something that is not in the Combo's list
+	// account.setCountry("FooBar");
+	// // Combo's selection will not Change
+	// assertEquals(combo.getSelectionIndex(), index);
+	// assertEquals(combo.getText(), txt);
+	//
+	// }
+	/**
+	 * This scenario tests a simple SWT combo that is bound to a list of Country
+	 * objects. The Country object's name property is listed in the Combo.
+	 * 
+	 * The Combo's selection is bounded to the Country property of an Account.
+	 */
+	// public void test_ROCombo_Scenario02_SWTCombo() {
+	//
+	// // Create a list of Strings for the countries
+	// IObservableList list = new WritableList();
+	// for (int i = 0; i < catalog.getAccounts().length; i++)
+	// list.add(catalog.getAccounts()[i].getCountry());
+	//
+	// // Bind the combo's content to that of the String based list
+	// getDbc().bind(combo, list, null);
+	// assertEquals(Arrays.asList(combo.getItems()), list);
+	//
+	// Account account = catalog.getAccounts()[0];
+	//
+	// // simple Combo's selection bound to the Account's country property
+	// getDbc().bind(new Property(combo, SWTProperties.SELECTION),
+	// new Property(account, "country"), null);
+	//
+	// // Drive the combo selection
+	// String selection = (String) list.get(2);
+	// combo.setText(selection); // this should drive the selection
+	// assertEquals(account.getCountry(), selection);
+	//
+	// }
+	/**
+	 * This scenario tests a simple SWT combo that is bound to a list of Country
+	 * objects. The Country object's name property is listed in the Combo.
+	 * 
+	 * The Combo's selection is bounded to the Country property of an Account.
+	 */
+	// public void test_ROCombo_Scenario02_ComboViewer() {
+	//
+	// // Account label provider will fill the combo with the country
+	// cviewer.setLabelProvider(accountLabelProvider);
+	// // Bind the ComboViewer's content to the available accounts
+	// getDbc().bind(
+	// cviewer,
+	// new ListModelDescription(new Property(catalog, "accounts"),
+	// "country"), null);
+	//
+	// // Ensure that cv's content now has the catalog's accounts
+	// assertArrayEquals(catalog.getAccounts(), getViewerContent(cviewer)
+	// .toArray());
+	// // Ensure that the cv's labels are the same as the account countries
+	// assertEquals(getColumn(catalog.getAccounts(), "country"),
+	// getComboContent());
+	//
+	// Account account = SampleData.FACTORY.createAccount();
+	//
+	// // Use the Viewers visual Combo (Strings) to set the account's country
+	// getDbc().bind(new Property(combo, SWTProperties.SELECTION),
+	// new Property(account, "country"), null);
+	//
+	// // Change the selection of the ComboViewer to all possible accounts, and
+	// // verify that the account's Country is being changed correctly.
+	// for (int i = 0; i < catalog.getAccounts().length; i++) {
+	// Account selection = catalog.getAccounts()[i];
+	// cviewer.setSelection(new StructuredSelection(selection));
+	// assertEquals(selection.getCountry(), account.getCountry());
+	// }
+	//
+	// }
+	/**
+	 * This test ensure that multiple combos can be bound to the same deomain
+	 * model
+	 */
+	public void test_ROCombo_multipleBindings() {
+		Adventure skiAdventure = SampleData.WINTER_HOLIDAY; // for selection
+
+		// Bind the ComboViewer's content to the available lodging
+		IObservableList lodgings = BeansObservables.observeList(Realm
+				.getDefault(), catalog, "lodgings");
+		ViewerSupport.bind(cviewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		// Ensure that cv's content now has the catalog's lodgings
+		assertArrayEquals(catalog.getLodgings(), getViewerContent(cviewer)
+				.toArray());
+
+		// Ensure that the cv's labels are the same as the lodging descriptions
+		assertEquals(getColumn(catalog.getLodgings(), "name"),
+				getComboContent());
+
+		ComboViewer otherViewer = new ComboViewer(getComposite(), SWT.NONE);
+		ViewerSupport.bind(otherViewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		// Ensure that cv's content now has the catalog's lodgings
+		assertArrayEquals(catalog.getLodgings(), getViewerContent(otherViewer)
+				.toArray());
+
+		// Bind both selections to the same thing
+		IObservableValue selection = ViewersObservables
+				.observeSingleSelection(cviewer);
+		getDbc().bindValue(selection,
+				BeansObservables.observeValue(skiAdventure, "defaultLodging"));
+
+		IObservableValue otherSelection = ViewersObservables
+				.observeSingleSelection(otherViewer);
+		getDbc().bindValue(otherSelection,
+				BeansObservables.observeValue(skiAdventure, "defaultLodging"));
+
+		Lodging lodging = catalog.getLodgings()[0];
+
+		// Ensure that setting the selection is driven forward to the other
+		// combo
+		cviewer.setSelection(new StructuredSelection(lodging));
+		assertEquals(((IStructuredSelection) cviewer.getSelection())
+				.getFirstElement(), ((IStructuredSelection) otherViewer
+				.getSelection()).getFirstElement());
+
+		// Change the list of one combo, and ensure it updates the other combo
+		catalog.removeLodging(lodging);
+		assertEquals(getViewerContent(cviewer), getViewerContent(otherViewer));
+
+	}
+
+	/**
+	 * This scenario tests a simple SWT CCombo that is bound to a list of
+	 * Country objects. The Country object's name property is listed in the
+	 * Combo.
+	 * 
+	 * The Combo's selection is bounded to the Country property of an Account.
+	 */
+	public void test_ROCombo_SWTCCombo() {
+
+		// Create a list of Strings for the countries
+		IObservableList list = new WritableList();
+		for (int i = 0; i < catalog.getAccounts().length; i++)
+			list.add(catalog.getAccounts()[i].getCountry());
+
+		CCombo ccombo = new CCombo(getComposite(), SWT.READ_ONLY
+				| SWT.DROP_DOWN);
+
+		// Bind the combo's content to that of the String based list
+		getDbc().bindList(SWTObservables.observeItems(ccombo), list);
+		assertEquals(Arrays.asList(ccombo.getItems()), list);
+
+		Account account = catalog.getAccounts()[0];
+
+		// simple Combo's selection bound to the Account's country property
+		IObservableValue comboSelection = SWTObservables
+				.observeSelection(ccombo);
+		getDbc().bindValue(comboSelection,
+				BeansObservables.observeValue(account, "country"));
+
+		// Drive the combo selection
+		String selection = (String) list.get(2);
+		ccombo.setText(selection); // this should drive the selection
+		assertEquals(account.getCountry(), selection);
+
+	}
+
+	/**
+	 * This scenario tests a simple SWT CCombo that is bound to a list of
+	 * Country objects. The Country object's name property is listed in the
+	 * Combo.
+	 * 
+	 * The Combo's selection is bounded to the Country property of an Account.
+	 */
+	public void test_WCombo_SWTCCombo() {
+
+		// Create a list of Strings for the countries
+		IObservableList list = new WritableList();
+		for (int i = 0; i < catalog.getAccounts().length; i++)
+			list.add(catalog.getAccounts()[i].getCountry());
+
+		CCombo ccombo = new CCombo(getComposite(), SWT.READ_ONLY
+				| SWT.DROP_DOWN);
+
+		// Bind the combo's content to that of the String based list
+		getDbc().bindList(SWTObservables.observeItems(ccombo), list);
+		assertEquals(Arrays.asList(ccombo.getItems()), list);
+
+		Account account = catalog.getAccounts()[0];
+
+		// simple Combo's selection bound to the Account's country property
+		IObservableValue comboSelection = SWTObservables
+				.observeSelection(ccombo);
+		getDbc().bindValue(comboSelection,
+				BeansObservables.observeValue(account, "country"));
+
+		// Drive the combo selection
+		String selection = (String) list.get(2);
+		ccombo.setText(selection); // this should drive the selection
+		assertEquals(account.getCountry(), selection);
+
+		selection = (String) list.get(1);
+		account.setCountry(selection);
+		assertEquals(selection, ccombo.getItem(ccombo.getSelectionIndex()));
+		assertEquals(selection, ccombo.getText());
+
+		selection = "country not in list";
+		account.setCountry(selection);
+		assertEquals(-1, ccombo.getSelectionIndex());
+		assertEquals(selection, ccombo.getText());
+	}
+
+	/**
+	 * This scenario tests a simple SWT CCombo that is bound to a list of
+	 * Country objects. The Country object's name property is listed in the
+	 * Combo.
+	 * 
+	 * The Combo's selection is bounded to the Country property of an Account.
+	 */
+	public void test_ROCombo_SWTList() {
+
+		// Create a list of Strings for the countries
+		IObservableList list = new WritableList();
+		for (int i = 0; i < catalog.getAccounts().length; i++)
+			list.add(catalog.getAccounts()[i].getCountry());
+
+		org.eclipse.swt.widgets.List swtlist = new org.eclipse.swt.widgets.List(
+				getComposite(), SWT.READ_ONLY | SWT.SINGLE);
+
+		// Bind the combo's content to that of the String based list
+		getDbc().bindList(SWTObservables.observeItems(swtlist), list);
+		assertEquals(Arrays.asList(swtlist.getItems()), list);
+
+		Account account = catalog.getAccounts()[0];
+
+		// simple Combo's selection bound to the Account's country property
+		IObservableValue listSelection = SWTObservables
+				.observeSelection(swtlist);
+		getDbc().bindValue(listSelection,
+				BeansObservables.observeValue(account, "country"));
+
+		String selection = (String) list.get(2);
+		swtlist.select(2); // this should drive the selection
+		swtlist.notifyListeners(SWT.Selection, null); // Force notification
+		assertEquals(account.getCountry(), selection);
+
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboUpdatingTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboUpdatingTest.java
new file mode 100644
index 0000000..be6ba59
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboUpdatingTest.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.BindingTestSuite;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * @since 3.2
+ */
+public class ComboUpdatingTest extends ScenariosTestCase {
+
+	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
+			this);
+
+	public void addPropertyChangeListener(PropertyChangeListener listener) {
+		propertyChangeSupport.addPropertyChangeListener(listener);
+	}
+
+	public void addPropertyChangeListener(String propertyName,
+			PropertyChangeListener listener) {
+		propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
+	}
+
+	public void removePropertyChangeListener(PropertyChangeListener listener) {
+		propertyChangeSupport.removePropertyChangeListener(listener);
+	}
+
+	public void removePropertyChangeListener(String propertyName,
+			PropertyChangeListener listener) {
+		propertyChangeSupport.removePropertyChangeListener(propertyName,
+				listener);
+	}
+
+	protected void firePropertyChange(String propertyName, Object oldValue,
+			Object newValue) {
+		propertyChangeSupport.firePropertyChange(propertyName, oldValue,
+				newValue);
+	}
+	
+
+	private Combo comboEditable;
+	//private Combo comboReadOnly;
+	
+	private static final String PROP_TEXT = "text";
+	private String text = "Hello, world";
+	
+	public String getText() {
+		return text;
+	}
+
+	public void setText(String text) {
+		this.text = text;
+	}
+	
+	private static final String PROP_CHOICES = "choices";
+	private List choices = new ArrayList();
+	 {
+		choices.add("Banana");
+		choices.add("Apple");
+		choices.add("Mango");
+	}
+
+	public List getChoices() {
+		return choices;
+	}
+
+	public void setChoices(List choices) {
+		this.choices = choices;
+		firePropertyChange(PROP_CHOICES, null, null);
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		getComposite().setLayout(new FillLayout());
+		comboEditable = new Combo(getComposite(), SWT.DROP_DOWN);
+//		comboReadOnly = new Combo(getComposite(), SWT.DROP_DOWN | SWT.READ_ONLY);
+	}
+	
+	//-------------------------------------------------------------------------
+	
+	private static final String NEXT = "Next";
+	public void testBindText() throws Exception {
+        getDbc().bindValue(SWTObservables.observeText(comboEditable), BeansObservables.observeValue(this, "text"));
+		spinEventLoop(0);
+		assertEquals("Should find value of text", text, comboEditable.getText());
+		comboEditable.setText(NEXT);
+		spinEventLoop(0);
+		assertEquals("Should find new value in text", NEXT, text);
+	}
+	
+	public void testBindItems_listHasSameItems_editable() throws Exception {
+		if (BindingTestSuite.failingTestsDisabled(this)) {
+			return;
+		}
+		text = "Apple";
+        
+        getDbc().bindValue(SWTObservables.observeText(comboEditable), BeansObservables.observeValue(this, PROP_TEXT));
+        
+		spinEventLoop(0);
+		assertEquals("Should find value of text", text, comboEditable.getText());
+        
+        IObservableList list = new WritableList(getChoices(), null);
+        getDbc().bindList(SWTObservables.observeItems(comboEditable), list);
+
+		spinEventLoop(0);
+		int position = 0;
+		for (Iterator choicesIter = choices.iterator(); choicesIter.hasNext();) {
+			String element = (String) choicesIter.next();
+			assertEquals(element, comboEditable.getItem(position));
+			++position;
+		}
+//		assertEquals("Should find value of text", "Apple", text);
+		assertEquals("Should find value of combo.getText()", "", comboEditable.getText());
+		comboEditable.setText("Banana");
+		spinEventLoop(0);
+		assertEquals("Should find value of text", "Banana", text);
+	}
+
+//	public void testBindItems_listHasSameItems_readOnly() throws Exception {
+//		text = "Apple";
+//		ComboObservableValue value = (ComboObservableValue) getDbc().createObservable(new Property(comboReadOnly, PROP_TEXT));
+//		getDbc().bind(value.getItems(), new Property(this, PROP_CHOICES), null);
+////		getDbc().bind(combo, new Property(this, PROP_CHOICES, String.class, Boolean.TRUE), null);
+//		spinEventLoop(0);
+//		assertEquals("Should find value of text", "Apple", text);
+// 		getDbc().bind(value, new Property(this, PROP_TEXT), null);
+//		spinEventLoop(0);
+//		assertEquals("Should find value of text", "Apple", text);
+//		assertEquals("Should find value of combo.getText()", "Apple", comboReadOnly.getText());
+//		int position = 0;
+//		for (Iterator choicesIter = choices.iterator(); choicesIter.hasNext();) {
+//			String element = (String) choicesIter.next();
+//			assertEquals(element, comboReadOnly.getItem(position));
+//			++position;
+//		}
+//		assertEquals("Should find value of text", "Apple", text);
+//		assertEquals("Should find value of combo.getText()", "Apple", comboReadOnly.getText());
+//		comboReadOnly.setText("Banana");
+//		spinEventLoop(0);
+//		assertEquals("Should find value of text", "Banana", text);
+//	}
+
+	public void testBindItems_listHasDifferentItems_editable() throws Exception {
+		if (BindingTestSuite.failingTestsDisabled(this)) {
+			return;
+		}
+        
+        getDbc().bindValue(SWTObservables.observeText(comboEditable), BeansObservables.observeValue(this, PROP_TEXT));
+
+		spinEventLoop(0);
+		assertEquals("Should find value of text", text, comboEditable.getText());
+        
+        IObservableList list = new WritableList(new ArrayList(), String.class);
+        list.addAll(getChoices());
+        getDbc().bindList(SWTObservables.observeItems(comboEditable), list);
+        
+		spinEventLoop(0);
+		int position = 0;
+		for (Iterator choicesIter = choices.iterator(); choicesIter.hasNext();) {
+			String element = (String) choicesIter.next();
+			assertEquals(element, comboEditable.getItem(position));
+			++position;
+		}
+//		assertEquals("Should find value of text", "Hello, world", text);
+		assertEquals("Should find value of combo.getText()", "", comboEditable.getText());
+		comboEditable.setText("Banana");
+		spinEventLoop(0);
+		assertEquals("Should find value of text", "Banana", text);
+	}
+
+//	public void testBindItems_listHasDifferentItems_readOnly() throws Exception {
+//		ComboObservableValue value = (ComboObservableValue) getDbc().createObservable(new Property(comboReadOnly, PROP_TEXT));
+//		getDbc().bind(value, new Property(this, PROP_TEXT), null);
+//		spinEventLoop(0);
+//		getDbc().bind(value.getItems(), new Property(this, PROP_CHOICES), null);
+////		getDbc().bind(combo, new Property(this, PROP_CHOICES, String.class, Boolean.TRUE), null);
+//		spinEventLoop(0);
+//		int position = 0;
+//		for (Iterator choicesIter = choices.iterator(); choicesIter.hasNext();) {
+//			String element = (String) choicesIter.next();
+//			assertEquals(element, comboReadOnly.getItem(position));
+//			++position;
+//		}
+////		assertEquals("Should find value of text", "Hello, world", text);
+//		assertEquals("Should find value of combo.getText()", "", comboReadOnly.getText());
+//		comboReadOnly.setText("Banana");
+//		spinEventLoop(0);
+//		assertEquals("Should find value of text", "Banana", text);
+//	}
+	
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboViewerScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboViewerScenario.java
new file mode 100644
index 0000000..fba8350
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ComboViewerScenario.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 160000
+ *     Matthew Hall - bugs 260329, 260337
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.Catalog;
+import org.eclipse.jface.examples.databinding.model.Lodging;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class ComboViewerScenario extends ScenariosTestCase {
+
+	private Catalog catalog;
+
+	private Combo combo;
+
+	private ComboViewer comboViewer;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		// do any setup work here
+		combo = new Combo(getComposite(), SWT.READ_ONLY | SWT.DROP_DOWN);
+		comboViewer = new ComboViewer(combo);
+		catalog = SampleData.CATALOG_2005; // Lodging source
+	}
+
+	protected void tearDown() throws Exception {
+		combo.dispose();
+		combo = null;
+		comboViewer = null;
+		super.tearDown();
+	}
+
+	public void testScenario01() {
+		// Bind the catalog's lodgings to the combo
+		IObservableList lodgings = BeansObservables.observeList(realm, catalog,
+				"lodgings");
+		ViewerSupport.bind(comboViewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		// Verify that the combo's items are the lodgings
+		for (int i = 0; i < catalog.getLodgings().length; i++) {
+			assertEquals(catalog.getLodgings()[i], comboViewer.getElementAt(i));
+		}
+		// Verify that the String being shown in the combo viewer is the
+		// "toString" of the combo viewer
+		String[] lodgingStrings = new String[catalog.getLodgings().length];
+		for (int i = 0; i < catalog.getLodgings().length; i++) {
+			lodgingStrings[i] = catalog.getLodgings()[i].getName();
+		}
+		assertArrayEquals(lodgingStrings, combo.getItems());
+
+		// Verify that the combo has no selected item
+		assertEquals(null, ((IStructuredSelection) comboViewer.getSelection())
+				.getFirstElement());
+
+		// Now bind the selection of the combo to the "defaultLodging" property
+		// of an adventure
+		final Adventure adventure = SampleData.WINTER_HOLIDAY;
+		IObservableValue selection = ViewersObservables
+				.observeSingleSelection(comboViewer);
+		getDbc().bindValue(selection,
+				BeansObservables.observeValue(adventure, "defaultLodging"));
+
+		// Verify that the combo selection is the default lodging
+		assertEquals(((IStructuredSelection) comboViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+		// Change the model and verify that the combo selection changes
+		adventure.setDefaultLodging(SampleData.CAMP_GROUND);
+		assertEquals(adventure.getDefaultLodging(), SampleData.CAMP_GROUND);
+		assertEquals(((IStructuredSelection) comboViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+		// Change the combo selection and verify that the model changes
+		comboViewer.getCombo().select(3);
+		assertEquals(((IStructuredSelection) comboViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+		adventure.setDefaultLodging(SampleData.YOUTH_HOSTEL);
+		spinEventLoop(0);
+		assertEquals(((IStructuredSelection) comboViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomBeanModelType.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomBeanModelType.java
new file mode 100644
index 0000000..f8af853
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomBeanModelType.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.scenarios;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class CustomBeanModelType {
+
+	private String propertyName;
+
+	private Object object;
+
+	private Class type;
+
+	/**
+	 * @param object
+	 * @param propertyName
+	 * @param type
+	 */
+	public CustomBeanModelType(Object object, String propertyName, Class type) {
+		this.object = object;
+		this.propertyName = propertyName;
+		this.type = type;
+	}
+
+	/**
+	 * @return
+	 */
+	public String getPropertyName() {
+		return propertyName;
+	}
+
+	/**
+	 * @return
+	 */
+	public Object getObject() {
+		return object;
+	}
+
+	/**
+	 * @return
+	 */
+	public Class getType() {
+		return type;
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomBeanUpdateValueStrategy.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomBeanUpdateValueStrategy.java
new file mode 100644
index 0000000..e244710
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomBeanUpdateValueStrategy.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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 116920, 159768
+ *******************************************************************************/
+// TODO djo: copyright
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.lang.reflect.Method;
+
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+
+/**
+ * A BindSpec that will automatically grab validators from an object's
+ * properties, if a get&lt;PropertyName>Validator method is defined. Makes it
+ * easy to associate validators with the properties that they are responsible
+ * for validating.
+ * 
+ */
+public class CustomBeanUpdateValueStrategy extends UpdateValueStrategy {
+
+	public IConverter createConverter(Object fromType, Object toType) {
+		if (fromType instanceof CustomBeanModelType) {
+			CustomBeanModelType customBeanModelType = (CustomBeanModelType) fromType;
+			fromType = customBeanModelType.getType();
+		}
+		if (toType instanceof CustomBeanModelType) {
+			CustomBeanModelType customBeanModelType = (CustomBeanModelType) toType;
+			toType = customBeanModelType.getType();
+		}
+		return super.createConverter(fromType, toType);
+	}
+
+	protected void fillDefaults(IObservableValue source,
+			IObservableValue destination) {
+		if (destination.getValueType() instanceof CustomBeanModelType) {
+			if (beforeSetValidator==null) {
+				CustomBeanModelType property = (CustomBeanModelType) destination.getValueType();
+				String propertyName = property.getPropertyName();
+				String getValidatorMethodName = "get" + upperCaseFirstLetter(propertyName) + "Validator"; //$NON-NLS-1$ //$NON-NLS-2$
+
+				Class objectClass = property.getObject().getClass();
+
+				Method getValidator;
+				try {
+					getValidator = objectClass.getMethod(getValidatorMethodName,
+							new Class[] { Class.class });
+					beforeSetValidator= (IValidator) getValidator.invoke(property
+								.getObject(), new Object[0]);
+				} catch (Exception e) {
+					// ignore
+				}
+
+			}
+ 		}
+		super.fillDefaults(source, destination);
+	}
+
+	private String upperCaseFirstLetter(String name) {
+		String result = name.substring(0, 1).toUpperCase() + name.substring(1);
+		return result;
+	}
+
+	public Boolean isAssignableFromTo(Object fromType, Object toType) {
+		if (fromType instanceof CustomBeanModelType) {
+			fromType = ((CustomBeanModelType) fromType).getType();
+		}
+		if (toType instanceof CustomBeanModelType) {
+			toType = ((CustomBeanModelType) toType).getType();
+		}
+		return super.isAssignableFromTo(fromType, toType);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomConverterScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomConverterScenarios.java
new file mode 100644
index 0000000..429a91d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomConverterScenarios.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.PriceModelObject;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class CustomConverterScenarios extends ScenariosTestCase {
+
+    private Adventure skiTrip;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        skiTrip = SampleData.WINTER_HOLIDAY;
+    }
+
+    protected void tearDown() throws Exception {
+        // do any teardown work here
+        super.tearDown();
+    }
+
+    public void testScenario01() {
+        DataBindingContext dbc = getDbc();
+        Spinner spinner_dollars = new Spinner(getComposite(), SWT.NONE);
+        spinner_dollars.setMaximum(10000);
+        Spinner spinner_cents = new Spinner(getComposite(), SWT.NONE);
+
+        // The price object is a double which contains both the dollars and
+        // cents
+        // To allow this to be worked on with two separate spinner controls
+        // (which get and set int values)
+        // an intermediate object is used
+        PriceModelObject priceModel = new PriceModelObject();
+
+        dbc.bindValue(BeansObservables.observeValue(priceModel, "price"), BeansObservables.observeValue(skiTrip,
+                "price"));
+
+        dbc.bindValue(SWTObservables.observeSelection(spinner_dollars),
+                BeansObservables.observeValue(priceModel, "dollars"));
+
+        dbc.bindValue(SWTObservables.observeSelection(spinner_cents),
+                BeansObservables.observeValue(priceModel, "cents"));
+
+        // spinEventLoop(1);
+        // Make sure that the selection on the spinner_dollars matches the
+        // dollars of the price
+        assertEquals(spinner_dollars.getSelection(), new Double(skiTrip.getPrice()).intValue());
+        // Make sure that the selection on the spinner_cents matches the dollars
+        // of the price
+        Double doublePrice = new Double(skiTrip.getPrice());
+        double cents = 100 * doublePrice.doubleValue() - 100 * doublePrice.intValue();
+        assertEquals(spinner_cents.getSelection(), (int) cents);
+
+        // Change the selection on the spinner_dollars to be $50 and make sure
+        // the model is updated with the cents included
+        spinner_dollars.setSelection(50);
+        double expectedPrice = 50 + cents / 100;
+        assertEquals(expectedPrice, skiTrip.getPrice(), 0.01);
+
+        // Change the selection on the spinner_cents to be 27 and make sure the
+        // model is updated with the dollars included
+        spinner_cents.setSelection(27);
+        assertEquals(50.27, skiTrip.getPrice(), 0.01);
+
+        // Change the model to be $60.99 dollars and make sure the
+        // spinner_dollars is 60 and spinner_cents is 99
+        skiTrip.setPrice(60.99);
+        assertEquals(60, spinner_dollars.getSelection());
+        assertEquals(99, spinner_cents.getSelection());
+
+    }
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomScenarios.java
new file mode 100644
index 0000000..30aedf6
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/CustomScenarios.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.AggregateObservableValue;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class CustomScenarios extends ScenariosTestCase {
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        // do any setup work here
+    }
+
+    protected void tearDown() throws Exception {
+        // do any teardown work here
+        super.tearDown();
+    }
+
+    public void testScenario01() {
+
+        // Binding the name property of an Adventure object to the contents of
+        // Text controls, no conversion, no validation.
+
+        Adventure adventure = SampleData.WINTER_HOLIDAY;
+        Text text = new Text(getComposite(), SWT.BORDER);
+
+        IObservableValue descriptionObservable = BeansObservables.observeValue(adventure, "description");
+        IObservableValue nameObservable = BeansObservables.observeValue(adventure, "name");
+        AggregateObservableValue customObservable_comma = new AggregateObservableValue(new IObservableValue[] {
+                descriptionObservable, nameObservable }, ",");
+
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify), customObservable_comma);
+        // spinEventLoop(1);
+        // Make sure that the description on the model match the widget
+        assertEquals(adventure.getDescription() + "," + adventure.getName(), text.getText());
+
+        // Change the widget to newDescription,newName and ensure the model is
+        // updated
+        text.setText("newDescription,newName");
+        assertEquals("newDescription", adventure.getDescription());
+        assertEquals("newName", adventure.getName());
+
+        // Change the model to newDescription_0 and newName_0 and ensure the GUI
+        // is updated
+        adventure.setDescription("newDescription_0");
+        adventure.setName("newName_0");
+        assertEquals("newDescription_0,newName_0", text.getText());
+
+        // Change text to newDescription_1 with no comma and ensure the model is
+        // updated correctly with no name
+        text.setText("newDescription_1");
+        assertEquals("newDescription_1", adventure.getDescription());
+        assertEquals(null, adventure.getName());
+
+        // Change text to newName with a preceeding comma and ensure the model
+        // is updated correctly with no description
+        // TODO - Get this test working + Add the one where we have two
+        // aggregates and update one and
+        // check that the other is updated - currently this fails on the GUI -
+        // JRW
+        // text.setText(",newName_1");
+        // assertEquals(null, adventure.getDescription());
+        // assertEquals("newName_1", adventure.getName());
+
+    }
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/LabelControlScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/LabelControlScenario.java
new file mode 100644
index 0000000..a069d01
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/LabelControlScenario.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Label;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class LabelControlScenario extends ScenariosTestCase {
+
+    private Adventure adventure;
+
+    private Label label;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        // do any setup work here
+        label = new Label(getComposite(), SWT.NONE);
+        adventure = SampleData.WINTER_HOLIDAY;
+    }
+
+    protected void tearDown() throws Exception {
+        // do any teardown work here
+        super.tearDown();
+        label.dispose();
+        label = null;
+    }
+
+    public void testScenario01() {
+        // Bind the adventure "name" property to a label control
+        // Change the UI and verify the model and UI are the same value
+        // Change the model and verify the UI changes
+        getDbc().bindValue(SWTObservables.observeText(label), BeansObservables.observeValue(adventure, "name"));
+
+        assertEquals(adventure.getName(), label.getText());
+        adventure.setName("France");
+        assertEquals("France", label.getText());
+        adventure.setName("Climb Everest");
+        spinEventLoop(0);
+        assertEquals("Climb Everest", label.getText());
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ListViewerScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ListViewerScenario.java
new file mode 100644
index 0000000..55fa2b8
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ListViewerScenario.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 116920, 160000
+ *     Matthew Hall - bugs 260329, 260337
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.Catalog;
+import org.eclipse.jface.examples.databinding.model.Lodging;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.List;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class ListViewerScenario extends ScenariosTestCase {
+
+	private Catalog catalog;
+
+	private List list;
+
+	private ListViewer listViewer;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		// do any setup work here
+		list = new List(getComposite(), SWT.READ_ONLY | SWT.SINGLE);
+		listViewer = new ListViewer(list);
+		catalog = SampleData.CATALOG_2005; // Lodging source
+	}
+
+	protected void tearDown() throws Exception {
+		list.dispose();
+		list = null;
+		listViewer = null;
+		super.tearDown();
+	}
+
+	public void testScenario01() {
+		// Bind the catalog's lodgings to the combo
+		IObservableList lodgings = BeansObservables.observeList(Realm
+				.getDefault(), catalog, "lodgings");
+		ViewerSupport.bind(listViewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		// Verify that the combo's items are the lodgings
+		for (int i = 0; i < catalog.getLodgings().length; i++) {
+			assertEquals(catalog.getLodgings()[i], listViewer.getElementAt(i));
+		}
+		// Verify that the String being shown in the list viewer is the
+		// "toString" of the combo viewer
+		String[] lodgingStrings = new String[catalog.getLodgings().length];
+		for (int i = 0; i < catalog.getLodgings().length; i++) {
+			lodgingStrings[i] = catalog.getLodgings()[i].getName();
+		}
+		assertArrayEquals(lodgingStrings, list.getItems());
+
+		// Verify that the list has no selected item
+		assertEquals(null, ((IStructuredSelection) listViewer.getSelection())
+				.getFirstElement());
+
+		// Now bind the selection of the combo to the "defaultLodging" property
+		// of an adventure
+		final Adventure adventure = SampleData.WINTER_HOLIDAY;
+
+		IObservableValue selection = ViewersObservables
+				.observeSingleSelection(listViewer);
+		getDbc().bindValue(selection,
+				BeansObservables.observeValue(adventure, "defaultLodging"));
+
+		// Verify that the list selection is the default lodging
+		assertEquals(((IStructuredSelection) listViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+		// Change the model and verify that the list selection changes
+		adventure.setDefaultLodging(SampleData.CAMP_GROUND);
+		assertEquals(adventure.getDefaultLodging(), SampleData.CAMP_GROUND);
+		assertEquals(((IStructuredSelection) listViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+		// Change the list selection and verify that the model changes
+		listViewer.getList().select(3);
+		assertEquals(((IStructuredSelection) listViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+		adventure.setDefaultLodging(SampleData.YOUTH_HOSTEL);
+		spinEventLoop(0);
+		assertEquals(((IStructuredSelection) listViewer.getSelection())
+				.getFirstElement(), adventure.getDefaultLodging());
+
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/MasterDetailScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/MasterDetailScenarios.java
new file mode 100644
index 0000000..0081fef
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/MasterDetailScenarios.java
@@ -0,0 +1,346 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 160000
+ *     Matthew Hall - bugs 260329, 260337
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.Catalog;
+import org.eclipse.jface.examples.databinding.model.Category;
+import org.eclipse.jface.examples.databinding.model.Lodging;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class MasterDetailScenarios extends ScenariosTestCase {
+
+	protected Object getViewerSelection(ContentViewer contentViewer) {
+		return ((IStructuredSelection) contentViewer.getSelection())
+				.getFirstElement();
+	}
+
+	/**
+	 * @return the ComboViewer's domain object list
+	 */
+	protected List getViewerContent(ContentViewer contentViewer) {
+		Object[] elements = ((IStructuredContentProvider) contentViewer
+				.getContentProvider()).getElements(null);
+		if (elements != null)
+			return Arrays.asList(elements);
+		return null;
+	}
+
+	public void testScenario01() {
+		// Displaying the catalog's list of Lodging objects in a list viewer,
+		// using their names. The name of the currently selected Lodging can
+		// be edited in a text widget. There is always a selected Lodging
+		// object.
+		ListViewer listViewer = new ListViewer(getComposite(), SWT.BORDER);
+		Realm realm = SWTObservables.getRealm(listViewer.getControl()
+				.getDisplay());
+		listViewer.getList().setLayoutData(
+				new GridData(SWT.FILL, SWT.FILL, false, false));
+		Catalog catalog = SampleData.CATALOG_2005;
+
+		IObservableList lodgings = BeansObservables.observeList(realm, catalog,
+				"lodgings");
+		ViewerSupport.bind(listViewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		assertArrayEquals(catalog.getLodgings(), getViewerContent(listViewer)
+				.toArray());
+
+		IObservableValue selectedLodging = ViewersObservables
+				.observeSingleSelection(listViewer);
+
+		selectedLodging.setValue(SampleData.CAMP_GROUND);
+
+		assertEquals(SampleData.CAMP_GROUND, getViewerSelection(listViewer));
+		Text txtName = new Text(getComposite(), SWT.BORDER);
+
+		getDbc().bindValue(
+				SWTObservables.observeText(txtName, SWT.Modify),
+				BeansObservables.observeDetailValue(selectedLodging, "name",
+						String.class));
+
+		assertEquals(txtName.getText(), SampleData.CAMP_GROUND.getName());
+		enterText(txtName, "foobar");
+		assertEquals("foobar", SampleData.CAMP_GROUND.getName());
+		listViewer.setSelection(new StructuredSelection(
+				SampleData.FIVE_STAR_HOTEL));
+		assertEquals(SampleData.FIVE_STAR_HOTEL, selectedLodging.getValue());
+		assertEquals(SampleData.FIVE_STAR_HOTEL.getName(), txtName.getText());
+		SampleData.FIVE_STAR_HOTEL.setName("barfoo");
+		assertEquals("barfoo", txtName.getText());
+
+		// Now make sure that the event listeners get removed on dispose()
+		// Values should no longer be updated
+		selectedLodging.dispose();
+
+		// selectedLodging.setValue(SampleData.CAMP_GROUND);
+		// assertNotSame(SampleData.CAMP_GROUND,
+		// getViewerSelection(listViewer));
+		// assertNotSame(txtName.getText(), SampleData.CAMP_GROUND.getName());
+		// enterText(txtName, "foobar");
+		// assertNotSame("foobar", SampleData.CAMP_GROUND.getName());
+		// listViewer.setSelection(new StructuredSelection(
+		// SampleData.FIVE_STAR_HOTEL));
+		// assertNotSame(SampleData.FIVE_STAR_HOTEL,
+		// selectedLodging.getValue());
+		// assertNotSame(SampleData.FIVE_STAR_HOTEL.getName(),
+		// txtName.getText());
+		// SampleData.FIVE_STAR_HOTEL.setName("barfoo");
+		// assertNotSame("barfoo", txtName.getText());
+	}
+
+	public void testScenario02() {
+		// Selecting from the list of lodgings for an adventure and editing the
+		// properties of the selected lodging in text widgets. If no lodging is
+		// selected the input controls for name and adventure are disabled.
+		// There are two buttons "Add" and "Remove"; clicking on "Add" creates a
+		// new lodging and selects it so it can be edited, clicking on "Remove"
+		// removes the currently selected lodging from the list.
+		final ListViewer listViewer = new ListViewer(getComposite(), SWT.BORDER);
+		listViewer.getList().setLayoutData(
+				new GridData(SWT.FILL, SWT.FILL, false, false));
+		final Catalog catalog = SampleData.CATALOG_2005;
+
+		IObservableList lodgings = BeansObservables.observeList(realm, catalog,
+				"lodgings");
+		ViewerSupport.bind(listViewer, lodgings, BeanProperties.value(
+				Lodging.class, "name"));
+
+		assertArrayEquals(catalog.getLodgings(), getViewerContent(listViewer)
+				.toArray());
+
+		final IObservableValue selectedLodgingObservable = ViewersObservables
+				.observeSingleSelection(listViewer);
+
+		selectedLodgingObservable.setValue(null);
+		assertTrue(listViewer.getSelection().isEmpty());
+
+		ComputedValue selectionExistsObservable = new ComputedValue(
+				boolean.class) {
+			protected Object calculate() {
+				return new Boolean(selectedLodgingObservable.getValue() != null);
+			}
+		};
+
+		assertFalse(((Boolean) selectionExistsObservable.getValue())
+				.booleanValue());
+
+		final Text txtName = new Text(getComposite(), SWT.BORDER);
+
+		getDbc().bindValue(SWTObservables.observeEnabled(txtName),
+				selectionExistsObservable);
+		getDbc().bindValue(
+				SWTObservables.observeText(txtName, SWT.Modify),
+				BeansObservables.observeDetailValue(selectedLodgingObservable,
+						"name", String.class));
+
+		assertEquals(txtName.getText(), "");
+		assertFalse(txtName.getEnabled());
+
+		final Text txtDescription = new Text(getComposite(), SWT.BORDER);
+
+		getDbc().bindValue(SWTObservables.observeEnabled(txtDescription),
+				selectionExistsObservable);
+		getDbc().bindValue(
+				SWTObservables.observeText(txtDescription, SWT.Modify),
+				MasterDetailObservables.detailValue(selectedLodgingObservable,
+						BeansObservables.valueFactory(realm, "description"),
+						String.class));
+
+		assertEquals(txtDescription.getText(), "");
+		assertFalse(txtDescription.getEnabled());
+
+		Button addButton = new Button(getComposite(), SWT.PUSH);
+		addButton.addSelectionListener(new SelectionListener() {
+			public void widgetSelected(SelectionEvent e) {
+				Lodging selectedLodging = (Lodging) selectedLodgingObservable
+						.getValue();
+				int insertionIndex = 0;
+				if (selectedLodging != null) {
+					insertionIndex = Arrays.asList(catalog.getLodgings())
+							.indexOf(selectedLodging);
+					assertTrue(insertionIndex >= 0);
+				}
+				Lodging newLodging = SampleData.FACTORY.createLodging();
+				int itemCount = listViewer.getList().getItemCount();
+				newLodging.setName("new lodging name " + itemCount);
+				newLodging.setDescription("new lodging description "
+						+ itemCount);
+				catalog.addLodging(newLodging);
+				assertEquals(itemCount + 1, listViewer.getList().getItemCount());
+				listViewer.setSelection(new StructuredSelection(newLodging));
+				assertSame(newLodging, selectedLodgingObservable.getValue());
+				assertTrue(txtName.getEnabled());
+				assertTrue(txtDescription.getEnabled());
+				assertEquals(newLodging.getName(), txtName.getText());
+				assertEquals(newLodging.getDescription(), txtDescription
+						.getText());
+			}
+
+			public void widgetDefaultSelected(SelectionEvent e) {
+				widgetSelected(e);
+			}
+		});
+
+		Button removeButton = new Button(getComposite(), SWT.PUSH);
+		removeButton.addSelectionListener(new SelectionListener() {
+			public void widgetSelected(SelectionEvent e) {
+				Lodging selectedLodging = (Lodging) selectedLodgingObservable
+						.getValue();
+				assertNotNull(selectedLodging);
+				int deletionIndex = Arrays.asList(catalog.getLodgings())
+						.indexOf(selectedLodging);
+				assertTrue(deletionIndex >= 0);
+				int itemCount = listViewer.getList().getItemCount();
+				catalog.removeLodging(selectedLodging);
+				assertEquals(itemCount - 1, listViewer.getList().getItemCount());
+				assertNull(selectedLodgingObservable.getValue());
+			}
+
+			public void widgetDefaultSelected(SelectionEvent e) {
+				widgetSelected(e);
+			}
+		});
+
+		pushButtonWithEvents(addButton);
+		pushButtonWithEvents(removeButton);
+		pushButtonWithEvents(addButton);
+		pushButtonWithEvents(addButton);
+		pushButtonWithEvents(removeButton);
+	}
+
+	public void testScenario03() {
+		// List adventures and for the selected adventure allow its default
+		// lodging�s name and description to be changed in text controls. If
+		// there is no selected adventure or the default lodging is null the
+		// text controls are disabled. This is a nested property. The default
+		// lodging can be changed elsewhere, and the list
+		final Catalog catalog = SampleData.CATALOG_2005;
+
+		final ListViewer categoryListViewer = new ListViewer(getComposite(),
+				SWT.BORDER);
+		categoryListViewer.getList().setLayoutData(
+				new GridData(SWT.FILL, SWT.FILL, false, false));
+
+		IObservableList categories = BeansObservables.observeList(realm,
+				catalog, "categories");
+		ViewerSupport.bind(categoryListViewer, categories, BeanProperties
+				.value(Category.class, "name"));
+
+		assertArrayEquals(catalog.getCategories(), getViewerContent(
+				categoryListViewer).toArray());
+
+		final IObservableValue selectedCategoryObservable = ViewersObservables
+				.observeSingleSelection(categoryListViewer);
+
+		final ListViewer adventureListViewer = new ListViewer(getComposite(),
+				SWT.BORDER);
+		adventureListViewer.getList().setLayoutData(
+				new GridData(SWT.FILL, SWT.FILL, false, false));
+
+		IObservableList adventures = BeansObservables.observeDetailList(
+				selectedCategoryObservable, "adventures", Adventure.class);
+		ViewerSupport.bind(adventureListViewer, adventures, BeanProperties
+				.value(Adventure.class, "name"));
+
+		ComputedValue categorySelectionExistsObservable = new ComputedValue() {
+			protected Object calculate() {
+				return new Boolean(
+						selectedCategoryObservable.getValue() != null);
+			}
+		};
+
+		getDbc().bindValue(
+				SWTObservables.observeEnabled(adventureListViewer.getList()),
+				categorySelectionExistsObservable);
+
+		final IObservableValue selectedAdventureObservable = ViewersObservables
+				.observeSingleSelection(adventureListViewer);
+
+		ComputedValue adventureSelectionExistsObservable = new ComputedValue() {
+			protected Object calculate() {
+				return new Boolean(
+						selectedAdventureObservable.getValue() != null);
+			}
+		};
+
+		final Text txtName = new Text(getComposite(), SWT.BORDER);
+
+		getDbc().bindValue(SWTObservables.observeEnabled(txtName),
+				adventureSelectionExistsObservable);
+		getDbc().bindValue(
+				SWTObservables.observeText(txtName, SWT.Modify),
+				BeansObservables.observeDetailValue(
+						selectedAdventureObservable, "name", String.class));
+
+		assertEquals(txtName.getText(), "");
+		assertFalse(txtName.getEnabled());
+
+		final Text txtDescription = new Text(getComposite(), SWT.BORDER);
+
+		getDbc().bindValue(SWTObservables.observeEnabled(txtDescription),
+				adventureSelectionExistsObservable);
+		getDbc().bindValue(
+				SWTObservables.observeText(txtDescription, SWT.Modify),
+				BeansObservables.observeDetailValue(
+						selectedAdventureObservable, "description",
+						String.class));
+
+		assertFalse(adventureListViewer.getList().isEnabled());
+		categoryListViewer.setSelection(new StructuredSelection(
+				SampleData.SUMMER_CATEGORY));
+		assertTrue(adventureListViewer.getList().isEnabled());
+		assertFalse(txtName.getEnabled());
+		adventureListViewer.setSelection(new StructuredSelection(
+				SampleData.RAFTING_HOLIDAY));
+		assertEquals(Boolean.TRUE, adventureSelectionExistsObservable
+				.getValue());
+		assertTrue(txtName.getEnabled());
+		assertEquals(SampleData.RAFTING_HOLIDAY.getName(), txtName.getText());
+		categoryListViewer.setSelection(new StructuredSelection(
+				SampleData.WINTER_CATEGORY));
+		assertTrue(adventureListViewer.getList().isEnabled());
+		assertFalse(txtName.getEnabled());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/NPETestScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/NPETestScenario.java
new file mode 100644
index 0000000..2cdcd25
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/NPETestScenario.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class NPETestScenario extends ScenariosTestCase {
+	private Text text;
+
+	Person person;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		person = new Person();
+		text = new Text(getComposite(), SWT.BORDER);
+	}
+
+	/**
+	 * Asserts the ability to have an initial value of <code>null</code> on the
+	 * model and to update the value by changing the value of the view.
+	 */
+	public void test_InitialNullValue() {
+		Person person = new Person();
+		assertNull(person.getName());
+
+		System.out
+				.println("Expecting message about not being able to attach a listener");
+		getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+				BeansObservables.observeValue(person, "name"));
+
+		text.setText("Brad");
+		text.notifyListeners(SWT.FocusOut, null);
+		assertEquals("Brad", person.getName());
+	}
+
+	static class Person {
+		private String name;
+
+		/**
+		 * @return Returns the name.
+		 */
+		public String getName() {
+			return name;
+		}
+
+		/**
+		 * @param name
+		 *            The name to set.
+		 */
+		public void setName(String name) {
+			this.name = name;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/NewTableScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/NewTableScenarios.java
new file mode 100644
index 0000000..6a32325
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/NewTableScenarios.java
@@ -0,0 +1,443 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.jface.examples.databinding.model.Catalog;
+import org.eclipse.jface.examples.databinding.model.Category;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class NewTableScenarios extends ScenariosTestCase {
+
+	private TableViewer tableViewer;
+
+	Catalog catalog;
+
+	Category category;
+
+	private TableColumn firstNameColumn;
+
+	private TableColumn lastNameColumn;
+
+	private TableColumn stateColumn;
+
+	Image[] images;
+
+	private TableColumn fancyColumn;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		getComposite().setLayout(new FillLayout());
+		tableViewer = new TableViewer(getComposite());
+		tableViewer.getTable().setLinesVisible(true);
+		tableViewer.getTable().setHeaderVisible(true);
+		firstNameColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		firstNameColumn.setWidth(50);
+		lastNameColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		lastNameColumn.setWidth(50);
+		stateColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		stateColumn.setWidth(50);
+		fancyColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		fancyColumn.setWidth(250);
+
+		catalog = SampleData.CATALOG_2005; // Lodging source
+		category = SampleData.WINTER_CATEGORY;
+
+		images = new Image[] {
+				getShell().getDisplay().getSystemImage(SWT.ICON_ERROR),
+				getShell().getDisplay().getSystemImage(SWT.ICON_WARNING),
+				getShell().getDisplay().getSystemImage(SWT.ICON_INFORMATION), };
+	}
+
+	protected void tearDown() throws Exception {
+		// do any teardown work here
+		super.tearDown();
+		tableViewer.getTable().dispose();
+		tableViewer = null;
+		firstNameColumn = null;
+		lastNameColumn = null;
+		stateColumn = null;
+	}
+
+	String getValue(String text) {
+		if (text == null)
+			return "";
+		return text;
+	}
+
+	public void testScenario01() {
+//		// Factory for directly creating IObservables for beans
+//		JavaBeans javaBeans = new JavaBeans();
+//		
+//		// Wrap the TableViewer in an IObservableCollectionWithLabels
+//		IObservableCollectionWithLabels accountTable = new TableViewerObservableTable(
+//				tableViewer);
+//
+//		// Create a readable set to track the catalog's accounts
+//		IReadableSet accountSet = javaBeans.createReadableSet(catalog,
+//				new PropertyDescriptor("accounts", Catalog.class, "getAccounts", null),
+//				Account.class);
+//		
+//		// Create an observable cell provider to track the given accounts' properties.
+//		IObservableCellProvider accountCellProvider = javaBeans.createObservableCellProvider(
+//				accountSet, new String[] { "firstName", "lastName", "state" });
+//
+//		// bind
+//		getDbc().bind(accountTable, accountCellProvider, null);
+//		
+//		// uncomment next line to see result interactively
+//		// interact();
+//
+//		// Verify the data in the table columns matches the accounts
+//		Account[] accounts = catalog.getAccounts();
+//		for (int i = 0; i < accounts.length; i++) {
+//			Account account = accounts[i];
+//			String col_0 = ((ITableLabelProvider) tableViewer
+//					.getLabelProvider()).getColumnText(account, 0);
+//			assertEquals(getValue(account.getFirstName()), col_0);
+//			String col_1 = ((ITableLabelProvider) tableViewer
+//					.getLabelProvider()).getColumnText(account, 1);
+//			assertEquals(getValue(account.getLastName()), col_1);
+//			String col_2 = ((ITableLabelProvider) tableViewer
+//					.getLabelProvider()).getColumnText(account, 2);
+//			assertEquals(getValue(account.getState()), col_2);
+//
+//		}
+	}
+
+	public void testScenario02() throws SecurityException,
+			IllegalArgumentException {
+//		// Show that a TableViewer with three columns can be used to update
+//		// columns
+//		Account[] accounts = catalog.getAccounts();
+//
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addEditableColumn("firstName");
+//		tableViewerDescription.addEditableColumn("lastName", null, null,
+//				new PhoneConverter());
+//		tableViewerDescription.addEditableColumn("state", null, null,
+//				new StateConverter());
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "accounts"), null);
+//
+//		Account account = accounts[0];
+//		// Select the first item in the table
+//		tableViewer.editElement(account, 0);
+//		// Set the text property of the cell editor which is now active over the
+//		// "firstName" column
+//		CellEditor[] cellEditors = tableViewer.getCellEditors();
+//		TextCellEditor firstNameEditor = (TextCellEditor) cellEditors[0];
+//		// Change the firstName and test it goes to the model
+//		enterText((Text) firstNameEditor.getControl(), "Bill");
+//		// Check whether the model has changed
+//		assertEquals("Bill", account.getFirstName());
+	}
+
+	public void testScenario04() {
+//		// Show that when an item is added to a collection the table gets an
+//		// extra item
+//		Account[] accounts = catalog.getAccounts();
+//
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addColumn("firstName");
+//		tableViewerDescription.addColumn("lastName");
+//		tableViewerDescription.addColumn("state");
+//		tableViewerDescription.addColumn(null, new IConverter() {
+//
+//			public Class getModelType() {
+//				return Account.class;
+//			}
+//
+//			public Class getTargetType() {
+//				return ViewerLabel.class;
+//			}
+//
+//			public Object convertTargetToModel(Object targetObject) {
+//				return null;
+//			}
+//
+//			public Object convertModelToTarget(Object modelObject) {
+//				Account account = (Account) modelObject;
+//				return new ViewerLabel(account.toString(), images[new Random()
+//						.nextInt(images.length)]);
+//			}
+//		});
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "accounts"), null);
+//
+//		// interact();
+//
+//		// Verify the number of accounts matches the number of items in the
+//		// table
+//		assertEquals(tableViewer.getTable().getItemCount(), accounts.length);
+//		// Add a new account and verify that the number of items in the table
+//		// increases
+//		Account newAccount = new Account();
+//		newAccount.setFirstName("Finbar");
+//		newAccount.setLastName("McGoo");
+//		newAccount.setLastName("NC");
+//		catalog.addAccount(newAccount);
+//		// The number of items should have gone up by one
+//		assertEquals(tableViewer.getTable().getItemCount(), accounts.length + 1);
+//		// The number of items should still match the number of accounts (i.e.
+//		// test the model)
+//		assertEquals(tableViewer.getTable().getItemCount(), catalog
+//				.getAccounts().length);
+//		// Remove the account that was just added
+//		catalog.removeAccount(newAccount);
+//		// The number of items should match the original
+//		assertEquals(tableViewer.getTable().getItemCount(), accounts.length);
+//		// The number of items should still match the number of accounts (i.e.
+//		// test the model is reset)
+//		assertEquals(tableViewer.getTable().getItemCount(), catalog
+//				.getAccounts().length);
+//
+//		// Test adding and removing to the model on a non UI thread
+//		int numberOfAccounts = catalog.getAccounts().length;
+//		final Account barney = new Account();
+//		barney.setFirstName("Barney");
+//		barney.setLastName("Smith");
+//		barney.setLastName("CA");
+//		invokeNonUI(new Runnable() {
+//			public void run() {
+//				catalog.addAccount(barney);
+//			}
+//		});
+//		spinEventLoop(0);
+//		// The number of items should have gone up by one
+//		assertEquals(tableViewer.getTable().getItemCount(),
+//				numberOfAccounts + 1);
+//
+//		invokeNonUI(new Runnable() {
+//			public void run() {
+//				catalog.removeAccount(barney);
+//			}
+//		});
+//		spinEventLoop(0);
+//		// The number of items should have reverted to the original number
+//		// before barney was added and removed
+//		assertEquals(tableViewer.getTable().getItemCount(), numberOfAccounts);
+//
+	}
+
+	public void testScenario03() {
+//		// Show that converters work for table columns
+//		Account[] accounts = catalog.getAccounts();
+//
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addEditableColumn("lastName");
+//		tableViewerDescription.addEditableColumn("phone", null, null,
+//				new PhoneConverter());
+//		tableViewerDescription.addEditableColumn("state", null, null,
+//				new StateConverter());
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "accounts"), null);
+//
+//		// Verify that the data in the the table columns matches the expected
+//		// What we are looking for is that the phone numbers are converterted to
+//		// nnn-nnn-nnnn and that
+//		// the state letters are converted to state names
+//		// Verify the data in the table columns matches the accounts
+//		PhoneConverter phoneConverter = new PhoneConverter();
+//		StateConverter stateConverter = new StateConverter();
+//		for (int i = 0; i < accounts.length; i++) {
+//			Account account = catalog.getAccounts()[i];
+//			// Check the phone number
+//			String col_phone = ((ITableLabelProvider) tableViewer
+//					.getLabelProvider()).getColumnText(account, 1);
+//			assertEquals(getValue((String) phoneConverter
+//					.convertModelToTarget(account.getPhone())), col_phone);
+//			String col_state = ((ITableLabelProvider) tableViewer
+//					.getLabelProvider()).getColumnText(account, 2);
+//			assertEquals(getValue((String) stateConverter
+//					.convertModelToTarget(account.getState())), col_state);
+//		}
+	}
+
+	public void testScenario05() {
+//		// Show that when the model changes then the UI refreshes to reflect
+//		// this
+//
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addColumn("lastName");
+//		tableViewerDescription.addColumn("phone", new PhoneConverter());
+//		tableViewerDescription.addColumn("state", new StateConverter());
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "accounts"), null);
+//
+//		final Account account = catalog.getAccounts()[0];
+//		String lastName = tableViewer.getTable().getItem(0).getText(0);
+//		// Check the firstName in the TableItem is the same as the model
+//		assertEquals(lastName, account.getLastName());
+//		// Now change the model and check again
+//		account.setLastName("Gershwin");
+//		lastName = tableViewer.getTable().getItem(0).getText(0);
+//		assertEquals(lastName, account.getLastName());
+//
+//		// Test the model update on a non UI thread
+//		invokeNonUI(new Runnable() {
+//			public void run() {
+//				account.setLastName("Mozart");
+//			}
+//		});
+//		spinEventLoop(0);
+//		lastName = tableViewer.getTable().getItem(0).getText(0);
+//		assertEquals(lastName, account.getLastName());
+//
+	}
+
+	public void testScenario06() {
+//		// Check that explicit type means that defaulting of converters works
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addEditableColumn("price");
+//		tableViewerDescription.getColumn(0).setPropertyType(Double.TYPE);
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "transporations"), null);
+//		Transportation transporation = catalog.getTransporations()[0];
+//		tableViewer.editElement(transporation, 0);
+//		// Set the text property of the cell editor which is now active over the
+//		// "firstName" column
+//		CellEditor[] cellEditors = tableViewer.getCellEditors();
+//		TextCellEditor priceEditor = (TextCellEditor) cellEditors[0];
+//		// Change the firstName and test it goes to the model
+//		enterText((Text) priceEditor.getControl(), "123.45");
+//		// Verify the model is updated
+//		assertEquals(transporation.getPrice(), 123.45, 0);
+
+	}
+
+	public void testScenario07() {
+//		// Verify that even when a column's property type is not set, that it is
+//		// worked out lazily from the target type
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addEditableColumn("price");
+//		// The column's type is not set to be Double.TYPE. This will be inferred
+//		// once the first Transportation object is set
+//		// into the ObservableCollection
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "transporations"), null);
+//		Transportation transporation = catalog.getTransporations()[0];
+//		tableViewer.editElement(transporation, 0);
+//		// Set the text property of the cell editor which is now active over the
+//		// "firstName" column
+//		CellEditor[] cellEditors = tableViewer.getCellEditors();
+//		TextCellEditor priceEditor = (TextCellEditor) cellEditors[0];
+//		// Change the firstName and test it goes to the model
+//		enterText((Text) priceEditor.getControl(), "123.45");
+//		// Verify the model is updated
+//		assertEquals(transporation.getPrice(), 123.45, 0);
+//
+	}
+
+	public void testScenario08_00() {
+//		// Verify that binding to a Collection property (rather than an array)
+//		// works when specifying data type
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addEditableColumn("userId");
+//		tableViewerDescription.addEditableColumn("password");
+//		getDbc().bind(tableViewerDescription,
+//				new Property(catalog, "signons", Signon.class, null), null);
+//		Signon firstSignon = (Signon) catalog.getSignons().get(0);
+//		// Verify the UI matches the model
+//		TableItem firstTableItem = tableViewer.getTable().getItem(0);
+//		assertEquals(firstTableItem.getText(1), firstSignon.getPassword());
+//		// Change the model and ensure the UI refreshes
+//		firstSignon.setPassword("Eclipse123Rocks");
+//		assertEquals("Eclipse123Rocks", firstSignon.getPassword());
+//		assertEquals(firstTableItem.getText(1), firstSignon.getPassword());
+//		// Change the GUI and ensure the model refreshes
+//		tableViewer.editElement(firstSignon, 1);
+//		CellEditor[] cellEditors = tableViewer.getCellEditors();
+//		TextCellEditor passwordEditor = (TextCellEditor) cellEditors[1];
+//		enterText((Text) passwordEditor.getControl(), "Cricket11Players");
+//		assertEquals("Cricket11Players", firstSignon.getPassword());
+//
+	}
+
+	public void testScenario08_01() {
+//		// Verify that binding to a Collection property (rather than an array)
+//		// works without specifying data type
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addEditableColumn("userId");
+//		tableViewerDescription.addEditableColumn("password");
+//		getDbc().bind(tableViewerDescription, new Property(catalog, "signons"),
+//				null);
+//		Signon firstSignon = (Signon) catalog.getSignons().get(0);
+//		// Verify the UI matches the model
+//		TableItem firstTableItem = tableViewer.getTable().getItem(0);
+//		assertEquals(firstTableItem.getText(1), firstSignon.getPassword());
+//		// Change the model and ensure the UI refreshes
+//		firstSignon.setPassword("Eclipse123Rocks");
+//		assertEquals("Eclipse123Rocks", firstSignon.getPassword());
+//		assertEquals(firstTableItem.getText(1), firstSignon.getPassword());
+//		// Change the GUI and ensure the model refreshes
+//		tableViewer.editElement(firstSignon, 1);
+//		CellEditor[] cellEditors = tableViewer.getCellEditors();
+//		TextCellEditor passwordEditor = (TextCellEditor) cellEditors[1];
+//		enterText((Text) passwordEditor.getControl(), "Cricket11Players");
+//		assertEquals("Cricket11Players", firstSignon.getPassword());
+//
+	}
+
+	public void testScenario09() {
+//		// Verify that nested properties work. Catalog has adventures. Adventure
+//		// has defaultLodging. Loding has name.
+//		TableViewerDescription tableViewerDescription = new TableViewerDescription(
+//				tableViewer);
+//		tableViewerDescription.addColumn("name");
+//		tableViewerDescription.addColumn("defaultLodging.name");
+//		getDbc().bind(tableViewerDescription,
+//				new Property(category, "adventures"), null);
+	}
+	/**
+	 * public void testScenario10(){ // Verify that for TIME_EARLY updating
+	 * occurs on a per key basic for a TextCellEditor // Show that converters
+	 * work for table columns Account[] accounts = catalog.getAccounts();
+	 * Account firstAccount = accounts[0];
+	 * SampleData.getSWTObservableFactory().setUpdateTime(DataBindingContext.TIME_EARLY);
+	 * TableViewerDescription tableViewerDescription = new
+	 * TableViewerDescription(tableViewer);
+	 * tableViewerDescription.addEditableColumn("lastName");
+	 * tableViewerDescription.addColumn("lastName");
+	 * getDbc().bind(tableViewerDescription,new Property(catalog, "accounts"),
+	 * null); // Verify that the first account is shown in the first row with
+	 * the last name correctly
+	 * assertEquals(tableViewer.getTable().getItem(0).getData(),firstAccount);
+	 * assertEquals(tableViewer.getTable().getItem(0).getText(0),firstAccount.getLastName());
+	 * assertEquals(tableViewer.getTable().getItem(0).getText(1),firstAccount.getLastName()); //
+	 * Create a cell editor over the first column
+	 * tableViewer.editElement(firstAccount, 0); // Set the text property of the
+	 * cell editor which is now active over the "firstName" column CellEditor[]
+	 * cellEditors = tableViewer.getCellEditors(); TextCellEditor
+	 * lastNameCellEditor = (TextCellEditor) cellEditors[0];
+	 * ((Text)lastNameCellEditor.getControl()).setText("E"); // Verify that the
+	 * key press goes to the model assertEquals(firstAccount.getLastName(),"E"); }
+	 */
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/PropertyScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/PropertyScenarios.java
new file mode 100644
index 0000000..44bfcd7
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/PropertyScenarios.java
@@ -0,0 +1,577 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 159768
+ *     Matthew Hall - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Locale;
+
+import org.eclipse.core.databinding.AggregateValidationStatus;
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.conversion.Converter;
+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.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+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.conversion.IdentityConverter;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Account;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.Cart;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.widgets.Text;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class PropertyScenarios extends ScenariosTestCase {
+
+    private Adventure adventure;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        // do any setup work here
+        adventure = SampleData.WINTER_HOLIDAY;
+    }
+
+    protected void tearDown() throws Exception {
+        // do any teardown work here
+        super.tearDown();
+    }
+
+    public void testEnterText() {
+        // just to make sure enterText() generates a FocusOut event.
+        Text text = new Text(getComposite(), SWT.BORDER);
+        final boolean[] focusLostHolder = { false };
+        text.addFocusListener(new FocusListener() {
+
+            public void focusGained(FocusEvent e) {
+                // only interested in focus lost events
+            }
+
+            public void focusLost(FocusEvent e) {
+                focusLostHolder[0] = true;
+            }
+        });
+        enterText(text, "hallo");
+        assertTrue(focusLostHolder[0]);
+    }
+
+    public void testScenario01() {
+        Text text = new Text(getComposite(), SWT.BORDER);
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+                BeansObservables.observeValue(adventure, "name"));
+
+        // getDbc().bind(text, new Property(adventure, "name"), null);
+        // uncomment the following line to see what's happening
+        // spinEventLoop(1);
+        assertEquals(adventure.getName(), text.getText());
+        enterText(text, "foobar");
+        // uncomment the following line to see what's happening
+        // spinEventLoop(1);
+        assertEquals("foobar", adventure.getName());
+        adventure.setName("barfoo");
+        // uncomment the following line to see what's happening
+        // spinEventLoop(1);
+        assertEquals("barfoo", text.getText());
+    }
+
+    public void testScenario02() {
+        // Binding the name property of an Adventure object to the contents of
+        // Text controls, no conversion, no validation. The Text widget editable
+        // is set to false.by the developer (can not change the name)
+        Text text = new Text(getComposite(), SWT.READ_ONLY);
+
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.None),
+                BeansObservables.observeValue(adventure, "name"));
+        assertEquals(adventure.getName(), text.getText());
+    }
+
+    public void testScenario03() {
+        // Binding of a read-only property of an Adventure object to the
+        // contents of Text controls, no conversion, no validation. Text control
+        // is not editable as a side effect of binding to a read-only property..
+        Cart cart = SampleData.CART;
+        cart.setAdventureDays(42);
+        // bind to the lodgingDays feature, which is read-only and always one
+        // less than the number of adventure days.
+        Text text = new Text(getComposite(), SWT.BORDER);
+
+        System.out.println("Expecting message about not being able to attach a listener");
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+                BeansObservables.observeValue(cart, "lodgingDays"));
+
+        assertEquals(new Integer(cart.getLodgingDays()).toString(), text.getText());
+    }
+
+    public void testScenario04() {
+        // Binding a nested property of an Adventure object to the content of a
+        // Text control, no conversion, no validation.
+        Text text = new Text(getComposite(), SWT.BORDER);
+        // TODO Scenario needs to be more specific - I'm binding to the default
+        // lodging's description of an adventure. What do we expect to happen
+        // when the default lodging changes? If we expect no change, then this
+        // scenario does not introduce anything new. If we expect the binding to
+        // be to the new default lodging's description, shouldn't we move this
+        // scenario to the master/detail section? I'm assuming the latter for
+        // now.
+
+        IObservableValue defaultLodging = BeansObservables.observeDetailValue(
+        		BeansObservables.observeValue(adventure, "defaultLodging"),
+        		"description", String.class); 
+
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify), defaultLodging);
+
+        // test changing the description
+        assertEquals(adventure.getDefaultLodging().getDescription(), text.getText());
+        enterText(text, "foobar");
+        assertEquals("foobar", adventure.getDefaultLodging().getDescription());
+        adventure.getDefaultLodging().setDescription("barfoo");
+        assertEquals(adventure.getDefaultLodging().getDescription(), text.getText());
+
+        // test changing the default lodging
+        adventure.setDefaultLodging(SampleData.CAMP_GROUND);
+        assertEquals(adventure.getDefaultLodging().getDescription(), text.getText());
+        adventure.getDefaultLodging().setDescription("barfo");
+        assertEquals(adventure.getDefaultLodging().getDescription(), text.getText());
+
+        adventure.setDefaultLodging(null);
+        assertEquals("", text.getText());
+
+        adventure.setDefaultLodging(SampleData.FIVE_STAR_HOTEL);
+        assertEquals(adventure.getDefaultLodging().getDescription(), text.getText());
+        adventure.getDefaultLodging().setDescription("barf");
+        assertEquals(adventure.getDefaultLodging().getDescription(), text.getText());
+
+    }
+
+    public void testScenario05() {
+        // Binding the name property of an Adventure object to the contents of
+        // Text controls where conversion occurs � the model data is held all
+        // in
+        // uppercase and displayed in lowercase with the first letter
+        // capitalized.
+        Text text = new Text(getComposite(), SWT.BORDER);
+        adventure.setName("UPPERCASE");
+
+        IConverter converter1 = new IConverter() {
+            public Object getFromType() {
+                return String.class;
+            }
+
+            public Object getToType() {
+                return String.class;
+            }
+
+            public Object convert(Object toObject) {
+                String modelValue = (String) toObject;
+                if (modelValue == null || modelValue.equals("")) {
+                    return modelValue;
+                }
+                String firstChar = modelValue.substring(0, 1);
+                String remainingChars = modelValue.substring(1);
+                return firstChar.toUpperCase() + remainingChars.toLowerCase();
+            }
+        };
+        IConverter converter2 = new IConverter() {
+            public Object getFromType() {
+                return String.class;
+            }
+
+            public Object getToType() {
+                return String.class;
+            }
+
+            public Object convert(Object fromObject) {
+                return ((String) fromObject).toUpperCase();
+            }
+        };
+
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+                BeansObservables.observeValue(adventure, "name"),
+                new UpdateValueStrategy().setConverter(converter2), new UpdateValueStrategy().setConverter(converter1));
+
+        // spinEventLoop(1);
+        assertEquals("Uppercase", text.getText());
+        enterText(text, "lowercase");
+        // spinEventLoop(1);
+        // TODO If we wanted to "canonicalize" the value in the text field, how
+        // could we do that?
+        assertEquals("LOWERCASE", adventure.getName());
+    }
+
+    public void testScenario06() {
+        // Binding the name property of an Adventure object to the contents of
+        // Text controls where validation occurs and the name cannot be longer
+        // than 15 characters and cannot contain spaces
+        Text text = new Text(getComposite(), SWT.BORDER);
+        final String noSpacesMessage = "Name must not contain spaces.";
+        final String max15CharactersMessage = "Maximum length for name is 15 characters.";
+        adventure.setName("ValidValue");
+
+        IValidator validator = new IValidator() {
+            public IStatus validate(Object value) {
+                String stringValue = (String) value;
+                if (stringValue.length() > 15) {
+                    return ValidationStatus.error(max15CharactersMessage);
+                } else if (stringValue.indexOf(' ') != -1) {
+                    return ValidationStatus.cancel(noSpacesMessage);
+                } else {
+                    return Status.OK_STATUS;
+                }
+            }
+        };
+
+//        BindSpec bindSpec = new DefaultBindSpec().setModelToTargetConverter(new IdentityConverter(String.class))
+//                .setTargetToModelConverter(new IdentityConverter(String.class))
+//                .addTargetValidator(BindingEvent.PIPELINE_VALUE_CHANGING, validator);
+
+        Binding binding = getDbc().bindValue(
+				SWTObservables.observeText(text, SWT.Modify),
+				BeansObservables.observeValue(adventure, "name"),
+				new UpdateValueStrategy().setConverter(new IdentityConverter(
+						String.class)).setAfterGetValidator(validator),
+				new UpdateValueStrategy().setConverter(new IdentityConverter(
+						String.class)));
+
+        // no validation message
+        assertTrue(((IStatus)binding.getValidationStatus().getValue()).isOK());
+        enterText(text, "Invalid Value");
+        assertEquals(noSpacesMessage, ((IStatus) binding.getValidationStatus().getValue()).getMessage());
+        assertEquals("ValidValue", adventure.getName());
+        text.setText("InvalidValueBecauseTooLong");
+        assertEquals(max15CharactersMessage,
+                ((IStatus) binding.getValidationStatus().getValue()).getMessage());
+        assertEquals("ValidValue", adventure.getName());
+        enterText(text, "anothervalid");
+        assertTrue(((IStatus)binding.getValidationStatus().getValue()).isOK());
+        assertEquals("anothervalid", adventure.getName());
+    }
+
+    public void testScenario07() {
+        // Binding the price property of an Adventure to a Text control. Price
+        // is a double and Text accepts String so conversion will have to occur.
+        // Validation ensure that the value is positive
+        Text text = new Text(getComposite(), SWT.BORDER);
+        adventure.setPrice(5.0);
+        final String cannotBeNegativeMessage = "Price cannot be negative.";
+        final String mustBeCurrencyMessage = "Price must be a currency.";
+
+        IValidator validator = new IValidator() {
+            public IStatus validate(Object value) {
+                String stringValue = (String) value;
+                try {
+                    double doubleValue = new Double(stringValue).doubleValue();
+                    if (doubleValue < 0.0) {
+                        return ValidationStatus.error(cannotBeNegativeMessage);
+                    }
+                    return Status.OK_STATUS;
+                } catch (NumberFormatException ex) {
+                    return ValidationStatus.error(mustBeCurrencyMessage);
+                }
+            }
+        };
+        
+        //Create a number formatter that will display one decimal position.
+		NumberFormat numberFormat = NumberFormat.getInstance();
+		numberFormat.setMinimumFractionDigits(1);
+
+		IConverter targetToModelConverter = StringToNumberConverter.toDouble(
+				numberFormat, true);
+		IConverter modelToTargetConverter = NumberToStringConverter.fromDouble(
+				numberFormat, true);
+
+		getDbc().bindValue(
+				SWTObservables.observeText(text, SWT.Modify),
+				BeansObservables.observeValue(adventure, "price"),
+				new UpdateValueStrategy().setAfterGetValidator(validator)
+						.setConverter(targetToModelConverter),
+				new UpdateValueStrategy().setConverter(modelToTargetConverter));
+
+		String expected = numberFormat.format(adventure.getPrice());
+        assertEquals(expected, text.getText());
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+        
+        String toEnter = numberFormat.format(0.65);
+        enterText(text, toEnter);
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+        assertEquals(0.65, adventure.getPrice(), 0.0001);
+        
+        adventure.setPrice(42.24);
+        expected = numberFormat.format(adventure.getPrice());
+        assertEquals(expected, text.getText());
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+        
+        enterText(text, "jygt");
+        assertEquals(mustBeCurrencyMessage, AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).getMessage());
+        
+        toEnter = numberFormat.format(-23.9);
+        enterText(text, toEnter);
+        assertEquals(cannotBeNegativeMessage, AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).getMessage());
+        assertEquals(42.24, adventure.getPrice(), 0.0001);
+        
+        adventure.setPrice(0.0);
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+    }
+
+    public void testScenario08() {
+        // Binding the price property of an Adventure to a Text control but with
+        // custom conversion � the double will be validated to only have two
+        // decimal places and displayed with a leading currency symbol, and can
+        // be entered with or without the currency symbol.
+        Text text = new Text(getComposite(), SWT.BORDER);
+        adventure.setPrice(5.0);
+        final String cannotBeNegativeMessage = "Price cannot be negative.";
+        final String mustBeCurrencyMessage = "Price must be a currency.";
+        final NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.CANADA);
+
+        IConverter toCurrency = new Converter(double.class, String.class) {
+            public Object convert(Object toObject) {
+                return currencyFormat.format(((Double) toObject).doubleValue());
+            }
+        };
+
+        IConverter toDouble = new Converter(String.class, double.class) {
+            public Object convert(Object fromObject) {
+                try {
+                    return new Double(currencyFormat.parse((String) fromObject).doubleValue());
+                } catch (ParseException e) {
+                    // TODO throw something like
+                    // IllegalConversionException?
+                    return new Double(0);
+                }
+            }
+        };
+
+        IValidator validator = new IValidator() {
+            public IStatus validate(Object value) {
+                String stringValue = (String) value;
+                try {
+                    double doubleValue = currencyFormat.parse(stringValue).doubleValue();
+                    if (doubleValue < 0.0) {
+                        return ValidationStatus.error(cannotBeNegativeMessage);
+                    }
+                    return Status.OK_STATUS;
+                } catch (ParseException e) {
+                    return ValidationStatus.error(mustBeCurrencyMessage);
+                }
+            }
+        };
+
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+                BeansObservables.observeValue(adventure, "price"),
+new UpdateValueStrategy().setConverter(toDouble).setAfterGetValidator(validator),new UpdateValueStrategy().setConverter(toCurrency));
+
+        String expected = currencyFormat.format(5);
+        assertEquals(expected, text.getText());
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+        
+        String toEnter = currencyFormat.format(0.65);
+        enterText(text, toEnter);
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+        assertEquals(0.65, adventure.getPrice(), 0.0001);
+        
+        adventure.setPrice(42.24);
+        expected = currencyFormat.format(adventure.getPrice());
+        assertEquals(expected, text.getText());
+        
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+        enterText(text, "jygt");
+        assertEquals(mustBeCurrencyMessage, AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).getMessage());
+        
+        toEnter = currencyFormat.format(-23.9);
+        enterText(text, toEnter);
+        
+        assertEquals(cannotBeNegativeMessage, AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).getMessage());
+        assertEquals(42.24, adventure.getPrice(), 0.0001);
+        adventure.setPrice(0.0);
+        assertTrue(AggregateValidationStatus.getStatusMaxSeverity(getDbc().getBindings()).isOK());
+    }
+
+    public void testScenario09() {
+        // Binding a boolean property to a CheckBox. Adventure will have a
+        // Boolean property �petsAllowed�
+        Button checkbox = new Button(getComposite(), SWT.CHECK);
+        // checkbox.setText("Pets allowed");
+        // checkbox.setLayoutData(new GridData(SWT.LEFT,SWT.TOP, false,false));
+        adventure.setPetsAllowed(true);
+
+        getDbc().bindValue(SWTObservables.observeSelection(checkbox),
+                BeansObservables.observeValue(adventure, "petsAllowed"));
+
+        assertEquals(true, checkbox.getSelection());
+        setButtonSelectionWithEvents(checkbox, false);
+        assertEquals(false, adventure.isPetsAllowed());
+        adventure.setPetsAllowed(true);
+        assertEquals(true, checkbox.getSelection());
+    }
+
+    public void testScenario10() {
+        // Binding a Transportation departure time to a Text control that
+        // formats and validates the time to and from a String. There are
+        // property bindings that bind elements of the GUI to elements to GUI
+        // and also elements of the domain to elements of the domain
+        // TODO fail("not implemented");
+    }
+
+    public void testScenario11() {
+        // Binding the max value of a spinner to another spinner.
+        Spinner spinner1 = new Spinner(getComposite(), SWT.NONE);
+        spinner1.setSelection(10);
+        spinner1.setMinimum(1);
+        spinner1.setMaximum(100);
+        Spinner spinner2 = new Spinner(getComposite(), SWT.NONE);
+        spinner2.setMaximum(1);
+
+        getDbc().bindValue(SWTObservables.observeSelection(spinner1), SWTObservables.observeMax(spinner2));
+
+        assertEquals(1, spinner1.getSelection());
+        spinner1.setSelection(10);
+        spinner1.notifyListeners(SWT.Modify, new Event());
+        assertEquals(10, spinner2.getMaximum());
+    }
+
+    public void testScenario12() {
+        // Binding the enabled state of several Text controls to a check box.
+        // There will be two check boxes, so as each is enabled/disabled the
+        // other one follows as do the states of the Text controls.
+        Button checkbox1 = new Button(getComposite(), SWT.CHECK);
+        checkbox1.setSelection(false);
+        Button checkbox2 = new Button(getComposite(), SWT.CHECK);
+        checkbox2.setSelection(false);
+        Text text1 = new Text(getComposite(), SWT.NONE);
+        Text text2 = new Text(getComposite(), SWT.NONE);
+
+        IObservableValue checkbox1Selected = SWTObservables.observeSelection(checkbox1);
+        IObservableValue checkbox2Selected = SWTObservables.observeSelection(checkbox2);
+
+        // bind the two checkboxes so that if one is checked, the other is not
+        // and vice versa.
+        Converter negatingConverter = new Converter(boolean.class, boolean.class) {
+            private Boolean negated(Boolean booleanObject) {
+                return new Boolean(!booleanObject.booleanValue());
+            }
+
+            public Object convert(Object targetObject) {
+                return negated((Boolean) targetObject);
+            }
+        };
+
+        getDbc().bindValue(checkbox1Selected,
+                checkbox2Selected,new UpdateValueStrategy().setConverter(negatingConverter),
+                new UpdateValueStrategy().setConverter(negatingConverter));
+        
+        // bind the enabled state of the two text widgets to one of the
+        // checkboxes each.
+        
+        getDbc().bindValue(SWTObservables.observeEnabled(text1), checkbox1Selected);
+        getDbc().bindValue(SWTObservables.observeEnabled(text2), checkbox2Selected);
+                
+        assertEquals(true, text1.getEnabled());
+        assertEquals(false, text2.getEnabled());
+        assertEquals(true, checkbox1.getSelection());
+        setButtonSelectionWithEvents(checkbox1, false);
+        assertEquals(false, text1.getEnabled());
+        assertEquals(true, text2.getEnabled());
+        assertEquals(true, checkbox2.getSelection());
+        setButtonSelectionWithEvents(checkbox2, false);
+        assertEquals(true, text1.getEnabled());
+        assertEquals(false, text2.getEnabled());
+        assertEquals(true, checkbox1.getSelection());
+    }
+
+    public void testScenario13() {
+        Text text = new Text(getComposite(), SWT.BORDER);
+        
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.FocusOut), BeansObservables.observeValue(adventure, "name"));
+
+        // uncomment the following line to see what's happening
+        // happening
+        // spinEventLoop(1);
+        String adventureName = adventure.getName();
+        assertEquals(adventureName, text.getText());
+        enterText(text, "foobar");
+        // uncomment the following line to see what's happening
+        // spinEventLoop(1);
+        assertEquals("foobar", adventure.getName());
+        adventure.setName("barfoo");
+        // uncomment the following line to see what's happening
+        // spinEventLoop(1);
+        assertEquals("barfoo", text.getText());
+    }
+
+    public void testScenario14() {
+        Text t1 = new Text(getComposite(), SWT.BORDER);
+        Text t2 = new Text(getComposite(), SWT.BORDER);
+  
+        getDbc().bindValue(SWTObservables.observeText(t1, SWT.Modify), BeansObservables.observeValue(adventure, "name"));
+        getDbc().bindValue(SWTObservables.observeText(t2, SWT.Modify), BeansObservables.observeValue(adventure, "name"));
+        
+        final int[] counter = { 0 };
+        
+        IObservableValue uv = BeansObservables.observeValue(adventure, "name");
+        
+        uv.addChangeListener(new IChangeListener() {
+            public void handleChange(ChangeEvent event) {
+                // Count how many times adventure has changed
+                counter[0]++;
+            }
+        });
+
+        String name = adventure.getName() + "Foo";
+        enterText(t1, name);
+        assertEquals(name, adventure.getName());
+        assertEquals(name, t2.getText());
+        assertTrue(counter[0] == 1);
+
+        name = name + "Bar";
+        uv.setValue(name);
+        assertEquals(t1.getText(), adventure.getName());
+        assertEquals(2, counter[0]);
+
+    }
+
+    public void testScenario15() {
+        Text text = new Text(getComposite(), SWT.NONE);
+        Account account = new Account();
+        account.setExpiryDate(new Date());
+        
+        Binding b = getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify), BeansObservables.observeValue(account, "expiryDate"));
+        Text errorText = new Text(getComposite(), SWT.NONE);
+        
+        getDbc().bindValue(SWTObservables.observeText(errorText, SWT.Modify), b.getValidationStatus(), new UpdateValueStrategy(false, UpdateValueStrategy.POLICY_NEVER), null);
+        assertTrue(((IStatus)b.getValidationStatus().getValue()).isOK());
+        enterText(text, "foo");
+        assertFalse(((IStatus)b.getValidationStatus().getValue()).isOK());
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ScenariosTestCase.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ScenariosTestCase.java
new file mode 100644
index 0000000..b55c59b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/ScenariosTestCase.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * 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 116920
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Abstract base class of the JFace binding scenario test classes.
+ */
+abstract public class ScenariosTestCase extends TestCase {
+
+	private Composite composite;
+	private DataBindingContext dbc;
+	protected Display display;
+	private boolean disposeDisplay = false;
+	protected Shell shell;
+	protected Text dummyText;
+	protected Realm realm;
+
+	protected Composite getComposite() {
+		return composite;
+	}
+
+	protected DataBindingContext getDbc() {
+		return dbc;
+	}
+
+	public Shell getShell() {
+		if (shell != null) {
+			return shell;
+		}
+		Shell result = BindingScenariosTestSuite.getShell();
+		if (result == null) {
+			display = Display.getDefault();
+			if (Display.getDefault() == null) {
+				display = new Display();
+				disposeDisplay = true;
+			}
+			shell = new Shell(display, SWT.SHELL_TRIM);
+			shell.setLayout(new FillLayout());
+			result = shell;
+		}
+		result.setText(getName()); // In the case that the shell() becomes
+		// visible.
+		return result;
+	}
+
+	protected void spinEventLoop(int secondsToWaitWithNoEvents) {
+		if (!composite.isVisible() && secondsToWaitWithNoEvents > 0) {
+			composite.getShell().open();
+		}
+		while (composite.getDisplay().readAndDispatch()) {
+			// do nothing, just process events
+		}
+		try {
+			Thread.sleep(secondsToWaitWithNoEvents * 1000);
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
+	}
+
+	protected void interact() {
+		if (!getShell().isVisible()) {
+			getShell().open();
+		}
+		while (!getShell().isDisposed()) {
+			if (!getShell().getDisplay().readAndDispatch()) {
+				getShell().getDisplay().sleep();
+			}
+		}
+	}
+
+	protected void setButtonSelectionWithEvents(Button button, boolean value) {
+		if (button.getSelection() == value) {
+			return;
+		}
+		button.setSelection(value);
+		pushButtonWithEvents(button);
+	}
+
+	protected void pushButtonWithEvents(Button button) {
+		button.notifyListeners(SWT.Selection, null);
+	}
+
+	protected void setUp() throws Exception {
+		realm = SWTObservables.getRealm(Display.getDefault());
+		RealmTester.setDefault(realm);
+
+		composite = new Composite(getShell(), SWT.NONE);
+		composite.setLayout(new FillLayout());
+		SampleData.initializeData(); // test may manipulate the data... let
+		// all start from fresh
+		dbc = new DataBindingContext();
+		dummyText = new Text(getComposite(), SWT.NONE);
+		dummyText.setText("dummy");
+	}
+
+	protected void tearDown() throws Exception {
+		realm = null;
+		getShell().setVisible(false); // same Shell may be reUsed across tests
+		composite.dispose();
+		composite = null;
+		if (shell != null) {
+			shell.close();
+			shell.dispose();
+		} else
+			dbc.dispose();
+		if (display != null && disposeDisplay) {
+			display.dispose();
+		}
+	}
+
+	protected void enterText(Text text, String string) {
+		text.notifyListeners(SWT.FocusIn, null);
+		text.setText(string);
+		text.notifyListeners(SWT.FocusOut, null);
+	}
+
+	protected void assertArrayEquals(Object[] expected, Object[] actual) {
+		assertEquals(Arrays.asList(expected), Arrays.asList(actual));
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/SpinnerControlScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/SpinnerControlScenario.java
new file mode 100644
index 0000000..2e65b78
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/SpinnerControlScenario.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class SpinnerControlScenario extends ScenariosTestCase {
+
+    private Adventure adventure;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        // do any setup work here
+        adventure = SampleData.WINTER_HOLIDAY;
+    }
+
+    protected void tearDown() throws Exception {
+        // do any teardown work here
+        super.tearDown();
+    }
+
+    public void testScenario01() {
+        // Bind the adventure "maxNumberOfPeople" property to a spinner
+        // Change the UI and verify the model changes
+        // Change the model and verify the UI changes
+        Spinner spinner = new Spinner(getComposite(), SWT.BORDER);
+        getDbc().bindValue(SWTObservables.observeSelection(spinner),
+                BeansObservables.observeValue(adventure, "maxNumberOfPeople"));
+
+        assertEquals(adventure.getMaxNumberOfPeople(), spinner.getSelection());
+        // Verify the model is updated when the GUI is changed
+        spinner.setSelection(5);
+        assertEquals(5, adventure.getMaxNumberOfPeople());
+        // Verify the GUI is updated when the model changes
+        adventure.setMaxNumberOfPeople(7);
+        assertEquals(7, spinner.getSelection());
+        adventure.setMaxNumberOfPeople(11);
+        spinEventLoop(0);
+        assertEquals(11, spinner.getSelection());
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/TableScenarios.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/TableScenarios.java
new file mode 100644
index 0000000..7bb4a2b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/TableScenarios.java
@@ -0,0 +1,447 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 160000
+ *     Matthew Hall - bug 260337
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.examples.databinding.model.Account;
+import org.eclipse.jface.examples.databinding.model.Catalog;
+import org.eclipse.jface.examples.databinding.model.Category;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class TableScenarios extends ScenariosTestCase {
+
+	private TableViewer tableViewer;
+
+	private Catalog catalog;
+
+	Category category;
+
+	private TableColumn firstNameColumn;
+
+	private TableColumn lastNameColumn;
+
+	private TableColumn stateColumn;
+
+	Image[] images;
+
+	private TableColumn fancyColumn;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		getComposite().setLayout(new FillLayout());
+		tableViewer = new TableViewer(getComposite());
+		tableViewer.getTable().setLinesVisible(true);
+		tableViewer.getTable().setHeaderVisible(true);
+		firstNameColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		firstNameColumn.setWidth(50);
+		lastNameColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		lastNameColumn.setWidth(50);
+		stateColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		stateColumn.setWidth(50);
+		fancyColumn = new TableColumn(tableViewer.getTable(), SWT.NONE);
+		fancyColumn.setWidth(250);
+
+		catalog = SampleData.CATALOG_2005; // Lodging source
+		category = SampleData.WINTER_CATEGORY;
+
+		images = new Image[] {
+				getShell().getDisplay().getSystemImage(SWT.ICON_ERROR),
+				getShell().getDisplay().getSystemImage(SWT.ICON_WARNING),
+				getShell().getDisplay().getSystemImage(SWT.ICON_INFORMATION), };
+	}
+
+	protected void tearDown() throws Exception {
+		// do any teardown work here
+		super.tearDown();
+		tableViewer.getTable().dispose();
+		tableViewer = null;
+		firstNameColumn = null;
+		lastNameColumn = null;
+		stateColumn = null;
+	}
+
+	public void testScenario01() {
+		// Show that a TableViewer with three columns renders the accounts
+		Account[] accounts = catalog.getAccounts();
+
+		IObservableList list = new WritableList(new ArrayList(), Account.class);
+		list.addAll(Arrays.asList(accounts));
+
+		ViewerSupport.bind(tableViewer, list, BeanProperties.values(
+				Account.class,
+				new String[] { "firstName", "lastName", "state" }));
+
+		Table table = tableViewer.getTable();
+
+		// Verify the data in the table columns matches the accounts
+		for (int i = 0; i < accounts.length; i++) {
+			Account account = catalog.getAccounts()[i];
+			TableItem item = table.getItem(i);
+
+			assertEquals(account.getFirstName(), item.getText(0));
+			assertEquals(account.getLastName(), item.getText(1));
+			assertEquals(account.getState(), item.getText(2));
+		}
+	}
+
+	public void testScenario02() throws SecurityException,
+			IllegalArgumentException {
+		// Show that a TableViewer with three columns can be used to update
+		// columns
+		// Account[] accounts = catalog.getAccounts();
+		//
+		// TableModelDescription tableModelDescription = new
+		// TableModelDescription(new Property(catalog, "accounts"), new
+		// String[]{"firstName","lastName","state"});
+		// // tableViewerDescription.addEditableColumn("firstName");
+		// // tableViewerDescription.addEditableColumn("lastName", null, null,
+		// new PhoneConverter());
+		// // tableViewerDescription.addEditableColumn("state", null, null, new
+		// StateConverter());
+		// getDbc().bind(tableViewer,
+		// tableModelDescription, null);
+		//
+		// Account account = accounts[0];
+		// // Select the first item in the table
+		// tableViewer.editElement(account, 0);
+		// // Set the text property of the cell editor which is now active over
+		// the "firstName" column
+		// CellEditor[] cellEditors = tableViewer.getCellEditors();
+		// TextCellEditor firstNameEditor = (TextCellEditor) cellEditors[0];
+		// // Change the firstName and test it goes to the model
+		// enterText((Text) firstNameEditor.getControl(), "Bill");
+		// // Check whether the model has changed
+		// assertEquals("Bill",account.getFirstName());
+	}
+
+	public void testScenario04() {
+		// // Show that when an item is added to a collection the table gets an
+		// extra item
+		// Account[] accounts = catalog.getAccounts();
+		//		
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addColumn("firstName");
+		// tableViewerDescription.addColumn("lastName");
+		// tableViewerDescription.addColumn("state");
+		// tableViewerDescription.addColumn(null,new IConverter(){
+		//			
+		// public Class getModelType() {
+		// return Account.class;
+		// }
+		//			
+		// public Class getTargetType() {
+		// return ViewerLabel.class;
+		// }
+		//			
+		// public Object convertTargetToModel(Object targetObject) {
+		// return null;
+		// }
+		//			
+		// public Object convertModelToTarget(Object modelObject) {
+		// Account account = (Account) modelObject;
+		// return new ViewerLabel(account.toString(), images[new
+		// Random().nextInt(images.length)]);
+		// }});
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "accounts"), null);
+		//
+		// //interact();
+		//		
+		// // Verify the number of accounts matches the number of items in the
+		// table
+		// assertEquals(tableViewer.getTable().getItemCount(),accounts.length);
+		// // Add a new account and verify that the number of items in the table
+		// increases
+		// Account newAccount = new Account();
+		// newAccount.setFirstName("Finbar");
+		// newAccount.setLastName("McGoo");
+		// newAccount.setLastName("NC");
+		// catalog.addAccount(newAccount);
+		// // The number of items should have gone up by one
+		// assertEquals(tableViewer.getTable().getItemCount(),accounts.length +
+		// 1);
+		// // The number of items should still match the number of accounts
+		// (i.e. test the model)
+		// assertEquals(tableViewer.getTable().getItemCount(),catalog.getAccounts().length);
+		// // Remove the account that was just added
+		// catalog.removeAccount(newAccount);
+		// // The number of items should match the original
+		// assertEquals(tableViewer.getTable().getItemCount(),accounts.length);
+		// // The number of items should still match the number of accounts
+		// (i.e. test the model is reset)
+		// assertEquals(tableViewer.getTable().getItemCount(),catalog.getAccounts().length);
+		//		
+		// // Test adding and removing to the model on a non UI thread
+		// int numberOfAccounts = catalog.getAccounts().length;
+		// final Account barney = new Account();
+		// barney.setFirstName("Barney");
+		// barney.setLastName("Smith");
+		// barney.setLastName("CA");
+		// invokeNonUI(new Runnable(){
+		// public void run(){
+		// catalog.addAccount(barney);
+		// }
+		// });
+		// spinEventLoop(0);
+		// // The number of items should have gone up by one
+		// assertEquals(tableViewer.getTable().getItemCount(),numberOfAccounts +
+		// 1);
+		//		
+		// invokeNonUI(new Runnable(){
+		// public void run(){
+		// catalog.removeAccount(barney);
+		// }
+		// });
+		// spinEventLoop(0);
+		// // The number of items should have reverted to the original number
+		// before barney was added and removed
+		// assertEquals(tableViewer.getTable().getItemCount(),numberOfAccounts);
+		//		
+	}
+
+	public void testScenario03() {
+		// // Show that converters work for table columns
+		// Account[] accounts = catalog.getAccounts();
+		//
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addEditableColumn("lastName");
+		// tableViewerDescription.addEditableColumn("phone", null, null ,
+		// new PhoneConverter());
+		// tableViewerDescription.addEditableColumn("state", null, null ,
+		// new StateConverter());
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "accounts"), null);
+		//
+		// // Verify that the data in the the table columns matches the expected
+		// // What we are looking for is that the phone numbers are converterted
+		// to
+		// // nnn-nnn-nnnn and that
+		// // the state letters are converted to state names
+		// // Verify the data in the table columns matches the accounts
+		// PhoneConverter phoneConverter = new PhoneConverter();
+		// StateConverter stateConverter = new StateConverter();
+		// for (int i = 0; i < accounts.length; i++) {
+		// Account account = catalog.getAccounts()[i];
+		// // Check the phone number
+		// String col_phone = ((ITableLabelProvider) tableViewer
+		// .getLabelProvider()).getColumnText(account, 1);
+		// assertEquals(getValue((String)phoneConverter
+		// .convertModelToTarget(account.getPhone())), col_phone);
+		// String col_state = ((ITableLabelProvider) tableViewer
+		// .getLabelProvider()).getColumnText(account, 2);
+		// assertEquals(getValue((String)stateConverter
+		// .convertModelToTarget(account.getState())), col_state);
+		// }
+	}
+
+	public void testScenario05() {
+		// // Show that when the model changes then the UI refreshes to reflect
+		// this
+		//
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addColumn("lastName");
+		// tableViewerDescription.addColumn("phone",
+		// new PhoneConverter());
+		// tableViewerDescription.addColumn("state",
+		// new StateConverter());
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "accounts"), null);
+		//		
+		// final Account account = catalog.getAccounts()[0];
+		// String lastName = tableViewer.getTable().getItem(0).getText(0);
+		// // Check the firstName in the TableItem is the same as the model
+		// assertEquals(lastName,account.getLastName());
+		// // Now change the model and check again
+		// account.setLastName("Gershwin");
+		// lastName = tableViewer.getTable().getItem(0).getText(0);
+		// assertEquals(lastName,account.getLastName());
+		//		
+		// // Test the model update on a non UI thread
+		// invokeNonUI(new Runnable(){
+		// public void run(){
+		// account.setLastName("Mozart");
+		// }
+		// });
+		// spinEventLoop(0);
+		// lastName = tableViewer.getTable().getItem(0).getText(0);
+		// assertEquals(lastName,account.getLastName());
+		//		
+	}
+
+	public void testScenario06() {
+		// // Check that explicit type means that defaulting of converters works
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addEditableColumn("price");
+		// tableViewerDescription.getColumn(0).setPropertyType(Double.TYPE);
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "transporations"), null);
+		// Transportation transporation = catalog.getTransporations()[0];
+		// tableViewer.editElement(transporation, 0);
+		// // Set the text property of the cell editor which is now active over
+		// the "firstName" column
+		// CellEditor[] cellEditors = tableViewer.getCellEditors();
+		// TextCellEditor priceEditor = (TextCellEditor) cellEditors[0];
+		// // Change the firstName and test it goes to the model
+		// enterText((Text) priceEditor.getControl(), "123.45");
+		// // Verify the model is updated
+		// assertEquals(transporation.getPrice(),123.45,0);
+
+	}
+
+	public void testScenario07() {
+		// // Verify that even when a column's property type is not set, that it
+		// is worked out lazily from the target type
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addEditableColumn("price");
+		// // The column's type is not set to be Double.TYPE. This will be
+		// inferred once the first Transportation object is set
+		// // into the ObservableCollection
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "transporations"), null);
+		// Transportation transporation = catalog.getTransporations()[0];
+		// tableViewer.editElement(transporation, 0);
+		// // Set the text property of the cell editor which is now active over
+		// the "firstName" column
+		// CellEditor[] cellEditors = tableViewer.getCellEditors();
+		// TextCellEditor priceEditor = (TextCellEditor) cellEditors[0];
+		// // Change the firstName and test it goes to the model
+		// enterText((Text) priceEditor.getControl(), "123.45");
+		// // Verify the model is updated
+		// assertEquals(transporation.getPrice(),123.45,0);
+		//		
+	}
+
+	public void testScenario08_00() {
+		// // Verify that binding to a Collection property (rather than an
+		// array) works when specifying data type
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addEditableColumn("userId");
+		// tableViewerDescription.addEditableColumn("password");
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "signons", Signon.class, null), null);
+		// Signon firstSignon = (Signon) catalog.getSignons().get(0);
+		// // Verify the UI matches the model
+		// TableItem firstTableItem = tableViewer.getTable().getItem(0);
+		// assertEquals(firstTableItem.getText(1),firstSignon.getPassword());
+		// // Change the model and ensure the UI refreshes
+		// firstSignon.setPassword("Eclipse123Rocks");
+		// assertEquals("Eclipse123Rocks",firstSignon.getPassword());
+		// assertEquals(firstTableItem.getText(1),firstSignon.getPassword());
+		// // Change the GUI and ensure the model refreshes
+		// tableViewer.editElement(firstSignon, 1);
+		// CellEditor[] cellEditors = tableViewer.getCellEditors();
+		// TextCellEditor passwordEditor = (TextCellEditor) cellEditors[1];
+		// enterText((Text) passwordEditor.getControl(), "Cricket11Players");
+		// assertEquals("Cricket11Players",firstSignon.getPassword());
+		//		
+	}
+
+	public void testScenario08_01() {
+		// // Verify that binding to a Collection property (rather than an
+		// array) works without specifying data type
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(
+		// tableViewer);
+		// tableViewerDescription.addEditableColumn("userId");
+		// tableViewerDescription.addEditableColumn("password");
+		// getDbc().bind(tableViewerDescription,
+		// new Property(catalog, "signons"), null);
+		// Signon firstSignon = (Signon) catalog.getSignons().get(0);
+		// // Verify the UI matches the model
+		// TableItem firstTableItem = tableViewer.getTable().getItem(0);
+		// assertEquals(firstTableItem.getText(1),firstSignon.getPassword());
+		// // Change the model and ensure the UI refreshes
+		// firstSignon.setPassword("Eclipse123Rocks");
+		// assertEquals("Eclipse123Rocks",firstSignon.getPassword());
+		// assertEquals(firstTableItem.getText(1),firstSignon.getPassword());
+		// // Change the GUI and ensure the model refreshes
+		// tableViewer.editElement(firstSignon, 1);
+		// CellEditor[] cellEditors = tableViewer.getCellEditors();
+		// TextCellEditor passwordEditor = (TextCellEditor) cellEditors[1];
+		// enterText((Text) passwordEditor.getControl(), "Cricket11Players");
+		// assertEquals("Cricket11Players",firstSignon.getPassword());
+		//		
+	}
+
+	public void testScenario09() {
+		// // Verify that nested properties work. Catalog has adventures.
+		// Adventure has defaultLodging. Loding has name.
+		// TableViewerDescription tableViewerDescription = new
+		// TableViewerDescription(tableViewer);
+		// tableViewerDescription.addColumn("name");
+		// tableViewerDescription.addColumn("defaultLodging.name");
+		// getDbc().bind(tableViewerDescription,new Property(category,
+		// "adventures"),null);
+		//		
+	}
+	/**
+	 * public void testScenario10(){ // Verify that for TIME_EARLY updating
+	 * occurs on a per key basic for a TextCellEditor // Show that converters
+	 * work for table columns Account[] accounts = catalog.getAccounts();
+	 * Account firstAccount = accounts[0];
+	 * SampleData.getSWTObservableFactory().setUpdateTime
+	 * (DataBindingContext.TIME_EARLY); TableViewerDescription
+	 * tableViewerDescription = new TableViewerDescription(tableViewer);
+	 * tableViewerDescription.addEditableColumn("lastName");
+	 * tableViewerDescription.addColumn("lastName");
+	 * getDbc().bind(tableViewerDescription,new Property(catalog, "accounts"),
+	 * null); // Verify that the first account is shown in the first row with
+	 * the last name correctly
+	 * assertEquals(tableViewer.getTable().getItem(0).getData(),firstAccount);
+	 * assertEquals
+	 * (tableViewer.getTable().getItem(0).getText(0),firstAccount.getLastName
+	 * ());
+	 * assertEquals(tableViewer.getTable().getItem(0).getText(1),firstAccount
+	 * .getLastName()); // Create a cell editor over the first column
+	 * tableViewer.editElement(firstAccount, 0); // Set the text property of the
+	 * cell editor which is now active over the "firstName" column CellEditor[]
+	 * cellEditors = tableViewer.getCellEditors(); TextCellEditor
+	 * lastNameCellEditor = (TextCellEditor) cellEditors[0];
+	 * ((Text)lastNameCellEditor.getControl()).setText("E"); // Verify that the
+	 * key press goes to the model assertEquals(firstAccount.getLastName(),"E");
+	 * }
+	 */
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/TextControlScenario.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/TextControlScenario.java
new file mode 100644
index 0000000..d054c0c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/scenarios/TextControlScenario.java
@@ -0,0 +1,309 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bug 260329
+ *******************************************************************************/
+package org.eclipse.jface.tests.databinding.scenarios;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.examples.databinding.model.Account;
+import org.eclipse.jface.examples.databinding.model.Adventure;
+import org.eclipse.jface.examples.databinding.model.SampleData;
+import org.eclipse.jface.examples.databinding.model.Transportation;
+import org.eclipse.jface.tests.databinding.BindingTestSuite;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * To run the tests in this class, right-click and select "Run As JUnit Plug-in
+ * Test". This will also start an Eclipse instance. To clean up the launch
+ * configuration, open up its "Main" tab and select "[No Application] - Headless
+ * Mode" as the application to run.
+ */
+
+public class TextControlScenario extends ScenariosTestCase {
+
+    private Text text;
+
+    private Adventure adventure;
+
+    private Transportation transportation;
+
+    Account account;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        // do any setup work here
+        adventure = SampleData.WINTER_HOLIDAY;
+        transportation = SampleData.EXECUTIVE_JET;
+        account = SampleData.PRESIDENT;
+        text = new Text(getComposite(), SWT.BORDER);
+    }
+
+    protected void tearDown() throws Exception {
+        text.dispose();
+        text = null;
+        super.tearDown();
+    }
+
+    public void testScenario01() {
+        // Bind the adventure "name" property to a text field
+        // Change the UI and verify the model changes
+        // Change the model and verify the UI changes
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+                BeansObservables.observeValue(adventure, "name"));
+
+        assertEquals(adventure.getName(), text.getText());
+        text.setText("England");
+        text.notifyListeners(SWT.FocusOut, null);
+        assertEquals("England", adventure.getName());
+        adventure.setName("France");
+        assertEquals("France", text.getText());
+        adventure.setName("Germany");
+        spinEventLoop(0);
+        assertEquals("Germany", text.getText());
+    }
+
+    public void testScenario02() {
+
+        // Bind the transportation "price" property to a text field
+        // This is a Double.TYPE so we check that conversion and validation
+        // occurs
+        // Change the UI and verify the model changes
+        // Change the model and verify the UI changes
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+                BeansObservables.observeValue(transportation, "price"));
+
+        NumberFormat numberFormat = NumberFormat.getInstance();
+        
+        assertEquals(numberFormat.format(transportation.getPrice()), text.getText());
+        text.setText("9876.54");
+        text.notifyListeners(SWT.FocusOut, null);
+        assertEquals(9876.54, transportation.getPrice(), 0);
+        
+        transportation.setPrice(1234.56);
+        assertEquals(numberFormat.format(transportation.getPrice()), text.getText());
+    }
+
+//    public void testScenario03() {
+//        // Show that the Escape key can be pressed in the middle of editing and
+//        // the value will revert
+//        // the updatePolicy for this test is TIME_LATE so it occurs when focus
+//        // is lost from the Text control
+//        getDbc().bindValue(SWTObservables.observeText(text, SWT.FocusOut),
+//                BeansObservables.observeValue(adventure, "name"),
+//                null, null);
+//
+//        String currentText = text.getText();
+//        text.setText("Switzerland");
+//        // We do not notify FocusOut
+//        // Verify that the model hasn't changed
+//        assertEquals(currentText, adventure.getName());
+//        Event event = new Event();
+//        event.character = SWT.ESC;
+//        event.keyCode = 27;
+//        text.notifyListeners(SWT.KeyDown, event);
+//        // Verify that the text has reverted
+//        assertEquals(currentText, text.getText());
+//        // And that the model didn't change
+//        assertEquals(adventure.getName(), currentText);
+//        // Now change the GUI and commit this change
+//        currentText = "Austria";
+//        text.setText(currentText);
+//        text.notifyListeners(SWT.FocusOut, null);
+//        assertEquals(text.getText(), adventure.getName());
+//        // Now change the text again and press escape a second time
+//        text.setText("Turkey");
+//        // Send escape
+//        text.notifyListeners(SWT.KeyDown, event);
+//        // Verify it has reverted to "Austria" and not any other value, i.e. the
+//        // last value it displayed
+//        assertEquals(currentText, text.getText());
+//
+//    }
+
+//    public void testScenario04() {
+//        // Show that the Escape key can be pressed in the middle of editing and
+//        // the value will revert
+//        // the updatePolicy for this test is TIME_EARLY so it occurs when each
+//        // keystroke occurs
+//        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
+//                BeansObservables.observeValue(adventure, "name"),
+//                null, null);
+//
+//        String originalName = adventure.getName();
+//        // Change the text field character by character and ensure that the
+//        // model changes
+//        String newName = "Switzerland";
+//        for (int i = 0; i < newName.length(); i++) {
+//            text.setText(newName.substring(0, i + 1));
+//            // Verify the model has changed
+//            assertEquals(newName.substring(0, i + 1), adventure.getName());
+//        }
+//        
+//        // Now send an escape key and verify that the model reverts
+//        Event event = new Event();
+//        event.character = SWT.ESC;
+//        event.keyCode = 27;
+//        text.notifyListeners(SWT.KeyDown, event);
+//        assertEquals(adventure.getName(), originalName);
+//        
+//        // Now send "Austria" key by key
+//        newName = "Austria";
+//        for (int i = 0; i < newName.length(); i++) {
+//            text.setText(newName.substring(0, i + 1));
+//            // Verify the model has changed
+//            assertEquals(newName.substring(0, i + 1), adventure.getName());
+//        }
+//        // Send a focus lost event to commit the change
+//        text.notifyListeners(SWT.FocusOut, null);
+//        // Send an escape key
+//        text.notifyListeners(SWT.KeyDown, event);
+//        // Verify that the model has changed and has not reverted
+//        assertEquals(newName, adventure.getName());
+//    }
+
+    /**
+     * public void testScenario05(){ // Show that nesting of properties works.
+     * Adventure has defaultLodging and Lodging has name getDbc().bind(text,new
+     * Property(adventure,"defaultLodging.name"),null); // Verify the GUI is
+     * showing the model value
+     * assertEquals(text.getText(),adventure.getDefaultLodging().getName()); }
+     */
+    public void testScenario06() {
+        // // Show that partial validation works for TIME_EARLY
+        // // We are using TIME_EARLY to verify that invalid states are not sent
+        // to the model
+        // getSWTObservableFactory().setUpdateTime(DataBindingContext.TIME_EARLY);
+        // getDbc().bind(text, new Property(account, "phone"), new BindSpec(new
+        // PhoneConverter(),new PhoneValidator()));
+        // // Verify we have no error message for partial validation or full
+        // validation yet
+        // assertTrue(((String)getDbc().getPartialValidationMessage().getValue()).length()
+        // == 0);
+        // assertTrue(((String)getDbc().getValidationMessage().getValue()).length()
+        // == 0);
+        // // Update some of the phone number
+        // String originalPhoneNumber = account.getPhone();
+        // text.setText("999");
+        // // Verify that the phone number is partially invalid and there is no
+        // validation message
+        // assertTrue(((String)getDbc().getPartialValidationMessage().getValue()).length()
+        // > 0);
+        // assertTrue(((String)getDbc().getValidationMessage().getValue()).length()
+        // == 0);
+        // // And that the model has not changed
+        // assertEquals(account.getPhone(),originalPhoneNumber);
+        // // Verify that fixing the phone removes the error and the model is
+        // updated too
+        // text.setText("999-888-7777");
+        // assertTrue(((String)getDbc().getPartialValidationMessage().getValue()).length()
+        // == 0);
+        // assertEquals(account.getPhone(),"9998887777");
+    }
+
+    public void testScenario07() {
+        // // Show that partial validation works for TIME_LATE
+        // getSWTObservableFactory().setUpdateTime(DataBindingContext.TIME_LATE);
+        // getDbc().bind(text, new Property(account, "phone"), new BindSpec(new
+        // PhoneConverter(),new PhoneValidator()));
+        // // Update some of the phone number
+        // String originalPhoneNumber = account.getPhone();
+        // text.setText("222");
+        // // Verify that we have no completion validation message and a partial
+        // one
+        // assertTrue(((String)getDbc().getPartialValidationMessage().getValue()).length()
+        // > 0);
+        // assertTrue(((String)getDbc().getValidationMessage().getValue()).length()
+        // == 0);
+        // // Fix the error
+        // text.setText("222-333-4444");
+        // // Verify that the errors are both fixed
+        // assertTrue(((String)getDbc().getPartialValidationMessage().getValue()).length()
+        // == 0);
+        // assertTrue(((String)getDbc().getValidationMessage().getValue()).length()
+        // == 0);
+        // // The model should not be changed
+        // assertEquals(originalPhoneNumber,account.getPhone());
+        // // Lose focus and verify that the complete validation message is
+        // fixed
+        // text.notifyListeners(SWT.FocusOut,null);
+        // assertTrue(((String)getDbc().getValidationMessage().getValue()).length()
+        // == 0);
+        // // The model should be changed
+        // assertEquals("2223334444",account.getPhone());
+    }
+
+    public void testScenario08() {
+
+        if (BindingTestSuite.failingTestsDisabled(this)) {
+            return;
+        }
+
+        // Show that the CustomBeanBindSupportFactory will automatically pick up
+        // the
+        // validator on the MaxNumberOfPeople property
+
+        DataBindingContext dbc = getDbc();
+        
+        dbc.bindValue(SWTObservables.observeText(text, SWT.Modify),
+				BeansObservables.observeValue(adventure, "maxNumberOfPeople"),
+				new CustomBeanUpdateValueStrategy(), null);
+
+        // make sure we can set a value inside the validator's range
+        text.setText("4");
+        assertEquals(4, adventure.getMaxNumberOfPeople());
+        // Now try to set a value outside the validator's range
+        text.setText("999");
+        assertEquals(4, adventure.getMaxNumberOfPeople());
+        dbc.dispose();
+    }
+
+    public void testScenario09() {
+        // Verify direct binding between a Text and Label following bugzilla
+        // 118696        
+        Label label = new Label(getComposite(), SWT.NONE);
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.FocusOut), SWTObservables.observeText(label));
+
+        // Change the text
+        text.setText("Frog");
+        // Verify the label does not change
+        assertTrue(label.getText().length() == 0);
+        // Lose focus from the text field
+        text.notifyListeners(SWT.FocusOut, null);
+        assertEquals(label.getText(), "Frog");
+
+    }
+
+    public void testScenario10() {
+        // Verify direct binding between a Text and Label following bugzilla
+        // 118696 with TIME_EARLY
+        Label label = new Label(getComposite(), SWT.NONE);
+        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify), SWTObservables.observeText(label));
+
+        // Change the text
+        String newTextValue = "Frog";
+        for (int i = 0; i < newTextValue.length(); i++) {
+            text.setText(newTextValue.substring(0, i + 1));
+            // Verify the label has changed key by key
+            assertEquals(text.getText(), label.getText());
+        }
+        // Lose focus
+        text.notifyListeners(SWT.FocusOut, null);
+        // Verify the text and label are the same following a lose focus
+        assertEquals(text.getText(), label.getText());
+    }
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/smoketest/swt.xswt b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/smoketest/swt.xswt
new file mode 100644
index 0000000..0097d63
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/smoketest/swt.xswt
@@ -0,0 +1,301 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<xswt xmlns:x="http://sweet_swt.sf.net/xswt">

+	<import xmlns="http://sweet_swt.sf.net/xswt">

+		<package name="java.lang"/>

+		<package name="org.eclipse.swt.widgets"/>

+		<package name="org.eclipse.swt.custom"/>

+		<package name="org.eclipse.swt.layout"/>

+	</import>

+

+	<!-- An XSWT example that uses nearly every SWT control and uses the new

+	     syntax wherever possible. 

+	     

+	     Get XSWT at:

+	     

+	     http://xswt.sourceforge.net/updates 

+	     

+	     then choose Window | Show View | Other... | XSWT Views | XSWT Preview -->

+

+	<layout x:class="gridLayout"/>

+	

+	<label text="SWT Controls:"/>

+	<tabFolder>

+		<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+

+		<composite x:id="Text">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="2"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" verticalAlignment="GridData.FILL" horizontalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="2"/>

+

+				<label text="Integer:"/><text x:style="BORDER" x:id="intText" text="1"/>

+				<label text="Float:"/><text x:style="BORDER" x:id="floatText" text="1.0"/>

+				<label text="String:"/><text x:style="BORDER" x:id="stringText" text="The quick brown fox jumped over the lazy dog."/>

+				<label text="RegexValidated String:"/><text x:style="BORDER" x:id="regexStringText" text="The quick brown fox jumped over the lazy dog."/>

+				<label text="Masked String:"/><text x:style="BORDER" x:id="maskedStringText" background="208 128 128"/>

+			</composite>

+			<slider x:style="VERTICAL" x:id="buttonScroller">

+				<layoutData x:class="gridData" verticalAlignment="GridData.FILL"/>

+			</slider>

+		</composite>

+		<tabItem text="Text" control="Text"/>

+

+		<composite x:id="Labels">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="2"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" verticalAlignment="GridData.FILL" horizontalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="1"/>

+

+				<label text="Label with text"/>

+				<label text="Label with image"/>

+				<label text="Label with text and image"/>

+				<label x:style="SEPARATOR | HORIZONTAL"><layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/></label>

+				<cLabel text="CLabel with text"/>

+				<cLabel text="CLabel with image"/>

+				<cLabel text="CLabel with text and image"/>

+			</composite>

+			<slider x:style="VERTICAL" x:id="textScroller"/>

+		</composite>

+		<tabItem text="Label / CLabel" control="Labels"/>

+	

+		<composite x:id="Button">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="2"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" verticalAlignment="GridData.FILL" horizontalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="2"/>

+

+				<label text="Push:"/><button x:id="pushButton" text="PushButton"/>

+				<label text="Arrow:"/><button x:id="arrowButton" x:style="ARROW" text="ArrowButton"/>

+				<label text="Toggle:"/><button x:id="toggleButton" x:style="TOGGLE" text="ToggleButton"/>

+				<label text="Check:"/><button x:style="CHECK" x:id="checkButton" text="CheckBox"/>

+				<label text="Radio Group 1:"/>

+				<composite x:style="BORDER" x:id="radioGroup1">

+					<layoutData x:class="gridData" horizontalAlignment="GridData.FILL"/>

+					<layout x:class="gridLayout" numColumns="1"/>

+

+					<button x:style="RADIO" x:id="radioButton0.1" text="Radio"/>

+					<button x:style="RADIO" x:id="radioButton0.2" text="Radio2" selection="true"/>

+					<button x:style="RADIO" x:id="radioButton0.3" text="Radio3"/>

+				</composite>

+				<label text="Radio Group 2:"/>

+				<composite x:style="BORDER" x:id="radioGroup2">

+					<layoutData x:class="gridData" horizontalAlignment="GridData.FILL"/>

+					<layout x:class="gridLayout" numColumns="1"/>

+

+					<button x:style="RADIO" x:id="radioButton1.1" text="Radio"/>

+					<button x:style="RADIO" x:id="radioButton1.2" text="Radio2"/>

+					<button x:style="RADIO" x:id="radioButton1.3" text="Radio3"/>

+				</composite>

+			</composite>

+			<slider x:style="VERTICAL" x:id="1buttonScroller">

+				<layoutData x:class="gridData" verticalAlignment="GridData.FILL"/>

+			</slider>

+		</composite>

+		<tabItem text="Button" control="Button"/>

+

+		<composite x:id="List">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="1"/>

+

+			<composite>

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL" grabExcessVerticalSpace="true" verticalAlignment="GridData.FILL"/>

+	            <layout x:class="fillLayout" type="VERTICAL"/>

+

+				<list x:style="BORDER | V_SCROLL | MULTI"/>

+				<list x:style="BORDER | V_SCROLL"/>

+			</composite>

+		</composite>

+		<tabItem text="List" control="List"/>

+

+		<composite x:id="Combos">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="2"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" verticalAlignment="GridData.FILL" horizontalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="1"/>

+

+				<combo>

+					<add x:p0="item1"/>

+					<add x:p0="item2"/>

+					<add x:p0="item3"/>

+				</combo>

+				<combo x:style="SIMPLE">

+					<add x:p0="item1"/>

+					<add x:p0="item2"/>

+					<add x:p0="item3"/>

+				</combo>

+				<combo x:style="READ_ONLY">

+					<add x:p0="item1"/>

+					<add x:p0="item2"/>

+					<add x:p0="item3"/>

+				</combo>

+				<label x:style="SEPARATOR | HORIZONTAL"><layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/></label>

+				<cCombo>

+					<add x:p0="item1"/>

+					<add x:p0="item2"/>

+					<add x:p0="item3"/>

+				</cCombo>

+				<cCombo x:style="READ_ONLY" background="255 255 255">

+					<add x:p0="item1"/>

+					<add x:p0="item2"/>

+					<add x:p0="item3"/>

+				</cCombo>

+			</composite>

+			<slider x:style="VERTICAL" x:id="2buttonScroller">

+				<layoutData x:class="gridData" verticalAlignment="GridData.FILL"/>

+			</slider>

+		</composite>

+		<tabItem text="Combo / CCombo" control="Combos"/>

+

+		<composite x:id="Table">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="1"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="2"/>

+

+				<table x:style="BORDER" linesVisible="true" headerVisible="true">

+					<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+

+					<tableColumn width="50" text="Done"/>

+					<tableColumn width="200" text="Description"/>

+					<tableColumn width="60" text="Planned"/>

+					<tableColumn width="60" text="Revised"/>

+					<tableColumn width="60" text="Actual"/>

+					<!--<tableItem/>-->

+				</table>

+			</composite>

+		</composite>

+		<tabItem text="Table" control="Table"/>

+

+		<composite x:id="Tree">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="1"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="2"/>

+

+				<tree x:style="BORDER">

+					<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+

+					<treeItem text="item">

+						<x:children>

+							<treeItem text="item"/>

+							<treeItem text="item"/>

+							<treeItem text="item">

+								<x:children>

+									<treeItem text="item"/>

+									<treeItem text="item"/>

+									<treeItem text="item"/>

+									<treeItem text="item"/>

+									<treeItem text="item"/>

+								</x:children>

+							</treeItem>

+							<treeItem text="item"/>

+						</x:children>

+					</treeItem>

+					<treeItem text="item with Image (or at least needs one)">

+						<x:children>

+							<treeItem text="item"/>

+							<treeItem text="item"/>

+						</x:children>

+					</treeItem>

+					<treeItem text="Another item">

+						<x:children>

+							<treeItem text="item"/>

+							<treeItem text="item"/>

+							<treeItem text="item"/>

+							<treeItem text="item"/>

+							<treeItem text="item"/>

+						</x:children>

+					</treeItem>

+				</tree>

+			</composite>

+		</composite>

+		<tabItem text="Tree" control="Tree"/>

+

+		<composite x:id="TableTree">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="1"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="2"/>

+

+				<tableTree x:style="BORDER">

+					<layoutData x:class="gridData" grabExcessHorizontalSpace="true" grabExcessVerticalSpace="true" horizontalAlignment="GridData.FILL" verticalAlignment="GridData.FILL"/>

+

+					<tableTreeItem text="item">

+						<x:children>

+							<tableTreeItem text="subitem"/>

+							<tableTreeItem text="subitem"/>

+						</x:children>

+					</tableTreeItem>

+					<tableTreeItem text="item">

+						<x:children>

+							<tableTreeItem text="subitem"/>

+							<tableTreeItem text="subitem"/>

+							<tableTreeItem text="subitem"/>

+							<tableTreeItem text="subitem"/>

+							<tableTreeItem text="subitem"/>

+						</x:children>

+					</tableTreeItem>

+					<tableTreeItem text="item">

+						<x:children>

+							<tableTreeItem text="item">

+								<x:children>

+									<tableTreeItem text="subitem"/>

+								</x:children>

+							</tableTreeItem>

+							<tableTreeItem text="item">

+								<x:children>

+									<tableTreeItem text="subitem"/>

+									<tableTreeItem text="subitem"/>

+									<tableTreeItem text="subitem"/>

+									<tableTreeItem text="subitem"/>

+									<tableTreeItem text="subitem"/>

+								</x:children>

+							</tableTreeItem>

+							<tableTreeItem text="item">

+								<x:children>

+									<tableTreeItem text="subitem"/>

+									<tableTreeItem text="subitem"/>

+									<tableTreeItem text="subitem"/>

+								</x:children>

+							</tableTreeItem>

+						</x:children>

+					</tableTreeItem>

+				</tableTree>

+			</composite>

+		</composite>

+		<tabItem text="TableTree" control="TableTree"/>

+

+		<composite x:id="Progress">

+			<layoutData x:class="gridData" grabExcessHorizontalSpace="true" horizontalAlignment="GridData.FILL"/>

+			<layout x:class="gridLayout" numColumns="2"/>

+

+			<composite x:style="BORDER">

+				<layoutData x:class="gridData" grabExcessHorizontalSpace="true" verticalAlignment="GridData.FILL" horizontalAlignment="GridData.FILL"/>

+				<layout x:class="gridLayout" numColumns="1"/>

+

+				<progressBar x:style="INDETERMINATE"/>

+				<progressBar x:style="INDETERMINATE | SMOOTH"/>

+				<progressBar x:style="SMOOTH" minimum="0" maximum="100" selection="40"/>

+				<progressBar minimum="0" maximum="100" selection="60"/>

+			</composite>

+			<slider x:style="VERTICAL" x:id="3textScroller"/>

+		</composite>

+		<tabItem text="ProgressBar" control="Progress"/>

+

+	</tabFolder>

+</xswt>

+

diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/SWTObservablesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/SWTObservablesTest.java
new file mode 100644
index 0000000..6659af5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/SWTObservablesTest.java
@@ -0,0 +1,598 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Chris Aniszczyk <zx@code9.com> - bug 131435
+ *     Matthew Hall - bugs 248621, 213893, 262320, 169876
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.swt;
+
+import org.eclipse.core.databinding.observable.IDecoratingObservable;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.swt.ISWTObservable;
+import org.eclipse.jface.databinding.swt.ISWTObservableList;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.internal.databinding.swt.ButtonImageProperty;
+import org.eclipse.jface.internal.databinding.swt.ButtonSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.ButtonTextProperty;
+import org.eclipse.jface.internal.databinding.swt.CComboItemsProperty;
+import org.eclipse.jface.internal.databinding.swt.CComboSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.CComboTextProperty;
+import org.eclipse.jface.internal.databinding.swt.CLabelImageProperty;
+import org.eclipse.jface.internal.databinding.swt.CLabelTextProperty;
+import org.eclipse.jface.internal.databinding.swt.CTabItemTooltipTextProperty;
+import org.eclipse.jface.internal.databinding.swt.ComboItemsProperty;
+import org.eclipse.jface.internal.databinding.swt.ComboSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.ComboTextProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlBackgroundProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlForegroundProperty;
+import org.eclipse.jface.internal.databinding.swt.ControlTooltipTextProperty;
+import org.eclipse.jface.internal.databinding.swt.DateTimeSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.ItemImageProperty;
+import org.eclipse.jface.internal.databinding.swt.ItemTextProperty;
+import org.eclipse.jface.internal.databinding.swt.LabelImageProperty;
+import org.eclipse.jface.internal.databinding.swt.LabelTextProperty;
+import org.eclipse.jface.internal.databinding.swt.ListItemsProperty;
+import org.eclipse.jface.internal.databinding.swt.ListSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.MenuEnabledProperty;
+import org.eclipse.jface.internal.databinding.swt.MenuItemEnabledProperty;
+import org.eclipse.jface.internal.databinding.swt.MenuItemSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.ScaleMaximumProperty;
+import org.eclipse.jface.internal.databinding.swt.ScaleMinimumProperty;
+import org.eclipse.jface.internal.databinding.swt.ScaleSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.ScrollBarEnabledProperty;
+import org.eclipse.jface.internal.databinding.swt.SpinnerMaximumProperty;
+import org.eclipse.jface.internal.databinding.swt.SpinnerMinimumProperty;
+import org.eclipse.jface.internal.databinding.swt.SpinnerSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.StyledTextTextProperty;
+import org.eclipse.jface.internal.databinding.swt.TableSingleSelectionIndexProperty;
+import org.eclipse.jface.internal.databinding.swt.TextEditableProperty;
+import org.eclipse.jface.internal.databinding.swt.TextTextProperty;
+import org.eclipse.jface.internal.databinding.swt.ToolItemEnabledProperty;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.DateTime;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.ToolTip;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 1.1
+ */
+public class SWTObservablesTest extends AbstractSWTTestCase {
+	private Shell shell;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		shell = getShell();
+		RealmTester.setDefault(SWTObservables.getRealm(shell.getDisplay()));
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		RealmTester.setDefault(null);
+	}
+
+	protected Shell getShell() {
+		if (shell == null) {
+			shell = new Shell(SWT.V_SCROLL);
+		}
+		return shell;
+	}
+
+	public void testObserveForeground() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeForeground(shell);
+		assertWidgetObservable(value, shell, ControlForegroundProperty.class);
+		assertEquals(Color.class, value.getValueType());
+	}
+
+	public void testObserveBackground() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeBackground(shell);
+		assertWidgetObservable(value, shell, ControlBackgroundProperty.class);
+		assertEquals(Color.class, value.getValueType());
+	}
+
+	public void testObserveFont() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeFont(shell);
+		assertNotNull(value);
+		assertEquals(Font.class, value.getValueType());
+	}
+
+	public void testObserveSelectionOfSpinner() throws Exception {
+		Spinner spinner = new Spinner(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeSelection(spinner);
+		assertWidgetObservable(value, spinner, SpinnerSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfButton() throws Exception {
+		Button button = new Button(shell, SWT.PUSH);
+		ISWTObservableValue value = SWTObservables.observeSelection(button);
+		assertWidgetObservable(value, button, ButtonSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfCombo() throws Exception {
+		Combo combo = new Combo(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeSelection(combo);
+		assertWidgetObservable(value, combo, ComboSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfCCombo() throws Exception {
+		CCombo combo = new CCombo(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeSelection(combo);
+		assertWidgetObservable(value, combo, CComboSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfDateTime_Date() throws Exception {
+		DateTime dateTime = new DateTime(shell, SWT.DATE);
+		ISWTObservableValue value = SWTObservables.observeSelection(dateTime);
+		assertWidgetObservable(value, dateTime, DateTimeSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfDateTime_Calendar() throws Exception {
+		DateTime dateTime = new DateTime(shell, SWT.CALENDAR);
+		ISWTObservableValue value = SWTObservables.observeSelection(dateTime);
+		assertWidgetObservable(value, dateTime, DateTimeSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfDateTime_Time() throws Exception {
+		DateTime dateTime = new DateTime(shell, SWT.TIME);
+		ISWTObservableValue value = SWTObservables.observeSelection(dateTime);
+		assertWidgetObservable(value, dateTime, DateTimeSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfList() throws Exception {
+		List list = new List(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeSelection(list);
+		assertWidgetObservable(value, list, ListSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfScale() throws Exception {
+		Scale scale = new Scale(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeSelection(scale);
+		assertWidgetObservable(value, scale, ScaleSelectionProperty.class);
+	}
+
+	public void testObserveSelectionOfUnsupportedControl() throws Exception {
+		try {
+			Text text = new Text(shell, SWT.NONE);
+			SWTObservables.observeSelection(text);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testObserveTextWithEventOfText() throws Exception {
+		Text text = new Text(shell, SWT.NONE);
+		assertFalse(text.isListening(SWT.FocusOut));
+
+		ISWTObservableValue value = SWTObservables.observeText(text,
+				SWT.FocusOut);
+		assertWidgetObservable(value, text, TextTextProperty.class);
+
+		assertFalse(text.isListening(SWT.FocusOut));
+		ChangeEventTracker.observe(value);
+		assertTrue(text.isListening(SWT.FocusOut));
+	}
+
+	public void testObserveTextOfStyledText() throws Exception {
+		StyledText text = new StyledText(shell, SWT.NONE);
+		assertFalse(text.isListening(SWT.FocusOut));
+
+		ISWTObservableValue value = SWTObservables.observeText(text,
+				SWT.FocusOut);
+		assertWidgetObservable(value, text, StyledTextTextProperty.class);
+
+		assertFalse(text.isListening(SWT.FocusOut));
+		ChangeEventTracker.observe(value);
+		assertTrue(text.isListening(SWT.FocusOut));
+	}
+
+	public void testObserveTextWithEventOfUnsupportedControl() throws Exception {
+		Label label = new Label(shell, SWT.NONE);
+		try {
+			SWTObservables.observeText(label, SWT.FocusOut);
+			fail("Exception should have been thrown");
+		} catch (Exception e) {
+		}
+	}
+
+	public void testObserveTextOfButton() throws Exception {
+		Button button = new Button(shell, SWT.PUSH);
+		ISWTObservableValue value = SWTObservables.observeText(button);
+		assertWidgetObservable(value, button, ButtonTextProperty.class);
+	}
+
+	public void testObserveTextOfLabel() throws Exception {
+		Label label = new Label(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeText(label);
+		assertWidgetObservable(value, label, LabelTextProperty.class);
+	}
+
+	public void testObserveTextOfCLabel() throws Exception {
+		CLabel label = new CLabel(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeText(label);
+		assertWidgetObservable(value, label, CLabelTextProperty.class);
+	}
+
+	public void testObserveTextOfCombo() throws Exception {
+		Combo combo = new Combo(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeText(combo);
+		assertWidgetObservable(value, combo, ComboTextProperty.class);
+	}
+
+	/**
+	 * @param observable
+	 * @return
+	 */
+	private IPropertyObservable getPropertyObservable(ISWTObservable observable) {
+		IDecoratingObservable decoratingObservable = (IDecoratingObservable) observable;
+		IPropertyObservable propertyObservable = (IPropertyObservable) decoratingObservable
+				.getDecorated();
+		return propertyObservable;
+	}
+
+	public void testObserveTextOfCCombo() throws Exception {
+		CCombo combo = new CCombo(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeText(combo);
+		assertWidgetObservable(value, combo, CComboTextProperty.class);
+	}
+
+	public void testObserveTextOfText() throws Exception {
+		Text text = new Text(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeText(text);
+
+		assertWidgetObservable(value, text, TextTextProperty.class);
+
+		assertFalse(text.isListening(SWT.Modify));
+		assertFalse(text.isListening(SWT.FocusOut));
+	}
+
+	public void testObserveTextOfItem() throws Exception {
+		CTabFolder ctf = new CTabFolder(shell, SWT.NONE);
+		Item item = new CTabItem(ctf, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeText(item);
+		assertWidgetObservable(value, item, ItemTextProperty.class);
+	}
+
+	public void testObserveTextOfUnsupportedControl() throws Exception {
+		Table table = new Table(shell, SWT.NONE);
+		try {
+			SWTObservables.observeText(table);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testObserveImageOfButton() throws Exception {
+		Button button = new Button(shell, SWT.PUSH);
+		ISWTObservableValue value = SWTObservables.observeImage(button);
+		assertWidgetObservable(value, button, ButtonImageProperty.class);
+	}
+
+	public void testObserveImageOfCLabel() throws Exception {
+		CLabel cLabel = new CLabel(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeImage(cLabel);
+		assertWidgetObservable(value, cLabel, CLabelImageProperty.class);
+	}
+
+	public void testObserveImageOfItem() throws Exception {
+		CTabFolder ctf = new CTabFolder(shell, SWT.NONE);
+		Item item = new CTabItem(ctf, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeImage(item);
+		assertWidgetObservable(value, item, ItemImageProperty.class);
+	}
+
+	public void testObserveImageOfLabel() throws Exception {
+		Label label = new Label(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeImage(label);
+		assertWidgetObservable(value, label, LabelImageProperty.class);
+	}
+
+	public void testObserveTooltipOfItem() throws Exception {
+		CTabFolder ctf = new CTabFolder(shell, SWT.NONE);
+		Item item = new CTabItem(ctf, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeTooltipText(item);
+		assertWidgetObservable(value, item, CTabItemTooltipTextProperty.class);
+	}
+
+	public void testObserveTooltipOfUnsupportedControl() throws Exception {
+		ToolTip ttip = new ToolTip(shell, SWT.NONE);
+		try {
+			SWTObservables.observeTooltipText(ttip);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testObserveTooltipOfControl() throws Exception {
+		Label label = new Label(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeTooltipText(label);
+		assertWidgetObservable(value, label, ControlTooltipTextProperty.class);
+	}
+
+	public void testObserveItemsOfCombo() throws Exception {
+		Combo combo = new Combo(shell, SWT.NONE);
+		ISWTObservableList list = (ISWTObservableList) SWTObservables
+				.observeItems(combo);
+		assertWidgetObservable(list, combo, ComboItemsProperty.class);
+	}
+
+	public void testObserveItemsOfCCombo() throws Exception {
+		CCombo ccombo = new CCombo(shell, SWT.NONE);
+		ISWTObservableList list = (ISWTObservableList) SWTObservables
+				.observeItems(ccombo);
+		assertWidgetObservable(list, ccombo, CComboItemsProperty.class);
+	}
+
+	public void testObserveItemsOfList() throws Exception {
+		List list = new List(shell, SWT.NONE);
+		ISWTObservableList observableList = (ISWTObservableList) SWTObservables
+				.observeItems(list);
+		assertWidgetObservable(observableList, list, ListItemsProperty.class);
+	}
+
+	public void testObserveItemsOfUnsupportedControl() throws Exception {
+		Table table = new Table(shell, SWT.NONE);
+		try {
+			SWTObservables.observeItems(table);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testObserveSingleSelectionIndexOfTable() throws Exception {
+		Table table = new Table(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables
+				.observeSingleSelectionIndex(table);
+		assertWidgetObservable(value, table,
+				TableSingleSelectionIndexProperty.class);
+	}
+
+	public void testObserveSingleSelectionIndexOfCCombo_DeselectAll()
+			throws Exception {
+		CCombo cCombo = new CCombo(shell, SWT.NONE);
+		cCombo.add("item");
+		cCombo.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(cCombo);
+		assertEquals(0, cCombo.getSelectionIndex());
+		value.setValue(new Integer(-1));
+		assertEquals(-1, cCombo.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfCCombo_SetValueNull()
+			throws Exception {
+		CCombo cCombo = new CCombo(shell, SWT.NONE);
+		cCombo.add("item");
+		cCombo.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(cCombo);
+		assertEquals(0, cCombo.getSelectionIndex());
+		value.setValue(null);
+		assertEquals(-1, cCombo.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfCombo_DeselectAll()
+			throws Exception {
+		Combo combo = new Combo(shell, SWT.NONE);
+		combo.add("item");
+		combo.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(combo);
+		assertEquals(0, combo.getSelectionIndex());
+		value.setValue(new Integer(-1));
+		assertEquals(-1, combo.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfCombo_SetValueNull()
+			throws Exception {
+		Combo combo = new Combo(shell, SWT.NONE);
+		combo.add("item");
+		combo.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(combo);
+		assertEquals(0, combo.getSelectionIndex());
+		value.setValue(null);
+		assertEquals(-1, combo.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfList_DeselectAll()
+			throws Exception {
+		List list = new List(shell, SWT.NONE);
+		list.add("item");
+		list.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(list);
+		assertEquals(0, list.getSelectionIndex());
+		value.setValue(new Integer(-1));
+		assertEquals(-1, list.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfList_SetValueNull()
+			throws Exception {
+		List list = new List(shell, SWT.NONE);
+		list.add("item");
+		list.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(list);
+		assertEquals(0, list.getSelectionIndex());
+		value.setValue(null);
+		assertEquals(-1, list.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfTable_DeselectAll()
+			throws Exception {
+		Table table = new Table(shell, SWT.NONE);
+		new TableItem(table, SWT.NONE);
+		table.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(table);
+		assertEquals(0, table.getSelectionIndex());
+		value.setValue(new Integer(-1));
+		assertEquals(-1, table.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfTable_SetValueNull()
+			throws Exception {
+		Table table = new Table(shell, SWT.NONE);
+		new TableItem(table, SWT.NONE);
+		table.select(0);
+
+		ISWTObservableValue value = WidgetProperties.singleSelectionIndex()
+				.observe(table);
+		assertEquals(0, table.getSelectionIndex());
+		value.setValue(null);
+		assertEquals(-1, table.getSelectionIndex());
+	}
+
+	public void testObserveSingleSelectionIndexOfUnsupportedControl()
+			throws Exception {
+		Tree tree = new Tree(shell, SWT.NONE);
+		try {
+			SWTObservables.observeSingleSelectionIndex(tree);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+
+		}
+	}
+
+	public void testObserveMinOfSpinner() throws Exception {
+		Spinner spinner = new Spinner(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeMin(spinner);
+		assertWidgetObservable(value, spinner, SpinnerMinimumProperty.class);
+	}
+
+	public void testObserveMinOfScale() throws Exception {
+		Scale scale = new Scale(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeMin(scale);
+		assertWidgetObservable(value, scale, ScaleMinimumProperty.class);
+	}
+
+	public void testObserveMinOfUnsupportedControl() throws Exception {
+		Text text = new Text(shell, SWT.NONE);
+		try {
+			SWTObservables.observeMin(text);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testObserveMaxOfSpinner() throws Exception {
+		Spinner spinner = new Spinner(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeMax(spinner);
+		assertWidgetObservable(value, spinner, SpinnerMaximumProperty.class);
+	}
+
+	public void testObserveMaxOfScale() throws Exception {
+		Scale scale = new Scale(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeMax(scale);
+		assertWidgetObservable(value, scale, ScaleMaximumProperty.class);
+	}
+
+	public void testObserveMaxOfUnsupportedControl() throws Exception {
+		Text text = new Text(shell, SWT.NONE);
+		try {
+			SWTObservables.observeMax(text);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testObserveEditableOfText() throws Exception {
+		Text text = new Text(shell, SWT.NONE);
+		ISWTObservableValue value = SWTObservables.observeEditable(text);
+		assertWidgetObservable(value, text, TextEditableProperty.class);
+	}
+
+	public void testObserveEnabledOfMenu() throws Exception {
+		Menu menu = new Menu(shell, SWT.BAR);
+		ISWTObservableValue value = SWTObservables.observeEnabled(menu);
+		assertWidgetObservable(value, menu, MenuEnabledProperty.class);
+	}
+
+	public void testObserveEnabledOfMenuItem() throws Exception {
+		Menu menu = new Menu(shell, SWT.DROP_DOWN);
+		MenuItem item = new MenuItem(menu, SWT.PUSH);
+		ISWTObservableValue value = SWTObservables.observeEnabled(item);
+		assertWidgetObservable(value, item, MenuItemEnabledProperty.class);
+	}
+
+	public void testObserveSelectionOfMenuItem() throws Exception {
+		Menu menu = new Menu(shell, SWT.DROP_DOWN);
+		MenuItem item = new MenuItem(menu, SWT.PUSH);
+		ISWTObservableValue value = SWTObservables.observeSelection(item);
+		assertWidgetObservable(value, item, MenuItemSelectionProperty.class);
+	}
+
+	public void testObserveEnabledOfScrollBar() throws Exception {
+		ScrollBar scrollBar = shell.getVerticalBar();
+		ISWTObservableValue value = SWTObservables.observeEnabled(scrollBar);
+		assertWidgetObservable(value, scrollBar, ScrollBarEnabledProperty.class);
+	}
+
+	public void testObserveEnabledOfToolItem() throws Exception {
+		ToolBar bar = new ToolBar(shell, SWT.HORIZONTAL);
+		ToolItem item = new ToolItem(bar, SWT.PUSH);
+		ISWTObservableValue value = SWTObservables.observeEnabled(item);
+		assertWidgetObservable(value, item, ToolItemEnabledProperty.class);
+	}
+
+	private void assertWidgetObservable(ISWTObservable observable,
+			Widget widget, Class propertyClass) {
+		assertNotNull(observable);
+		assertTrue(observable.getWidget() == widget);
+		IPropertyObservable propertyObservable = getPropertyObservable(observable);
+		assertTrue(propertyClass.isInstance(propertyObservable.getProperty()));
+	}
+
+	public void testObserveEditableOfUnsupportedControl() throws Exception {
+		Label label = new Label(shell, SWT.NONE);
+		try {
+			SWTObservables.observeEditable(label);
+			fail("Exception should have been thrown");
+		} catch (IllegalArgumentException e) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/WidgetObservableThreadTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/WidgetObservableThreadTest.java
new file mode 100644
index 0000000..35907c0
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/WidgetObservableThreadTest.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 281723)
+ *     Matthew Hall - bug 286533
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.swt;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.tests.databinding.observable.ThreadRealm;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class WidgetObservableThreadTest extends AbstractSWTTestCase {
+	protected ThreadRealm threadRealm;
+	private DataBindingContext ctx;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		threadRealm = new ThreadRealm();
+		new Thread() {
+			public void run() {
+				RealmTester.setDefault(threadRealm);
+				threadRealm.init(Thread.currentThread());
+				threadRealm.block();
+			}
+		}.start();
+
+        threadRealm.waitUntilBlocking();
+	}
+
+	protected void tearDown() throws Exception {
+		if (ctx != null) {
+			threadRealm.exec(new Runnable() {
+				public void run() {
+					ctx.dispose();
+					ctx = null;
+				}
+			});
+			threadRealm.processQueue();
+		}
+
+		threadRealm.unblock();
+		threadRealm = null;
+
+		super.tearDown();
+	}
+
+	public void testBindWidgetObservableFromNonDisplayThread() {
+		final Text text = new Text(getShell(), SWT.BORDER);
+		final Bean bean = new Bean("oldValue");
+
+		assertEquals("oldValue", bean.getValue());
+		assertEquals("", text.getText());
+
+		threadRealm.exec(new Runnable() {
+			public void run() {
+				ctx = new DataBindingContext();
+				ctx.bindValue(WidgetProperties.text(SWT.Modify).observe(text),
+						BeanProperties.value("value")
+								.observe(threadRealm, bean));
+			}
+		});
+		threadRealm.processQueue();
+
+		assertEquals("oldValue", bean.getValue());
+		assertEquals("", text.getText());
+
+		processDisplayQueue();
+		threadRealm.processQueue();
+
+		assertEquals("oldValue", bean.getValue());
+		assertEquals("oldValue", text.getText());
+
+		text.setText("newValue");
+		threadRealm.processQueue();
+
+		assertEquals("newValue", bean.getValue());
+		assertEquals("newValue", text.getText());
+
+		threadRealm.exec(new Runnable() {
+			public void run() {
+				bean.setValue("newerValue");
+			}
+		});
+		threadRealm.processQueue();
+
+		assertEquals("newerValue", bean.getValue());
+		assertEquals("newValue", text.getText());
+
+		processDisplayQueue();
+
+		assertEquals("newerValue", bean.getValue());
+		assertEquals("newerValue", text.getText());
+	}
+
+	private void processDisplayQueue() {
+		while (Display.getCurrent().readAndDispatch()) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/WidgetPropertiesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/WidgetPropertiesTest.java
new file mode 100644
index 0000000..767688a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/swt/WidgetPropertiesTest.java
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 262946)
+ *     Matthew Hall - bug 213893
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.util.RealmTester;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.Tray;
+import org.eclipse.swt.widgets.TrayItem;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class WidgetPropertiesTest extends AbstractSWTTestCase {
+	private Shell shell;
+
+	private String string1;
+	private String string2;
+
+	private Image image1;
+	private Image image2;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		shell = getShell();
+
+		string1 = "1";
+		string2 = "2";
+
+		image1 = shell.getDisplay().getSystemImage(SWT.ICON_WARNING);
+		image2 = shell.getDisplay().getSystemImage(SWT.ICON_ERROR);
+
+		RealmTester.setDefault(SWTObservables.getRealm(shell.getDisplay()));
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		RealmTester.setDefault(null);
+	}
+
+	protected Shell getShell() {
+		if (shell == null) {
+			shell = new Shell(SWT.V_SCROLL);
+		}
+		return shell;
+	}
+
+	public void testImage_ObserveButton() {
+		Button button = /* who's got the */new Button(shell, SWT.PUSH);
+		button.setImage(image1);
+
+		IObservableValue observable = WidgetProperties.image().observe(button);
+		assertSame(image1, observable.getValue());
+
+		observable.setValue(image2);
+		assertSame(image2, button.getImage());
+	}
+
+	public void testImage_ObserveCLabel() {
+		CLabel label = new CLabel(shell, SWT.NONE);
+		label.setImage(image1);
+
+		IObservableValue observable = WidgetProperties.image().observe(label);
+		assertSame(image1, observable.getValue());
+
+		observable.setValue(image2);
+		assertSame(image2, label.getImage());
+	}
+
+	public void testImage_ObserveLabel() {
+		Label label = new Label(shell, SWT.NONE);
+		label.setImage(image1);
+
+		IObservableValue observable = WidgetProperties.image().observe(label);
+		assertSame(image1, observable.getValue());
+
+		observable.setValue(image2);
+		assertSame(image2, label.getImage());
+	}
+
+	public void testText_ObserveButton() {
+		Button button = /* who's got a */new Button(shell, SWT.PUSH);
+		button.setText(string1);
+
+		IObservableValue observable = WidgetProperties.text().observe(button);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, button.getText());
+	}
+
+	public void testTooltipText_ObserveCTabItem() {
+		CTabFolder tf = new CTabFolder(shell, SWT.NONE);
+		CTabItem item = new CTabItem(tf, SWT.NONE);
+		item.setToolTipText(string1);
+
+		IObservableValue observable = WidgetProperties.tooltipText().observe(
+				item);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, item.getToolTipText());
+	}
+
+	public void testTooltipText_ObserveControl() {
+		Control control = new Label(shell, SWT.NONE);
+		control.setToolTipText(string1);
+
+		IObservableValue observable = WidgetProperties.tooltipText().observe(
+				control);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, control.getToolTipText());
+	}
+
+	public void testTooltipText_ObserveTabItem() {
+		TabFolder tf = new TabFolder(shell, SWT.NONE);
+		TabItem item = new TabItem(tf, SWT.NONE);
+		item.setToolTipText(string1);
+
+		IObservableValue observable = WidgetProperties.tooltipText().observe(
+				item);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, item.getToolTipText());
+	}
+
+	public void testTooltipText_ObserveTableColumn() {
+		Table table = new Table(shell, SWT.NONE);
+		TableColumn column = new TableColumn(table, SWT.NONE);
+		column.setToolTipText(string1);
+
+		IObservableValue observable = WidgetProperties.tooltipText().observe(
+				column);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, column.getToolTipText());
+	}
+
+	public void testTooltipText_ObserveToolItem() {
+		ToolBar bar = new ToolBar(shell, SWT.NONE);
+		ToolItem item = new ToolItem(bar, SWT.NONE);
+		item.setToolTipText(string1);
+
+		IObservableValue observable = WidgetProperties.tooltipText().observe(
+				item);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, item.getToolTipText());
+	}
+
+	public void testTooltipText_ObserveTrayItem() {
+		Tray tray = shell.getDisplay().getSystemTray();
+		TrayItem item = new TrayItem(tray, SWT.NONE);
+
+		try {
+			item.setToolTipText(string1);
+
+			IObservableValue observable = WidgetProperties.tooltipText()
+					.observe(item);
+			assertEquals(string1, observable.getValue());
+
+			observable.setValue(string2);
+			assertEquals(string2, item.getToolTipText());
+		} finally {
+			item.dispose(); // cleanup
+		}
+	}
+
+	public void testTooltipText_ObserveTreeColumn() {
+		Tree tree = new Tree(shell, SWT.NONE);
+		TreeColumn column = new TreeColumn(tree, SWT.NONE);
+		column.setToolTipText(string1);
+
+		IObservableValue observable = WidgetProperties.tooltipText().observe(
+				column);
+		assertEquals(string1, observable.getValue());
+
+		observable.setValue(string2);
+		assertEquals(string2, column.getToolTipText());
+	}
+
+	public void testEnabled_ObserveMenu() {
+		Menu menu = new Menu(shell, SWT.BAR);
+		IObservableValue observable = WidgetProperties.enabled().observe(menu);
+
+		assertEquals(boolean.class, observable.getValueType());
+
+		menu.setEnabled(true);
+		assertEquals(Boolean.TRUE, observable.getValue());
+
+		observable.setValue(Boolean.FALSE);
+		assertEquals(false, menu.getEnabled());
+	}
+
+	public void testEnabled_ObserveMenuItem() {
+		Menu menu = new Menu(shell, SWT.BAR);
+		MenuItem item = new MenuItem(menu, SWT.PUSH);
+		IObservableValue observable = WidgetProperties.enabled().observe(item);
+
+		assertEquals(boolean.class, observable.getValueType());
+
+		item.setEnabled(true);
+		assertEquals(Boolean.TRUE, observable.getValue());
+
+		observable.setValue(Boolean.FALSE);
+		assertEquals(false, item.getEnabled());
+	}
+
+	public void testSelection_ObserveMenuItem() {
+		Menu menu = new Menu(shell, SWT.BAR);
+		MenuItem item = new MenuItem(menu, SWT.CHECK);
+		IObservableValue observable = WidgetProperties.selection()
+				.observe(item);
+
+		assertEquals(boolean.class, observable.getValueType());
+
+		item.setSelection(true);
+		assertEquals(Boolean.TRUE, observable.getValue());
+
+		observable.setValue(Boolean.FALSE);
+		assertEquals(false, item.getSelection());
+	}
+
+	public void testEnabled_ObserveScrollBar() {
+		ScrollBar bar = shell.getVerticalBar();
+		IObservableValue observable = WidgetProperties.enabled().observe(bar);
+
+		assertEquals(boolean.class, observable.getValueType());
+
+		bar.setEnabled(true);
+		assertEquals(Boolean.TRUE, observable.getValue());
+
+		observable.setValue(Boolean.FALSE);
+		assertEquals(false, bar.getEnabled());
+	}
+
+	public void testEnabled_ObserveToolItem() {
+		ToolBar bar = new ToolBar(shell, SWT.HORIZONTAL);
+		ToolItem item = new ToolItem(bar, SWT.PUSH);
+		IObservableValue observable = WidgetProperties.enabled().observe(item);
+
+		assertEquals(boolean.class, observable.getValueType());
+
+		item.setEnabled(true);
+		assertEquals(Boolean.TRUE, observable.getValue());
+
+		observable.setValue(Boolean.FALSE);
+		assertEquals(false, item.getEnabled());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableListContentProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableListContentProviderTest.java
new file mode 100644
index 0000000..d0272e7
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableListContentProviderTest.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 263956
+ ******************************************************************************/
+package org.eclipse.jface.tests.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class ObservableListContentProviderTest extends
+		AbstractDefaultRealmTestCase {
+	private Shell shell;
+	private TableViewer viewer;
+	private ObservableListContentProvider contentProvider;
+	private IObservableList input;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		viewer = new TableViewer(shell, SWT.NONE);
+
+		contentProvider = new ObservableListContentProvider();
+		viewer.setContentProvider(contentProvider);
+
+		input = new WritableList();
+		viewer.setInput(input);
+	}
+
+	protected void tearDown() throws Exception {
+		shell.dispose();
+		viewer = null;
+		input = null;
+		super.tearDown();
+	}
+
+	public void testKnownElements_Realm() throws Exception {
+		assertSame("realm for the known elements should be the SWT realm",
+				SWTObservables.getRealm(Display.getDefault()), contentProvider
+						.getKnownElements().getRealm());
+	}
+
+	public void testRealizedElements_Realm() {
+		assertSame("realm for the realized elements should be the SWT realm",
+				SWTObservables.getRealm(Display.getDefault()), contentProvider
+						.getRealizedElements().getRealm());
+	}
+
+	public void testKnownElementsAfterSetInput() {
+		assertEquals(0, contentProvider.getKnownElements().size());
+		Set newElements = new HashSet(Arrays.asList(new String[] { "one",
+				"two", "three" }));
+		WritableList newInput = new WritableList();
+		newInput.addAll(newElements);
+		viewer.setInput(newInput);
+		assertEquals(newElements, contentProvider.getKnownElements());
+	}
+
+	public void testViewerUpdate_RemoveElementAfterMutation() {
+		Mutable element = new Mutable(1);
+		input.add(element);
+
+		assertEquals(1, viewer.getTable().getItemCount());
+
+		element.id++;
+		input.clear();
+
+		assertEquals(0, viewer.getTable().getItemCount());
+	}
+
+	public void testInputChanged_ClearsKnownElements() {
+		Object element = new Object();
+		input.add(element);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+		assertEquals(Collections.singleton(element), knownElements);
+		viewer.setInput(Observables.emptyObservableList());
+		assertEquals(Collections.EMPTY_SET, knownElements);
+	}
+
+	public void testInputChanged_ClearsRealizedElements() {
+		// Realized elements must be allocated before adding the element
+		// otherwise we'd have to spin the event loop to see the new element
+		IObservableSet realizedElements = contentProvider.getRealizedElements();
+
+		Object element = new Object();
+		input.add(element);
+
+		assertEquals(Collections.singleton(element), realizedElements);
+		viewer.setInput(Observables.emptyObservableList());
+		assertEquals(Collections.EMPTY_SET, realizedElements);
+	}
+
+	static class Mutable {
+		public int id;
+
+		public Mutable(int id) {
+			this.id = id;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Mutable that = (Mutable) obj;
+			return this.id == that.id;
+		}
+
+		public int hashCode() {
+			return id;
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableListTreeContentProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableListTreeContentProviderTest.java
new file mode 100644
index 0000000..e62dedf
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableListTreeContentProviderTest.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 263956
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+
+public class ObservableListTreeContentProviderTest extends
+		AbstractDefaultRealmTestCase {
+	private Shell shell;
+	private TreeViewer viewer;
+	private Tree tree;
+	private ObservableListTreeContentProvider contentProvider;
+	private Object input;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		tree = new Tree(shell, SWT.NONE);
+		viewer = new TreeViewer(tree);
+		input = new Object();
+	}
+
+	protected void tearDown() throws Exception {
+		shell.dispose();
+		tree = null;
+		viewer = null;
+		input = null;
+		super.tearDown();
+	}
+
+	private void initContentProvider(IObservableFactory listFactory) {
+		contentProvider = new ObservableListTreeContentProvider(listFactory, null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setInput(input);
+	}
+
+	public void testConstructor_NullArgumentThrowsException() {
+		try {
+			initContentProvider(null);
+			fail("Constructor should have thrown AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testGetElements_ChangesFollowObservedList() {
+		final IObservableList elements = new WritableList();
+		final Object input = new Object();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return target == input ? elements : null;
+			}
+		});
+
+		assertTrue(Arrays.equals(new Object[0], contentProvider
+				.getElements("unknown input")));
+
+		Object element0 = new Object();
+		elements.add(element0);
+
+		assertTrue(Arrays.equals(new Object[] { element0 }, contentProvider
+				.getElements(input)));
+
+		Object element1 = new Object();
+		elements.add(element1);
+
+		assertTrue(Arrays.equals(new Object[] { element0, element1 },
+				contentProvider.getElements(input)));
+	}
+
+	public void testViewerUpdate_RemoveElementAfterMutation() {
+		final IObservableList children = new WritableList();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return target == input ? children : null;
+			}
+		});
+
+		Mutable element = new Mutable();
+		children.add(element);
+		assertEquals(1, tree.getItemCount());
+
+		element.mutate();
+		children.remove(element);
+		assertEquals(0, tree.getItemCount());
+	}
+
+	public void testInputChanged_ClearsKnownElements() {
+		input = new Object();
+		final Object input2 = new Object();
+		
+		final IObservableList children = new WritableList();
+		final IObservableList children2 = new WritableList();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				if (target == input)
+					return children;
+				if (target == input2)
+					return children2;
+				return null;
+			}
+		});
+
+		Object element = new Object();
+		children.add(element);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+		assertEquals(Collections.singleton(element), knownElements);
+		viewer.setInput(input2);
+		assertEquals(Collections.EMPTY_SET, knownElements);
+	}
+
+	public void testInputChanged_ClearsRealizedElements() {
+		input = new Object();
+		final Object input2 = new Object();
+		
+		final IObservableList children = new WritableList();
+		final IObservableList children2 = new WritableList();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				if (target == input)
+					return children;
+				if (target == input2)
+					return children2;
+				return null;
+			}
+		});
+
+		// Realized elements must be allocated before adding the element
+		// otherwise we'd have to spin the event loop to see the new element
+		IObservableSet realizedElements = contentProvider.getRealizedElements();
+
+		Object element = new Object();
+		children.add(element);
+
+		assertEquals(Collections.singleton(element), realizedElements);
+		viewer.setInput(input2);
+		assertEquals(Collections.EMPTY_SET, realizedElements);
+	}
+
+	static class Mutable {
+		private int id;
+
+		public Mutable() {
+			this(0);
+		}
+
+		public Mutable(int id) {
+			this.id = id;
+		}
+
+		public void mutate() {
+			id++;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Mutable that = (Mutable) obj;
+			return this.id == that.id;
+		}
+
+		public int hashCode() {
+			return id;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableMapLabelProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableMapLabelProviderTest.java
new file mode 100644
index 0000000..3f2ae6b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableMapLabelProviderTest.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 164247
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import java.util.HashSet;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.examples.databinding.ModelObject;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.1
+ */
+public class ObservableMapLabelProviderTest extends AbstractDefaultRealmTestCase {
+    
+    public void testGetColumnText() throws Exception {
+        WritableSet set = new WritableSet(new HashSet(), Item.class);
+        Item item = new Item();
+        String value = "value";
+        item.setValue(value);
+        set.add(item);
+        
+        ObservableMapLabelProvider labelProvider = new ObservableMapLabelProvider(BeansObservables.observeMap(set, Item.class, "value"));
+        assertEquals(item.getValue(), labelProvider.getColumnText(item, 0));
+    }
+    
+    public void testGetColumnTextNullValue() throws Exception {
+        WritableSet set = new WritableSet(new HashSet(), Item.class);
+        Item item = new Item();
+        set.add(item);   
+        
+        ObservableMapLabelProvider labelProvider = new ObservableMapLabelProvider(BeansObservables.observeMap(set, Item.class, "value"));
+        assertNull(item.getValue());
+        assertEquals("", labelProvider.getColumnText(item, 0));
+    }
+    
+    private static class Item extends ModelObject {
+        private String value;
+        
+        public String getValue() {
+            return value;
+        }
+        
+        public void setValue(String value) {
+            String old = this.value;
+            
+            firePropertyChange("value", old, this.value = value);
+        }
+    }
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableSetContentProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableSetContentProviderTest.java
new file mode 100644
index 0000000..bf5ca76
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableSetContentProviderTest.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Boris Bokowski, IBM - bug 209484
+ *     Matthew Hall - bug 263956
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableSetContentProvider;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.3
+ * 
+ */
+public class ObservableSetContentProviderTest extends AbstractSWTTestCase {
+	private Shell shell;
+	private TableViewer viewer;
+	private ObservableSetContentProvider contentProvider;
+	private IObservableSet input;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		viewer = new TableViewer(shell, SWT.NONE);
+
+		contentProvider = new ObservableSetContentProvider();
+		viewer.setContentProvider(contentProvider);
+
+		input = new WritableSet();
+		viewer.setInput(input);
+	}
+
+	protected void tearDown() throws Exception {
+		shell.dispose();
+		viewer = null;
+		input = null;
+		super.tearDown();
+	}
+
+	public void testKnownElements_Realm() throws Exception {
+		assertSame("realm for the known elements should be the SWT realm",
+				SWTObservables.getRealm(Display.getDefault()), contentProvider
+						.getKnownElements().getRealm());
+	}
+
+	public void testRealizedElements_Realm() {
+		assertSame("realm for the realized elements should be the SWT realm",
+				SWTObservables.getRealm(Display.getDefault()), contentProvider
+						.getRealizedElements().getRealm());
+	}
+
+	public void testKnownElementsAfterSetInput() {
+		assertEquals(0, contentProvider.getKnownElements().size());
+		Set newElements = new HashSet(Arrays.asList(new String[] { "one",
+				"two", "three" }));
+		WritableSet newInput = new WritableSet();
+		newInput.addAll(newElements);
+		viewer.setInput(newInput);
+		assertEquals(newElements, contentProvider.getKnownElements());
+	}
+
+	public void testViewerUpdate_RemoveElementAfterMutation() {
+		Mutable element = new Mutable(1);
+		input.add(element);
+
+		assertEquals(1, viewer.getTable().getItemCount());
+
+		element.id++;
+		input.clear();
+
+		assertEquals(0, viewer.getTable().getItemCount());
+	}
+
+	public void testInputChanged_ClearsKnownElements() {
+		Object element = new Object();
+		input.add(element);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+		assertEquals(Collections.singleton(element), knownElements);
+		viewer.setInput(Observables.emptyObservableSet());
+		assertEquals(Collections.EMPTY_SET, knownElements);
+	}
+
+	public void testInputChanged_ClearsRealizedElements() {
+		// Realized elements must be allocated before adding the element
+		// otherwise we'd have to spin the event loop to see the new element
+		IObservableSet realizedElements = contentProvider.getRealizedElements();
+
+		Object element = new Object();
+		input.add(element);
+
+		assertEquals(Collections.singleton(element), realizedElements);
+		viewer.setInput(Observables.emptyObservableSet());
+		assertEquals(Collections.EMPTY_SET, realizedElements);
+	}
+
+	static class Mutable {
+		public int id;
+
+		public Mutable(int id) {
+			this.id = id;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Mutable that = (Mutable) obj;
+			return this.id == that.id;
+		}
+
+		public int hashCode() {
+			return id;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableSetTreeContentProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableSetTreeContentProviderTest.java
new file mode 100644
index 0000000..18bebe1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableSetTreeContentProviderTest.java
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 263956
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider;
+import org.eclipse.jface.internal.databinding.viewers.ObservableViewerElementSet;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+
+public class ObservableSetTreeContentProviderTest extends
+		AbstractDefaultRealmTestCase {
+	private Shell shell;
+	private TreeViewer viewer;
+	private Tree tree;
+	private ObservableSetTreeContentProvider contentProvider;
+	private Object input;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		tree = new Tree(shell, SWT.NONE);
+		viewer = new TreeViewer(tree);
+		input = new Object();
+	}
+
+	protected void tearDown() throws Exception {
+		shell.dispose();
+		tree = null;
+		viewer = null;
+		input = null;
+		super.tearDown();
+	}
+
+	private void initContentProvider(IObservableFactory listFactory) {
+		contentProvider = new ObservableSetTreeContentProvider(listFactory, null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setInput(input);
+	}
+
+	public void testConstructor_NullArgumentThrowsException() {
+		try {
+			initContentProvider(null);
+			fail("Constructor should have thrown AssertionFailedException");
+		} catch (AssertionFailedException expected) {
+		}
+	}
+
+	public void testGetElements_ChangesFollowObservedList() {
+		final IObservableSet elements = new WritableSet();
+		final Object input = new Object();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return target == input ? elements : null;
+			}
+		});
+
+		assertTrue(Arrays.equals(new Object[0], contentProvider
+				.getElements("unknown input")));
+
+		Object element0 = new Object();
+		elements.add(element0);
+
+		assertTrue(Arrays.equals(new Object[] { element0 }, contentProvider
+				.getElements(input)));
+
+		Object element1 = new Object();
+		elements.add(element1);
+
+		List elementList = Arrays.asList(contentProvider.getElements(input));
+		assertEquals(2, elementList.size());
+		assertTrue(elementList.containsAll(Arrays.asList(new Object[] {
+				element0, element1 })));
+	}
+
+	public void testViewerUpdate_RemoveElementAfterMutation() {
+		IElementComparer comparer = new IElementComparer() {
+			public boolean equals(Object a, Object b) {
+				return a == b;
+			}
+
+			public int hashCode(Object element) {
+				return System.identityHashCode(element);
+			}
+		};
+		viewer.setComparer(comparer);
+
+		final IObservableSet children = ObservableViewerElementSet
+				.withComparer(Realm.getDefault(), null, comparer);
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				return target == input ? children : null;
+			}
+		});
+
+		Mutable element = new Mutable();
+		children.add(element);
+		assertEquals(1, tree.getItemCount());
+
+		element.mutate();
+		assertTrue(children.remove(element));
+		assertEquals(0, tree.getItemCount());
+	}
+
+	public void testInputChanged_ClearsKnownElements() {
+		input = new Object();
+		final Object input2 = new Object();
+
+		final IObservableSet children = new WritableSet();
+		final IObservableSet children2 = new WritableSet();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				if (target == input)
+					return children;
+				if (target == input2)
+					return children2;
+				return null;
+			}
+		});
+
+		Object element = new Object();
+		children.add(element);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+		assertEquals(Collections.singleton(element), knownElements);
+		viewer.setInput(input2);
+		assertEquals(Collections.EMPTY_SET, knownElements);
+	}
+
+	public void testInputChanged_ClearsRealizedElements() {
+		input = new Object();
+		final Object input2 = new Object();
+
+		final IObservableSet children = new WritableSet();
+		final IObservableSet children2 = new WritableSet();
+		initContentProvider(new IObservableFactory() {
+			public IObservable createObservable(Object target) {
+				if (target == input)
+					return children;
+				if (target == input2)
+					return children2;
+				return null;
+			}
+		});
+
+		// Realized elements must be allocated before adding the element
+		// otherwise we'd have to spin the event loop to see the new element
+		IObservableSet realizedElements = contentProvider.getRealizedElements();
+
+		Object element = new Object();
+		children.add(element);
+
+		assertEquals(Collections.singleton(element), realizedElements);
+		viewer.setInput(input2);
+		assertEquals(Collections.EMPTY_SET, realizedElements);
+	}
+
+	static class Mutable {
+		private int id;
+
+		public Mutable() {
+			this(0);
+		}
+
+		public Mutable(int id) {
+			this.id = id;
+		}
+
+		public void mutate() {
+			id++;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Mutable that = (Mutable) obj;
+			return this.id == that.id;
+		}
+
+		public int hashCode() {
+			return id;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableValueEditingSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableValueEditingSupportTest.java
new file mode 100644
index 0000000..c845d08
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ObservableValueEditingSupportTest.java
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 146397, 260337
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import org.eclipse.core.databinding.Binding;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class ObservableValueEditingSupportTest extends AbstractSWTTestCase {
+	private Shell shell;
+
+	private ObservableValueEditingSupportStub editingSupport;
+
+	private DataBindingContext dbc;
+
+	private TableViewer viewer;
+
+	private Bean bean;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.jface.tests.databinding.AbstractSWTTestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		shell = getShell();
+		dbc = new DataBindingContext();
+
+		viewer = new TableViewer(shell);
+		TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
+
+		editingSupport = new ObservableValueEditingSupportStub(viewer, dbc);
+		column.setEditingSupport(editingSupport);
+
+		WritableList input = WritableList.withElementType(String.class);
+		bean = new Bean();
+		bean.setValue("value");
+		input.add(bean);
+
+		// Bind viewer to input
+		ViewerSupport.bind(viewer, input, BeanProperties.value(Bean.class,
+				"value"));
+	}
+
+	public void testInitializeCellEditorValue_OrderOfOperations()
+			throws Exception {
+		assertEquals("precondition", 0, editingSupport.events.length());
+
+		viewer.editElement(bean, 0);
+		assertEquals(
+				"createCellEditorObservable createElementObservable createBinding",
+				editingSupport.events.toString());
+	}
+
+	public void testSaveCellEditorValue_UpdatesModel() throws Exception {
+		shell.open();
+
+		String newValue = bean.getValue() + "a";
+
+		viewer.editElement(bean, 0);
+		editingSupport.target.setValue(newValue);
+
+		// force the focus to leave the editor updating the value
+		closeCellEditor();
+
+		assertTrue(editingSupport.binding.isDisposed());
+		assertEquals(newValue, bean.getValue());
+	}
+
+	/**
+	 * 
+	 */
+	protected void closeCellEditor() {
+		editingSupport.text.notifyListeners(SWT.DefaultSelection, new Event());
+	}
+
+	public void testSaveCellEditorValue_IgnoreIfNotDirty() throws Exception {
+		String initialValue = bean.getValue();
+
+		shell.open();
+
+		viewer.editElement(bean, 0);
+
+		// force the focus to leave the editor updating the value
+		closeCellEditor();
+
+		assertTrue(editingSupport.binding.isDisposed());
+		assertEquals(initialValue, bean.getValue());
+	}
+
+	public void testDisposesBinding() throws Exception {
+		shell.open();
+
+		viewer.editElement(bean, 0);
+		assertFalse("precondition", editingSupport.binding.isDisposed());
+
+		closeCellEditor();
+		assertTrue(editingSupport.binding.isDisposed());
+	}
+
+	public void testDisposesTargetObservable() throws Exception {
+		shell.open();
+
+		viewer.editElement(bean, 0);
+		assertFalse("precondition", editingSupport.target.isDisposed());
+
+		closeCellEditor();
+		assertTrue(editingSupport.target.isDisposed());
+	}
+
+	public void testDisposesModelObservable() throws Exception {
+		shell.open();
+
+		viewer.editElement(bean, 0);
+		assertFalse("precondition", editingSupport.model.isDisposed());
+
+		closeCellEditor();
+		assertTrue(editingSupport.model.isDisposed());
+	}
+
+	public void testCanEdit_DefaultIsTrue() throws Exception {
+		assertTrue(editingSupport.canEdit(bean));
+	}
+
+	private static class ObservableValueEditingSupportStub extends
+			ObservableValueEditingSupport {
+		StringBuffer events = new StringBuffer();
+
+		Text text;
+
+		TextCellEditor editor;
+
+		Binding binding;
+
+		IObservableValue target;
+
+		IObservableValue model;
+
+		/**
+		 * @param viewer
+		 * @param dbc
+		 */
+		public ObservableValueEditingSupportStub(ColumnViewer viewer,
+				DataBindingContext dbc) {
+			super(viewer, dbc);
+			editor = new TextCellEditor((Composite) viewer.getControl());
+		}
+
+		protected boolean canEdit(Object element) {
+			return super.canEdit(element);
+		}
+
+		private void event(String event) {
+			if (events.length() > 0) {
+				events.append(" ");
+			}
+
+			events.append(event);
+		}
+
+		protected IObservableValue doCreateCellEditorObservable(
+				CellEditor cellEditor) {
+			event("createCellEditorObservable");
+
+			text = (Text) cellEditor.getControl();
+			return target = SWTObservables.observeText(cellEditor.getControl(),
+					SWT.NONE);
+		}
+
+		protected IObservableValue doCreateElementObservable(Object element,
+				ViewerCell cell) {
+			event("createElementObservable");
+			return model = BeansObservables.observeValue(element, "value");
+		}
+
+		protected Binding createBinding(IObservableValue target,
+				IObservableValue model) {
+			event("createBinding");
+
+			return binding = super.createBinding(target, model);
+		}
+
+		protected CellEditor getCellEditor(Object element) {
+			return editor;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ViewerSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ViewerSupportTest.java
new file mode 100644
index 0000000..97a7abd
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ViewerSupportTest.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 283428)
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.databinding.property.list.IListProperty;
+import org.eclipse.core.databinding.property.set.ISetProperty;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.databinding.util.ILogger;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.tests.internal.databinding.beans.Bean;
+import org.eclipse.jface.databinding.viewers.ViewerSupport;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.jface.util.ISafeRunnableRunner;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.AbstractTableViewer;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+
+public class ViewerSupportTest extends AbstractSWTTestCase {
+	private ILogger oldLog;
+	private ISafeRunnableRunner oldRunner;
+
+	private AbstractTableViewer structuredViewer;
+	private AbstractTreeViewer treeViewer;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		oldLog = Policy.getLog();
+		Policy.setLog(new ILogger() {
+			public void log(IStatus status) {
+				if (status.getException() != null)
+					throw new RuntimeException(status.getException());
+				fail("Unexpected status: " + status);
+			}
+		});
+
+		oldRunner = SafeRunnable.getRunner();
+		SafeRunnable.setRunner(new ISafeRunnableRunner() {
+			public void run(ISafeRunnable code) {
+				try {
+					code.run();
+				} catch (Exception e) {
+					throw new RuntimeException(e);
+				}
+			}
+		});
+	}
+
+	protected void tearDown() throws Exception {
+		if (structuredViewer != null)
+			structuredViewer.getControl().dispose();
+		if (treeViewer != null)
+			treeViewer.getControl().dispose();
+
+		Policy.setLog(oldLog);
+
+		SafeRunnable.setRunner(oldRunner);
+
+		super.tearDown();
+	}
+
+	private StructuredViewer getStructuredViewer() {
+		if (structuredViewer == null) {
+			structuredViewer = new TableViewer(getShell());
+		}
+		return structuredViewer;
+	}
+
+	private AbstractTreeViewer getTreeViewer() {
+		if (treeViewer == null)
+			treeViewer = new TreeViewer(getShell());
+		return treeViewer;
+	}
+
+	public void testBindList_Twice() {
+		StructuredViewer viewer = getStructuredViewer();
+		IObservableList input0 = WritableList.withElementType(Bean.class);
+		IObservableList input1 = WritableList.withElementType(Bean.class);
+		input0.add(new Bean("element0"));
+		input1.add(new Bean("element1"));
+		IValueProperty labelProp = BeanProperties.value(Bean.class, "value");
+		ViewerSupport.bind(viewer, input0, labelProp);
+		ViewerSupport.bind(viewer, input1, labelProp);
+	}
+
+	public void testBindSet_Twice() {
+		StructuredViewer viewer = getStructuredViewer();
+		IObservableSet input0 = WritableSet.withElementType(Bean.class);
+		IObservableSet input1 = WritableSet.withElementType(Bean.class);
+		input0.add(new Bean("element0"));
+		input1.add(new Bean("element1"));
+		IValueProperty labelProp = BeanProperties.value(Bean.class, "value");
+		ViewerSupport.bind(viewer, input0, labelProp);
+		ViewerSupport.bind(viewer, input1, labelProp);
+	}
+
+	public void testBindListTree_Twice() {
+		AbstractTreeViewer viewer = getTreeViewer();
+		Bean input0 = new Bean(Arrays.asList(new Bean[] { new Bean("elem0"),
+				new Bean("elem1"), new Bean("elem2") }));
+		Bean input1 = new Bean(Arrays.asList(new Bean[] { new Bean("elem3"),
+				new Bean("elem4"), new Bean("elem5") }));
+		IValueProperty labelProp = BeanProperties.value(Bean.class, "value");
+		IListProperty childrenProp = BeanProperties.list(Bean.class, "list");
+		ViewerSupport.bind(viewer, input0, childrenProp, labelProp);
+		ViewerSupport.bind(viewer, input1, childrenProp, labelProp);
+	}
+
+	public void testBindSetTree_Twice() {
+		AbstractTreeViewer viewer = getTreeViewer();
+		Bean input0 = new Bean(new HashSet(Arrays.asList(new Bean[] {
+				new Bean("elem0"), new Bean("elem1"), new Bean("elem2") })));
+		Bean input1 = new Bean(new HashSet(Arrays.asList(new Bean[] {
+				new Bean("elem3"), new Bean("elem4"), new Bean("elem5") })));
+		IValueProperty labelProp = BeanProperties.value(Bean.class, "value");
+		ISetProperty childrenProp = BeanProperties.set(Bean.class, "set");
+		ViewerSupport.bind(viewer, input0, childrenProp, labelProp);
+		ViewerSupport.bind(viewer, input1, childrenProp, labelProp);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ViewersObservablesTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ViewersObservablesTest.java
new file mode 100644
index 0000000..dffe20b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/viewers/ViewersObservablesTest.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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)
+ *     Matthew Hall - bug 194734
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.IDecoratingObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.property.IPropertyObservable;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.internal.databinding.viewers.ViewerInputProperty;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for ViewersObservables
+ * 
+ * @since 1.2
+ */
+public class ViewersObservablesTest extends AbstractDefaultRealmTestCase {
+	TableViewer viewer;
+	Realm realm;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		realm = SWTObservables.getRealm(Display.getCurrent());
+		Shell shell = new Shell();
+		viewer = new TableViewer(shell, SWT.NONE);
+	}
+
+	protected void tearDown() throws Exception {
+		Shell shell = viewer.getTable().getShell();
+		if (!shell.isDisposed())
+			shell.dispose();
+		shell = null;
+		realm = null;
+		super.tearDown();
+	}
+
+	public void testObserveInput_InstanceOfViewerInputObservableValue() {
+		IViewerObservableValue observable = (IViewerObservableValue) ViewersObservables
+				.observeInput(viewer);
+		assertTrue(observable.getViewer() == viewer);
+		IPropertyObservable propertyObservable = (IPropertyObservable) ((IDecoratingObservable) observable)
+				.getDecorated();
+		assertTrue(propertyObservable.getProperty() instanceof ViewerInputProperty);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/wizard/WizardPageSupportTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/wizard/WizardPageSupportTest.java
new file mode 100644
index 0000000..20571d5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/wizard/WizardPageSupportTest.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Ovidio Mallo and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ovidio Mallo - initial API and implementation (bug 235195)
+ *     Ovidio Mallo - bugs 237856, 248877
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.databinding.wizard;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.ValidationStatusProvider;
+import org.eclipse.core.databinding.observable.Diffs;
+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.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.commands.util.Util;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.databinding.dialog.ValidationMessageProvider;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @since 1.2
+ */
+public class WizardPageSupportTest extends AbstractSWTTestCase {
+
+	/**
+	 * Bug 235195.
+	 */
+	public void testPageComplete() {
+		IWizardPage page = new WizardPage("Page") {
+			public void createControl(Composite parent) {
+				setControl(parent);
+
+				IObservableValue validation = new WritableValue(
+						ValidationStatus.ok(), IStatus.class);
+
+				DataBindingContext dbc = new DataBindingContext();
+				ValidationProvider validationProvider = new ValidationProvider(
+						validation);
+				dbc.addValidationStatusProvider(validationProvider);
+
+				WizardPageSupport.create(this, dbc);
+
+				assertTrue(isPageComplete());
+
+				validation.setValue(ValidationStatus.info("INFO"));
+				assertTrue(isPageComplete());
+
+				validation.setValue(ValidationStatus.warning("WARNING"));
+				assertTrue(isPageComplete());
+
+				validation.setValue(ValidationStatus.error("ERROR"));
+				assertFalse(isPageComplete());
+
+				validation.setValue(ValidationStatus.cancel("CANCEL"));
+				assertFalse(isPageComplete());
+			}
+		};
+
+		loadWizardPage(page);
+	}
+
+	public void testPageCompleteOnValidationStaleness() {
+		IWizardPage page = new WizardPage("Page") {
+			public void createControl(Composite parent) {
+				setControl(parent);
+
+				ValidationObservable validation = new ValidationObservable();
+
+				DataBindingContext dbc = new DataBindingContext();
+				dbc.addValidationStatusProvider(new ValidationProvider(
+						validation));
+
+				WizardPageSupport.create(this, dbc);
+
+				assertTrue(isPageComplete());
+
+				validation.setStale(true);
+				assertFalse(isPageComplete());
+
+				validation.setStale(false);
+				assertTrue(isPageComplete());
+			}
+		};
+
+		loadWizardPage(page);
+	}
+
+	public void testValidationMessageProvider() {
+		IWizardPage page = new WizardPage("Page") {
+			public void createControl(Composite parent) {
+				setControl(parent);
+
+				ValidationObservable validation = new ValidationObservable();
+
+				DataBindingContext dbc = new DataBindingContext();
+				dbc.addValidationStatusProvider(new ValidationProvider(
+						validation));
+
+				WizardPageSupport wizardPageSupport = WizardPageSupport.create(
+						this, dbc);
+				TestValidationMessageProvider messageProvider = new TestValidationMessageProvider();
+				wizardPageSupport.setValidationMessageProvider(messageProvider);
+
+				// We have an info message but display a warning with a
+				// different text.
+				messageProvider.message = "message1";
+				messageProvider.messageType = IMessageProvider.WARNING;
+
+				validation.setValue(ValidationStatus.info("INFO"));
+				assertEquals(messageProvider.message, getMessage());
+				assertEquals(messageProvider.messageType, getMessageType());
+				assertNull(getErrorMessage());
+				assertTrue(isPageComplete());
+
+				// We have an error which, however, is no displayed as such, so
+				// the error message on the wizard page must be null.
+				// Nevertheless,
+				// the page must *not* be marked as complete!
+				messageProvider.message = "message2";
+				messageProvider.messageType = IMessageProvider.NONE;
+
+				validation.setValue(ValidationStatus.error("ERROR"));
+				assertEquals(messageProvider.message, getMessage());
+				assertEquals(messageProvider.messageType, getMessageType());
+				assertNull(getErrorMessage());
+				assertFalse(isPageComplete());
+
+				// null should be allowed as message.
+				messageProvider.message = null;
+
+				validation.setValue(ValidationStatus.ok());
+				assertEquals(messageProvider.message, getMessage());
+				assertEquals(messageProvider.messageType, getMessageType());
+				assertNull(getErrorMessage());
+				assertTrue(isPageComplete());
+
+				// Errors should be displayed using setErrorMessage().
+				messageProvider.message = "message3";
+				messageProvider.messageType = IMessageProvider.ERROR;
+
+				validation.setValue(ValidationStatus.error("ERROR"));
+				assertNull(getMessage());
+				assertEquals(IMessageProvider.NONE, getMessageType());
+				assertEquals(messageProvider.message, getErrorMessage());
+				assertFalse(isPageComplete());
+			}
+		};
+
+		loadWizardPage(page);
+	}
+
+	private void loadWizardPage(IWizardPage page) {
+		Wizard wizard = new Wizard() {
+			public boolean performFinish() {
+				return true;
+			}
+		};
+		wizard.addPage(page);
+
+		WizardDialog dialog = new WizardDialog(getShell(), wizard);
+		dialog.create();
+	}
+
+	private static class ValidationObservable extends AbstractObservableValue {
+
+		private Object value = ValidationStatus.ok();
+
+		private boolean stale = false;
+
+		public ValidationObservable() {
+			super(Realm.getDefault());
+		}
+
+		protected Object doGetValue() {
+			return value;
+		}
+
+		protected void doSetValue(Object value) {
+			Object oldValue = this.value;
+			this.value = value;
+			if (!Util.equals(oldValue, value)) {
+				fireValueChange(Diffs.createValueDiff(oldValue, value));
+			}
+		}
+
+		public boolean isStale() {
+			ObservableTracker.getterCalled(this);
+			return stale;
+		}
+
+		public void setStale(boolean stale) {
+			if (this.stale != stale) {
+				this.stale = stale;
+				if (stale) {
+					fireStale();
+				} else {
+					fireValueChange(Diffs.createValueDiff(value, value));
+				}
+			}
+		}
+
+		public Object getValueType() {
+			return IStatus.class;
+		}
+	}
+
+	private static class ValidationProvider extends ValidationStatusProvider {
+
+		private final IObservableValue validation;
+
+		public ValidationProvider(IObservableValue validation) {
+			this.validation = validation;
+		}
+
+		public IObservableValue getValidationStatus() {
+			return validation;
+		}
+
+		public IObservableList getTargets() {
+			WritableList targets = new WritableList();
+			targets.add(validation);
+			return targets;
+		}
+
+		public IObservableList getModels() {
+			return Observables.emptyObservableList();
+		}
+	}
+
+	private static class TestValidationMessageProvider extends
+			ValidationMessageProvider {
+
+		public String message;
+
+		public int messageType;
+
+		public String getMessage(ValidationStatusProvider statusProvider) {
+			return message;
+		}
+
+		public int getMessageType(ValidationStatusProvider statusProvider) {
+			return messageType;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/databinding/mask/internal/EditMaskLexerAndTokenTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/databinding/mask/internal/EditMaskLexerAndTokenTest.java
new file mode 100755
index 0000000..040ae6c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/databinding/mask/internal/EditMaskLexerAndTokenTest.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+package org.eclipse.jface.tests.examples.databinding.mask.internal;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jface.examples.databinding.mask.internal.EditMaskLexerAndToken;
+
+public class EditMaskLexerAndTokenTest extends TestCase {
+
+	private EditMaskLexerAndToken token;
+	
+	protected void setUp() throws Exception {
+		token = new EditMaskLexerAndToken();
+	}
+
+	public void testInitWithNumeric() throws Exception {
+		token.initializeEditMask("#", 0);
+		assertTrue("Should accept a digit", token.accept("0"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("1"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("2"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("3"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("4"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("5"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("6"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("7"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("8"));
+		token.clear();
+		assertTrue("Should accept a digit", token.accept("9"));
+		token.clear();
+		assertFalse("Should not accept an alpha", token.accept("A"));
+		token.clear();
+		assertFalse("Should not accept an alpha", token.accept("z"));
+		assertFalse("Placeholders are not read-only", token.isReadOnly());
+	}
+	
+	public void testInitWithLiteral() throws Exception {
+		token.initializeEditMask("(", 0);
+		assertEquals("Literals automatically set their input", "(", token.getInput());
+		assertFalse("Literals don't accept anything", token.accept("("));
+		assertTrue("literals are read-only", token.isReadOnly());
+		assertTrue("Literals are complete", token.isComplete());
+		assertFalse("Literals cannot accept characters", token.canAcceptMoreCharacters());
+	}
+	
+	public void testInitWithBackslashLiteral() throws Exception {
+		token.initializeEditMask("\\#", 0);
+		assertEquals("Should get backslash literal", "#", token.getInput());
+	}
+	
+	public void testAcceptWithValidInputAndEmpty() throws Exception {
+		token.initializeEditMask("#", 0);
+		assertTrue("Should accept a 0", token.accept("0"));
+	}
+	
+	public void testAcceptWhenParserCannotAcceptMoreCharacters() throws Exception {
+		token.initializeEditMask("#", 0);
+		assertTrue("Should accept a 0", token.accept("0"));
+		assertFalse("Should not accept a 0 -- input full", token.accept("0"));
+	}
+	
+	public void testGetInput() throws Exception {
+		token.initializeEditMask("#", 0);
+		assertTrue("Should accept a #", token.accept("0"));
+		assertEquals(token.getInput(), "0");
+	}
+	
+	public void testClear_withNonLiteral() throws Exception {
+		token.initializeEditMask("#", 0);
+		assertTrue("Should accept a 0", token.accept("0"));
+		assertNotNull("Input should not be null", token.getInput());
+		token.clear();
+		assertNull("Input should be null after clear", token.getInput());
+	}
+	
+	public void testClear_withLiteral() throws Exception {
+		token.initializeEditMask("(", 0);
+		assertNotNull("Input should not be null", token.getInput());
+		token.clear();
+		assertNotNull("Input should still not be null after clear of read-only literal", token.getInput());
+	}
+	
+	public void testIsComplete_withNonLiteral() throws Exception {
+		token.initializeEditMask("#", 0);
+		assertFalse("should not be complete", token.isComplete());
+		token.accept("1");
+		assertTrue("should be complete", token.isComplete());
+		token.clear();
+		assertFalse("should not be complete", token.isComplete());
+	}
+
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/databinding/mask/internal/EditMaskParserTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/databinding/mask/internal/EditMaskParserTest.java
new file mode 100755
index 0000000..8a2ceb4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/databinding/mask/internal/EditMaskParserTest.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.examples.databinding.mask.internal;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jface.examples.databinding.mask.EditMaskParseException;
+import org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser;
+
+/**
+ * @since 3.2
+ *
+ */
+public class EditMaskParserTest extends TestCase {
+
+	private EditMaskParser parser;
+
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		parser = new EditMaskParser("(###) ###-####");
+	}
+
+	/**
+	 * Test method for {@link org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser#EditMaskParser(java.lang.String)}.
+	 */
+	public void testEditMaskParser_validMask() {
+		new EditMaskParser("(###) ###-####");
+	}
+
+	/**
+	 * Test method for {@link org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser#EditMaskParser(java.lang.String)}.
+	 */
+	public void testEditMaskParser_invalidMask() {
+		try {
+			new EditMaskParser("(###) ###-####\\");
+			fail("Should have thrown exception");
+		} catch (EditMaskParseException e) {
+			// success
+		}
+	}
+
+	/**
+	 * Test method for {@link org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser#setInput(java.lang.String)}.
+	 */
+	public void testSetInput() {
+		parser.setInput("63a0) 5*55-1\\212abc9");
+		assertEquals("Unformatted input", "6305551212", parser.getRawResult());
+		assertEquals("Formatted input", "(630) 555-1212", parser.getFormattedResult());
+	}
+
+	/**
+	 * Test method for {@link org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser#setInput(java.lang.String)}.
+	 */
+	public void testSetInput_incomplete() {
+		parser.setInput("6a0) 5*5-1\\12");
+		assertEquals("Unformatted input", "6055112", parser.getRawResult());
+		assertEquals("Formatted input", "(605) 511-2   ", parser.getFormattedResult());
+	}
+
+	/**
+	 * Test method for {@link org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser#isComplete()}.
+	 */
+	public void testIsComplete() {
+		parser.setInput("63a0) 5*55-1\\212");
+		assertTrue("complete", parser.isComplete());
+		parser.setInput("6a0) 5*5-1\\12");
+		assertFalse("incomplete", parser.isComplete());
+	}
+	
+	public void testSetPlaceholder() throws Exception {
+		parser.setInput("6a0) 5*5-1\\12");
+		assertEquals("Formatted input", "(605) 511-2   ", parser.getFormattedResult());
+		parser.setPlaceholder('_');
+		assertEquals("Formatted input", "(605) 511-2___", parser.getFormattedResult());
+	}
+
+	/**
+	 * Test method for {@link org.eclipse.jface.examples.databinding.mask.internal.EditMaskParser#getNextInputPosition(int)}.
+	 */
+	public void testGetNextInputPosition() {
+		assertEquals("Skip leading (", 1, parser.getNextInputPosition(0));
+		assertEquals("Position 1 is good", 1, parser.getNextInputPosition(1));
+		assertEquals("Skip )<space>", 6, parser.getNextInputPosition(4));
+	}
+	
+	public void testGetFirstIncompleteInputPosition() throws Exception {
+		assertEquals("1st position incomplete", 1, parser.getFirstIncompleteInputPosition());
+		parser.setInput("6a0) 5*5-1\\12");
+		assertEquals("11th position incomplete", 11, parser.getFirstIncompleteInputPosition());
+		parser.setInput("63a0) 5*55-1\\212");
+		assertEquals("all complete", -1, parser.getFirstIncompleteInputPosition());
+	}
+}
+
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/model/PersonTests.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/model/PersonTests.java
new file mode 100644
index 0000000..1c9b9c1
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/examples/model/PersonTests.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2006 Brad Reynolds.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.examples.model;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jface.examples.databinding.model.SimplePerson;
+
+/**
+ * @since 3.2
+ *
+ */
+public class PersonTests extends TestCase {
+	public void testSetName() {
+		SimplePerson person = new SimplePerson();
+		Listener listener = new Listener();
+		
+		person.addPropertyChangeListener(listener);
+		assertEquals(0, listener.count);
+		assertNull(listener.lastEvent);
+		person.setState("new state"); //$NON-NLS-1$
+		
+		assertEquals(1, listener.count);
+		assertNotNull(listener.lastEvent);
+		assertEquals("state", listener.lastEvent.getPropertyName()); //$NON-NLS-1$
+		assertEquals("", listener.lastEvent.getOldValue());
+		assertEquals("new state", listener.lastEvent.getNewValue()); //$NON-NLS-1$
+		assertEquals("new state", person.getState());
+	}
+	
+	private class Listener implements PropertyChangeListener {
+		private int count;
+		private PropertyChangeEvent lastEvent;
+		
+		public void propertyChange(PropertyChangeEvent evt) {
+			count++;
+			this.lastEvent = evt;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ButtonObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ButtonObservableValueTest.java
new file mode 100644
index 0000000..68596e6
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ButtonObservableValueTest.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class ButtonObservableValueTest extends AbstractSWTTestCase {
+	private Button button;
+	private ISWTObservableValue observableValue;
+	private ValueChangeEventTracker listener;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		Shell shell = getShell();
+		button = new Button(shell, SWT.CHECK);
+		observableValue = SWTObservables.observeSelection(button);
+		listener = new ValueChangeEventTracker();
+	}
+
+	public void testSelection_ChangeNotifiesObservable() throws Exception {
+		observableValue.addValueChangeListener(listener);
+		button.setSelection(true);
+
+		// precondition
+		assertEquals(0, listener.count);
+		button.notifyListeners(SWT.Selection, null);
+
+		assertEquals("Selection event should notify observable.", 1,
+				listener.count);
+	}
+
+	public void testSelection_NoChange() throws Exception {
+		button.setSelection(true);
+		button.notifyListeners(SWT.Selection, null);
+		observableValue.addValueChangeListener(listener);
+
+		// precondition
+		assertEquals(0, listener.count);
+
+		button.notifyListeners(SWT.Selection, null);
+		assertEquals(
+				"Value did not change.  Listeners should not have been notified.",
+				0, listener.count);
+	}
+
+	public void testSetValue_NullConvertedToFalse() {
+		button.setSelection(true);
+		assertEquals(Boolean.TRUE, observableValue.getValue());
+
+		observableValue.setValue(null);
+		assertEquals(Boolean.FALSE, observableValue.getValue());
+	}
+
+	public void testDispose() throws Exception {
+		ValueChangeEventTracker testCounterValueChangeListener = new ValueChangeEventTracker();
+		observableValue.addValueChangeListener(testCounterValueChangeListener);
+
+		assertEquals(Boolean.FALSE, observableValue.getValue());
+		assertFalse(button.getSelection());
+
+		button.setSelection(true);
+		button.notifyListeners(SWT.Selection, null);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(Boolean.TRUE, observableValue.getValue());
+		assertTrue(button.getSelection());
+
+		observableValue.dispose();
+
+		button.setSelection(false);
+		button.notifyListeners(SWT.Selection, null);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ButtonObservableValueTest.class
+				.getName());
+		suite.addTestSuite(ButtonObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		Shell shell;
+
+		Button button;
+
+		public void setUp() {
+			super.setUp();
+
+			shell = new Shell();
+			button = new Button(shell, SWT.CHECK);
+		}
+
+		public void tearDown() {
+			super.tearDown();
+
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.selection().observe(realm, button);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Boolean.TYPE;
+		}
+
+		public void change(IObservable observable) {
+			((IObservableValue) observable).setValue(Boolean
+					.valueOf(changeValue(button)));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return Boolean.valueOf(changeValue(button));
+		}
+
+		private boolean changeValue(Button button) {
+			return !button.getSelection();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueSelectionTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueSelectionTest.java
new file mode 100644
index 0000000..f42b743
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueSelectionTest.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservable;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class CComboObservableValueSelectionTest extends TestCase {
+	private Delegate delegate;
+
+	private CCombo combo;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = new Delegate();
+		delegate.setUp();
+		combo = delegate.combo;
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		delegate.tearDown();
+	}
+
+	public void testSelection_NotifiesObservable() throws Exception {
+		IObservableValue observable = (IObservableValue) delegate
+				.createObservable(SWTObservables.getRealm(Display.getDefault()));
+
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+		combo.select(0);
+
+		assertEquals("Observable was not notified.", 1, listener.count);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				CComboObservableValueSelectionTest.class.getName());
+		suite.addTestSuite(CComboObservableValueSelectionTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		/* package */CCombo combo;
+
+		public void setUp() {
+			shell = new Shell();
+			combo = new CCombo(shell, SWT.NONE);
+			combo.add("a");
+			combo.add("b");
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.selection().observe(realm, combo);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue ov = (IObservableValue) observable;
+			ov.setValue(createValue(ov));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			CCombo combo = ((CCombo) ((ISWTObservable) observable).getWidget());
+			switch (combo.getSelectionIndex()) {
+			case -1:
+				// fall thru
+			case 1:
+				return combo.getItem(0);
+			case 0:
+				return combo.getItem(1);
+			default:
+				throw new RuntimeException("Unexpected selection.");
+			}
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueTest.java
new file mode 100644
index 0000000..035efbd
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Eric Rizzo - bug 134884
+ *     Matthew Hall - bug 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+
+/**
+ * @since 3.2
+ */
+public class CComboObservableValueTest extends AbstractSWTTestCase {
+	public void testDispose() throws Exception {
+		CCombo combo = new CCombo(getShell(), SWT.NONE);
+		ISWTObservableValue observableValue = SWTObservables.observeText(combo);
+
+		ValueChangeEventTracker testCounterValueChangeListener = new ValueChangeEventTracker();
+		observableValue.addValueChangeListener(testCounterValueChangeListener);
+
+		assertEquals("", combo.getText());
+		assertEquals("", observableValue.getValue());
+
+		String expected1 = "Test123";
+		combo.setText(expected1);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected1, combo.getText());
+		assertEquals(expected1, observableValue.getValue());
+
+		observableValue.dispose();
+
+		String expected2 = "NewValue123";
+		combo.setText(expected2);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected2, combo.getText());
+	}
+
+	public void testSetValueWithNull() {
+		testSetValueWithNull(WidgetProperties.text());
+		testSetValueWithNull(WidgetProperties.selection());
+	}
+
+	protected void testSetValueWithNull(IValueProperty property) {
+		CCombo combo = new CCombo(getShell(), SWT.NONE);
+		combo.setItems(new String[] { "one", "two", "three" });
+		IObservableValue observable = property.observe(Realm.getDefault(),
+				combo);
+
+		observable.setValue("two");
+		assertEquals("two", combo.getText());
+		assertEquals(1, combo.getSelectionIndex());
+
+		observable.setValue(null);
+		assertEquals("", combo.getText());
+		assertEquals(-1, combo.getSelectionIndex());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueTextTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueTextTest.java
new file mode 100644
index 0000000..eb2060f
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboObservableValueTextTest.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class CComboObservableValueTextTest extends TestCase {
+	private Delegate delegate;
+
+	private CCombo combo;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = new Delegate();
+		delegate.setUp();
+		combo = delegate.combo;
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		delegate.tearDown();
+	}
+
+	public void testModify_NotifiesObservable() throws Exception {
+		IObservableValue observable = delegate
+				.createObservableValue(SWTObservables.getRealm(Display
+						.getDefault()));
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+
+		combo.setText((String) delegate.createValue(observable));
+
+		assertEquals("Observable was not notified.", 1, listener.count);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(CComboObservableValueTextTest.class
+				.getName());
+		suite.addTestSuite(CComboObservableValueTextTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		/* package */CCombo combo;
+
+		private Shell shell;
+
+		public void setUp() {
+			shell = new Shell();
+			combo = new CCombo(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text().observe(realm, combo);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue ov = (IObservableValue) observable;
+			ov.setValue(createValue(ov));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return observable.getValue() + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboSingleSelectionObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboSingleSelectionObservableValueTest.java
new file mode 100644
index 0000000..19edf2d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CComboSingleSelectionObservableValueTest.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198903
+ *     Matthew Hall - bug 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CCombo;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class CComboSingleSelectionObservableValueTest extends
+		AbstractSWTTestCase {
+	public void testSetValue() throws Exception {
+		CCombo combo = new CCombo(getShell(), SWT.NONE);
+		IObservableValue observableValue = SWTObservables
+				.observeSingleSelectionIndex(combo);
+		combo.add("Item1");
+		combo.add("Item2");
+
+		assertEquals(-1, combo.getSelectionIndex());
+		assertEquals(-1, ((Integer) observableValue.getValue()).intValue());
+
+		Integer value = new Integer(1);
+		observableValue.setValue(value);
+		assertEquals("combo selection index", value.intValue(), combo
+				.getSelectionIndex());
+		assertEquals("observable value", value, observableValue.getValue());
+
+		assertEquals("Item2", combo.getText());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				CComboSingleSelectionObservableValueTest.class.getName());
+		suite.addTestSuite(CComboSingleSelectionObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private CCombo combo;
+		private Shell shell;
+
+		public void setUp() {
+			shell = new Shell();
+			combo = new CCombo(shell, SWT.NONE);
+			combo.add("0");
+			combo.add("1");
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.singleSelectionIndex()
+					.observe(realm, combo);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue value = (IObservableValue) observable;
+			value.setValue(createValue(value));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Integer(_createValue(observable));
+		}
+
+		private int _createValue(IObservableValue observable) {
+			int value = Math.max(0, combo.getSelectionIndex());
+
+			// returns either 0 or 1 depending upon current value
+			return Math.abs(value - 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CLabelObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CLabelObservableValueTest.java
new file mode 100644
index 0000000..cf8b797
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/CLabelObservableValueTest.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class CLabelObservableValueTest extends TestCase {
+	private Delegate delegate;
+	private IObservableValue observable;
+	private CLabel label;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = new Delegate();
+		delegate.setUp();
+		label = delegate.label;
+		observable = delegate.createObservableValue(SWTObservables
+				.getRealm(Display.getDefault()));
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		delegate.tearDown();
+		observable.dispose();
+	}
+
+	public void testSetValue() throws Exception {
+		// preconditions
+		assertEquals(null, label.getText());
+		assertEquals(null, observable.getValue());
+
+		String value = "value";
+		observable.setValue(value);
+		assertEquals("label text", value, label.getText());
+		assertEquals("observable value", value, observable.getValue());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(CLabelObservableValueTest.class
+				.getName());
+		suite.addTestSuite(CLabelObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		CLabel label;
+
+		public void setUp() {
+			shell = new Shell();
+			label = new CLabel(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text().observe(realm, label);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue value = (IObservableValue) observable;
+			value.setValue(value.getValue() + "a");
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return observable.getValue() + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueSelectionTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueSelectionTest.java
new file mode 100644
index 0000000..594fa46
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueSelectionTest.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservable;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ComboObservableValueSelectionTest extends TestCase {
+	private Delegate delegate;
+
+	private Combo combo;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = new Delegate();
+		delegate.setUp();
+		combo = delegate.combo;
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		delegate.tearDown();
+	}
+
+	public void testSelection_NotifiesObservable() throws Exception {
+		IObservableValue observable = (IObservableValue) delegate
+				.createObservable(SWTObservables.getRealm(Display.getDefault()));
+
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+		combo.select(0);
+		combo.notifyListeners(SWT.Selection, null);
+
+		assertEquals("Observable was not notified.", 1, listener.count);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ComboObservableValueSelectionTest.class
+				.toString());
+		suite.addTestSuite(ComboObservableValueSelectionTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		/* package */Combo combo;
+
+		public void setUp() {
+			shell = new Shell();
+			combo = new Combo(shell, SWT.NONE);
+			combo.add("a");
+			combo.add("b");
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.selection().observe(realm, combo);
+		}
+
+		public void change(IObservable observable) {
+			int index = combo
+					.indexOf((String) createValue((IObservableValue) observable));
+
+			((IObservableValue) observable).setValue(combo.getItem(index));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			Combo combo = ((Combo) ((ISWTObservable) observable).getWidget());
+			switch (combo.getSelectionIndex()) {
+			case -1:
+				// fall thru
+			case 1:
+				return combo.getItem(0);
+			case 0:
+				return combo.getItem(1);
+			default:
+				throw new RuntimeException("Unexpected selection.");
+			}
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueTest.java
new file mode 100644
index 0000000..31b60ef
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueTest.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.internal.databinding.swt.ComboSelectionProperty;
+import org.eclipse.jface.internal.databinding.swt.ComboTextProperty;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * @since 3.2
+ * @no
+ */
+public class ComboObservableValueTest extends AbstractSWTTestCase {
+	public void testDispose() throws Exception {
+		Combo combo = new Combo(getShell(), SWT.NONE);
+		IObservableValue observableValue = SWTObservables.observeText(combo);
+		ValueChangeEventTracker testCounterValueChangeListener = new ValueChangeEventTracker();
+		observableValue.addValueChangeListener(testCounterValueChangeListener);
+
+		assertEquals("", combo.getText());
+		assertEquals("", observableValue.getValue());
+
+		String expected1 = "Test123";
+		combo.setText(expected1);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected1, combo.getText());
+		assertEquals(expected1, observableValue.getValue());
+
+		observableValue.dispose();
+
+		String expected2 = "NewValue123";
+		combo.setText(expected2);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected2, combo.getText());
+	}
+
+	public void testSetValueWithNull() {
+		testSetValueWithNull(WidgetProperties.text());
+		testSetValueWithNull(WidgetProperties.selection());
+	}
+
+	protected void testSetValueWithNull(IValueProperty property) {
+		Combo combo = new Combo(getShell(), SWT.NONE);
+		combo.setItems(new String[] { "one", "two", "three" });
+		IObservableValue observable = property.observe(Realm.getDefault(),
+				combo);
+
+		observable.setValue("two");
+		assertEquals("two", combo.getText());
+		if (property instanceof ComboSelectionProperty) {
+			assertEquals("expect selection at index 1 in selection mode", 1,
+					combo.getSelectionIndex());
+		}
+
+		if (property instanceof ComboTextProperty) {
+			observable.setValue(null);
+			assertEquals("expect empty text in text mode", "", combo.getText());
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueTextTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueTextTest.java
new file mode 100644
index 0000000..5af6816
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboObservableValueTextTest.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ComboObservableValueTextTest extends TestCase {
+	private Delegate delegate;
+
+	private Combo combo;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = new Delegate();
+		delegate.setUp();
+		combo = delegate.combo;
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+
+		delegate.tearDown();
+	}
+
+	public void testModify_NotifiesObservable() throws Exception {
+		IObservableValue observable = delegate
+				.createObservableValue(SWTObservables.getRealm(Display
+						.getDefault()));
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+
+		combo.setText((String) delegate.createValue(observable));
+
+		assertEquals("Observable was not notified.", 1, listener.count);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ComboObservableValueTextTest.class
+				.toString());
+		suite.addTestSuite(ComboObservableValueTextTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		/* package */Combo combo;
+
+		private Shell shell;
+
+		public void setUp() {
+			shell = new Shell();
+			combo = new Combo(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text().observe(realm, combo);
+		}
+
+		public void change(IObservable observable) {
+			((IObservableValue) observable)
+					.setValue(createValue((IObservableValue) observable));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return observable.getValue() + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboSingleSelectionObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboSingleSelectionObservableValueTest.java
new file mode 100644
index 0000000..680f20d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ComboSingleSelectionObservableValueTest.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Ashley Cambrell and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ashley Cambrell - initial API and implementation (bug 198903)
+ *     Matthew Hall - bug 194734
+ ******************************************************************************/
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+/**
+ * @since 3.2
+ *
+ */
+public class ComboSingleSelectionObservableValueTest extends
+		AbstractSWTTestCase {
+	public void testSetValue() throws Exception {
+		Combo combo = new Combo(getShell(), SWT.NONE);
+		IObservableValue observableValue = SWTObservables
+				.observeSingleSelectionIndex(combo);
+		combo.add("Item1");
+		combo.add("Item2");
+
+		assertEquals(-1, combo.getSelectionIndex());
+		assertEquals(-1, ((Integer) observableValue.getValue()).intValue());
+
+		Integer value = new Integer(1);
+		observableValue.setValue(value);
+		assertEquals("combo selection index", value.intValue(), combo
+				.getSelectionIndex());
+		assertEquals("observable value", value, observableValue.getValue());
+
+		assertEquals("Item2", combo.getText());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ControlObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ControlObservableValueTest.java
new file mode 100644
index 0000000..79366a0
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ControlObservableValueTest.java
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 170848
+ *     Matthew Hall - bug 194734
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ControlObservableValueTest extends AbstractDefaultRealmTestCase {
+	private Shell shell;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		
+		shell = new Shell();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	protected void tearDown() throws Exception {
+		if (shell != null && !shell.isDisposed()) {
+			shell.dispose();
+			shell = null;
+		}
+	}
+
+	public void testSetValueEnabled() throws Exception {
+		ISWTObservableValue observableValue = SWTObservables
+				.observeEnabled(shell);
+		Boolean value = Boolean.FALSE;
+		observableValue.setValue(value);
+		assertFalse(shell.isEnabled());
+	}
+
+	public void testGetValueEnabled() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeEnabled(shell);
+		shell.setEnabled(false);
+		assertEquals(Boolean.FALSE, value.getValue());
+	}
+
+	public void testGetValueTypeEnabled() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeEnabled(shell);
+		assertEquals(boolean.class, value.getValueType());
+	}
+
+	public void testSetValueVisible() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeVisible(shell);
+		value.setValue(Boolean.FALSE);
+		assertFalse(shell.isVisible());
+	}
+
+	public void testGetValueVisible() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeVisible(shell);
+		shell.setVisible(false);
+		assertEquals(Boolean.FALSE, value.getValue());
+	}
+
+	public void testGetValueTypeVisible() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeVisible(shell);
+		assertEquals(Boolean.TYPE, value.getValueType());
+	}
+
+	public void testSetValueForeground() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeForeground(shell);
+
+		Color color = shell.getDisplay().getSystemColor(SWT.COLOR_BLACK);
+
+		value.setValue(color);
+		assertEquals(color, shell.getForeground());
+	}
+
+	public void testGetValueForeground() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeForeground(shell);
+
+		Color color = shell.getDisplay().getSystemColor(SWT.COLOR_BLACK);
+		shell.setForeground(color);
+		assertEquals(color, value.getValue());
+	}
+
+	public void testGetValueTypeForgroundColor() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeForeground(shell);
+		assertEquals(Color.class, value.getValueType());
+	}
+
+	public void testGetValueBackground() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeBackground(shell);
+
+		Color color = shell.getDisplay().getSystemColor(SWT.COLOR_BLACK);
+		shell.setBackground(color);
+		assertEquals(color, value.getValue());
+	}
+
+	public void testSetValueBackground() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeBackground(shell);
+
+		Color color = shell.getDisplay().getSystemColor(SWT.COLOR_BLACK);
+
+		value.setValue(color);
+		assertEquals(color, shell.getBackground());
+	}
+
+	public void testGetValueTypeBackgroundColor() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeBackground(shell);
+		assertEquals(Color.class, value.getValueType());
+	}
+
+	public void testGetValueTypeTooltip() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeTooltipText(shell);
+		assertEquals(String.class, value.getValueType());
+	}
+
+	public void testSetValueFont() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeFont(shell);
+
+		Font font = JFaceResources.getDialogFont();
+
+		value.setValue(font);
+		assertEquals(font, shell.getFont());
+	}
+
+	public void testGetValueFont() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeFont(shell);
+
+		Font font = JFaceResources.getDialogFont();
+		shell.setFont(font);
+		assertEquals(font, value.getValue());
+	}
+
+	public void testGetValueTypeFont() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeFont(shell);
+		assertEquals(Font.class, value.getValueType());
+	}
+
+	public void testSetValueTooltipText() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeTooltipText(shell);
+		String text = "text";
+		value.setValue(text);
+		assertEquals(text, shell.getToolTipText());
+	}
+
+	public void testGetValueTooltipText() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeTooltipText(shell);
+		String text = "text";
+		shell.setToolTipText(text);
+		assertEquals(text, value.getValue());
+	}
+
+	public void testGetValueTypeTooltipText() throws Exception {
+		ISWTObservableValue value = SWTObservables.observeTooltipText(shell);
+		assertEquals(String.class, value.getValueType());
+	}
+
+	public void testObserveFocus() {
+		shell.setLayout(new FillLayout());
+		Control c1 = new Text(shell, SWT.NONE);
+		Control c2 = new Text(shell, SWT.NONE);
+		shell.pack();
+		shell.setVisible(true);
+
+		assertTrue(c1.setFocus());
+
+		IObservableValue value = WidgetProperties.focused().observe(c2);
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(value);
+
+		assertTrue(c2.setFocus());
+
+		processDisplayQueue();
+
+		assertEquals(Boolean.TRUE, value.getValue());
+
+		assertEquals(1, tracker.count);
+		assertEquals(Boolean.FALSE, tracker.event.diff.getOldValue());
+		assertEquals(Boolean.TRUE, tracker.event.diff.getNewValue());
+	}
+
+	private void processDisplayQueue() {
+		while (Display.getCurrent().readAndDispatch()) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeCalendarObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeCalendarObservableValueTest.java
new file mode 100644
index 0000000..9d60e67
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeCalendarObservableValueTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734, 195222
+ *        (through ComboObservableValueTest.java)
+ *     Matthew Hall - bug 169876
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DateTime;
+
+/**
+ * @since 3.2
+ * @no
+ */
+public class DateTimeCalendarObservableValueTest extends AbstractSWTTestCase {
+	private DateTime dateTime;
+	private IObservableValue dateObservable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		dateTime = new DateTime(getShell(), SWT.CALENDAR);
+		dateObservable = WidgetProperties.selection().observe(dateTime);
+	}
+
+	public void testGetValue_ExcludesTimeComponent() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.clear();
+		int epochHour = calendar.get(Calendar.HOUR_OF_DAY);
+		int epochMinute = calendar.get(Calendar.MINUTE);
+		int epochSecond = calendar.get(Calendar.SECOND);
+		int epochMillisecond = calendar.get(Calendar.MILLISECOND);
+
+		calendar.set(2009, 3, 3, 12, 7, 21); // time of writing
+		dateObservable.setValue(calendar.getTime());
+
+		calendar.setTime((Date) dateObservable.getValue());
+
+		assertEquals(2009, calendar.get(Calendar.YEAR));
+		assertEquals(3, calendar.get(Calendar.MONTH));
+		assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH));
+
+		assertEquals(epochHour, calendar.get(Calendar.HOUR_OF_DAY));
+		assertEquals(epochMinute, calendar.get(Calendar.MINUTE));
+		assertEquals(epochSecond, calendar.get(Calendar.SECOND));
+		assertEquals(epochMillisecond, calendar.get(Calendar.MILLISECOND));
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeDateObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeDateObservableValueTest.java
new file mode 100644
index 0000000..607cd7c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeDateObservableValueTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734, 195222
+ *        (through ComboObservableValueTest.java)
+ *     Matthew Hall - bug 169876
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DateTime;
+
+/**
+ * @since 3.2
+ * @no
+ */
+public class DateTimeDateObservableValueTest extends AbstractSWTTestCase {
+	private DateTime dateTime;
+	private IObservableValue dateObservable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		dateTime = new DateTime(getShell(), SWT.DATE);
+		dateObservable = WidgetProperties.selection().observe(dateTime);
+	}
+
+	public void testGetValue_ExcludesTimeComponent() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.clear();
+		int epochHour = calendar.get(Calendar.HOUR_OF_DAY);
+		int epochMinute = calendar.get(Calendar.MINUTE);
+		int epochSecond = calendar.get(Calendar.SECOND);
+		int epochMillisecond = calendar.get(Calendar.MILLISECOND);
+
+		calendar.set(2009, 3, 3, 12, 7, 21); // time of writing
+		dateObservable.setValue(calendar.getTime());
+
+		calendar.setTime((Date) dateObservable.getValue());
+
+		assertEquals(2009, calendar.get(Calendar.YEAR));
+		assertEquals(3, calendar.get(Calendar.MONTH));
+		assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH));
+
+		assertEquals(epochHour, calendar.get(Calendar.HOUR_OF_DAY));
+		assertEquals(epochMinute, calendar.get(Calendar.MINUTE));
+		assertEquals(epochSecond, calendar.get(Calendar.SECOND));
+		assertEquals(epochMillisecond, calendar.get(Calendar.MILLISECOND));
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeSelectionPropertyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeSelectionPropertyTest.java
new file mode 100644
index 0000000..d3a7d63
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeSelectionPropertyTest.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 271720)
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.jface.internal.databinding.swt.DateTimeSelectionProperty;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DateTime;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class DateTimeSelectionPropertyTest extends AbstractSWTTestCase {
+	DateTime dateTime;
+	DateTimeSelectionProperty property;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		dateTime = new DateTime(getShell(), SWT.DATE);
+		property = new DateTimeSelectionProperty();
+	}
+
+	public void testSetValue_NullThrowIllegalArgumentException() {
+		try {
+			property.setValue(dateTime, null);
+			fail("Expected IllegalArgumentException");
+		} catch (IllegalArgumentException expected) {
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeTimeObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeTimeObservableValueTest.java
new file mode 100644
index 0000000..c86553c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/DateTimeTimeObservableValueTest.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734, 195222
+ *        (through ComboObservableValueTest.java)
+ *     Matthew Hall - bug 169876
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DateTime;
+
+/**
+ * @since 3.2
+ * @no
+ */
+public class DateTimeTimeObservableValueTest extends AbstractSWTTestCase {
+	private DateTime dateTime;
+	private IObservableValue dateObservable;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		dateTime = new DateTime(getShell(), SWT.TIME);
+		dateObservable = WidgetProperties.selection().observe(dateTime);
+	}
+
+	public void testGetValue_ExcludesDateComponent() {
+		Calendar calendar = Calendar.getInstance();
+		calendar.clear();
+		int epochYear = calendar.get(Calendar.YEAR);
+		int epochMonth = calendar.get(Calendar.MONTH);
+		int epochDay = calendar.get(Calendar.DAY_OF_MONTH);
+
+		calendar.set(2009, 3, 3, 12, 7, 21); // time of writing
+		dateObservable.setValue(calendar.getTime());
+
+		calendar.setTime((Date) dateObservable.getValue());
+
+		assertEquals(epochYear, calendar.get(Calendar.YEAR));
+		assertEquals(epochMonth, calendar.get(Calendar.MONTH));
+		assertEquals(epochDay, calendar.get(Calendar.DAY_OF_MONTH));
+
+		assertEquals(12, calendar.get(Calendar.HOUR_OF_DAY));
+		assertEquals(7, calendar.get(Calendar.MINUTE));
+		assertEquals(21, calendar.get(Calendar.SECOND));
+		assertEquals(0, calendar.get(Calendar.MILLISECOND));
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/LabelObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/LabelObservableValueTest.java
new file mode 100644
index 0000000..19e8f32
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/LabelObservableValueTest.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class LabelObservableValueTest extends ObservableDelegateTest {
+	private Delegate delegate;
+	private IObservableValue observable;
+	private Label label;
+
+	public LabelObservableValueTest() {
+		this(null);
+	}
+
+	public LabelObservableValueTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		label = delegate.label;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testSetValue() throws Exception {
+		// preconditions
+		assertEquals("", label.getText());
+		assertEquals("", observable.getValue());
+
+		String value = "value";
+		observable.setValue(value);
+		assertEquals("label text", value, label.getText());
+		assertEquals("observable value", value, observable.getValue());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(LabelObservableValueTest.class
+				.toString());
+		suite.addTestSuite(LabelObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Label label;
+
+		public void setUp() {
+			shell = new Shell();
+			label = new Label(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text().observe(realm, label);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue value = (IObservableValue) observable;
+			value.setValue(value.getValue() + "a");
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return observable.getValue() + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ListSingleSelectionObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ListSingleSelectionObservableValueTest.java
new file mode 100644
index 0000000..b64af74
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ListSingleSelectionObservableValueTest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Ashley Cambrell and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Ashley Cambrell - initial API and implementation
+ *     Matthew Hall - bug 194734
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.List;
+
+/**
+ * @since 3.2
+ *
+ */
+public class ListSingleSelectionObservableValueTest extends AbstractSWTTestCase {
+	public void testSetValue() throws Exception {
+		List list = new List(getShell(), SWT.NONE);
+		IObservableValue observableValue = SWTObservables
+				.observeSingleSelectionIndex(list);
+		list.add("Item1");
+
+		assertEquals(-1, list.getSelectionIndex());
+		assertEquals(-1, ((Integer) observableValue.getValue()).intValue());
+
+		Integer value = new Integer(0);
+		observableValue.setValue(value);
+		assertEquals("list selection index", value.intValue(), list
+				.getSelectionIndex());
+		assertEquals("observable value", value, observableValue.getValue());
+	}
+
+	public void testDispose() throws Exception {
+		List list = new List(getShell(), SWT.NONE);
+		IObservableValue observableValue = SWTObservables
+				.observeSingleSelectionIndex(list);
+		list.add("Item1");
+		list.add("Item2");
+
+		assertEquals(-1, list.getSelectionIndex());
+		assertEquals(-1, ((Integer) observableValue.getValue()).intValue());
+
+		list.select(0);
+		list.notifyListeners(SWT.Selection, null);
+		assertEquals(0, list.getSelectionIndex());
+		assertEquals(new Integer(0), observableValue.getValue());
+
+		observableValue.dispose();
+
+		list.select(1);
+		list.notifyListeners(SWT.Selection, null);
+		assertEquals(1, list.getSelectionIndex());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SWTDelayedObservableValueDecoratorTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SWTDelayedObservableValueDecoratorTest.java
new file mode 100644
index 0000000..fc16db3
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SWTDelayedObservableValueDecoratorTest.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 212223)
+ *     Matthew Hall - bug 213145, 245647, 194734
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.swt.SWTObservableValueDecorator;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for DelayedObservableValue
+ * 
+ * @since 1.2
+ */
+public class SWTDelayedObservableValueDecoratorTest extends
+		AbstractDefaultRealmTestCase {
+	private Display display;
+	private Shell shell;
+	private Object oldValue;
+	private Object newValue;
+	private ISWTObservableValue target;
+	private ISWTObservableValue delayed;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		display = Display.getCurrent();
+		shell = new Shell(display);
+		target = new SWTObservableValueDecorator(new WritableValue(
+				SWTObservables.getRealm(display)), shell);
+		oldValue = new Object();
+		newValue = new Object();
+		target.setValue(oldValue);
+		delayed = SWTObservables.observeDelayedValue(1, target);
+	}
+
+	protected void tearDown() throws Exception {
+		delayed.dispose();
+		target.dispose();
+		target = null;
+		shell.dispose();
+		shell = null;
+		display = null;
+		super.tearDown();
+	}
+
+	public void testFocusOut_FiresPendingValueChange() {
+		assertFiresPendingValueChange(new Runnable() {
+			public void run() {
+				// simulate focus-out event
+				shell.notifyListeners(SWT.FocusOut, new Event());
+			}
+		});
+	}
+
+	private void assertFiresPendingValueChange(Runnable runnable) {
+		ValueChangeEventTracker tracker = ValueChangeEventTracker
+				.observe(delayed);
+
+		target.setValue(newValue);
+		assertTrue(delayed.isStale());
+		assertEquals(0, tracker.count);
+
+		runnable.run();
+
+		assertFalse(delayed.isStale());
+		assertEquals(1, tracker.count);
+		assertEquals(oldValue, tracker.event.diff.getOldValue());
+		assertEquals(newValue, tracker.event.diff.getNewValue());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				SWTDelayedObservableValueDecoratorTest.class.getName());
+		suite.addTestSuite(SWTDelayedObservableValueDecoratorTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		Shell shell;
+
+		public void setUp() {
+			super.setUp();
+			shell = new Shell();
+		}
+
+		public void tearDown() {
+			shell.dispose();
+			shell = null;
+			super.tearDown();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return SWTObservables.observeDelayedValue(0,
+					new SWTObservableValueDecorator(new WritableValue(realm,
+							null, Object.class), shell));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Object.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new Object();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueMaxTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueMaxTest.java
new file mode 100644
index 0000000..6bfbec3
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueMaxTest.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class ScaleObservableValueMaxTest extends ObservableDelegateTest {
+	private Delegate delegate;
+
+	private Scale scale;
+
+	private IObservableValue observable;
+
+	public ScaleObservableValueMaxTest() {
+		this(null);
+	}
+
+	public ScaleObservableValueMaxTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		scale = delegate.scale;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testGetValue() throws Exception {
+		int max = 100;
+		scale.setMaximum(max);
+		assertEquals(new Integer(max), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		int max = 100;
+		observable.setValue(new Integer(max));
+		assertEquals(max, scale.getMaximum());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ScaleObservableValueMaxTest.class
+				.toString());
+		suite.addTestSuite(ScaleObservableValueMaxTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Scale scale;
+
+		public void setUp() {
+			shell = new Shell();
+			scale = new Scale(shell, SWT.NONE);
+			scale.setMaximum(1000);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.maximum().observe(realm, scale);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			return new Integer(((Integer) observable.getValue()).intValue() + 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueMinTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueMinTest.java
new file mode 100644
index 0000000..00f3e05
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueMinTest.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class ScaleObservableValueMinTest extends ObservableDelegateTest {
+	private Delegate delegate;
+
+	private Scale scale;
+
+	private IObservableValue observable;
+
+	public ScaleObservableValueMinTest() {
+		this(null);
+	}
+
+	public ScaleObservableValueMinTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		scale = delegate.scale;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testGetValue() throws Exception {
+		int min = 100;
+		scale.setMinimum(min);
+		assertEquals(new Integer(min), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		int min = 100;
+		observable.setValue(new Integer(min));
+		assertEquals(min, scale.getMinimum());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ScaleObservableValueMinTest.class
+				.toString());
+		suite.addTestSuite(ScaleObservableValueMinTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Scale scale;
+
+		public void setUp() {
+			shell = new Shell();
+			scale = new Scale(shell, SWT.NONE);
+			scale.setMaximum(1000);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.minimum().observe(realm, scale);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			return new Integer(((Integer) observable.getValue()).intValue() + 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueSelectionTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueSelectionTest.java
new file mode 100644
index 0000000..a685a1a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ScaleObservableValueSelectionTest.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Scale;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ */
+public class ScaleObservableValueSelectionTest extends ObservableDelegateTest {
+	private Delegate delegate;
+
+	private Scale scale;
+
+	private IObservableValue observable;
+
+	public ScaleObservableValueSelectionTest() {
+		this(null);
+	}
+
+	public ScaleObservableValueSelectionTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		scale = delegate.scale;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testGetValue() throws Exception {
+		int value = 100;
+		scale.setSelection(value);
+		assertEquals(new Integer(value), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		int value = 100;
+		observable.setValue(new Integer(value));
+		assertEquals(value, scale.getSelection());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ScaleObservableValueSelectionTest.class
+				.toString());
+		suite.addTestSuite(ScaleObservableValueSelectionTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Scale scale;
+
+		public void setUp() {
+			shell = new Shell();
+			scale = new Scale(shell, SWT.NONE);
+			scale.setMaximum(1000);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.selection().observe(realm, scale);
+		}
+
+		public void change(IObservable observable) {
+			scale
+					.setSelection(createIntegerValue(
+							(IObservableValue) observable).intValue());
+			scale.notifyListeners(SWT.Selection, null);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			return new Integer(((Integer) observable.getValue()).intValue() + 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ShellObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ShellObservableValueTest.java
new file mode 100644
index 0000000..4e5a843
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/ShellObservableValueTest.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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 212235)
+ *     Matthew Hall - bug 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for ShellObservableValue
+ * 
+ * @since 1.2
+ */
+public class ShellObservableValueTest extends AbstractDefaultRealmTestCase {
+	String oldValue;
+	String newValue;
+	Shell shell;
+	IObservableValue observable;
+	ValueChangeEventTracker tracker;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		observable = SWTObservables.observeText(shell);
+		oldValue = "old";
+		newValue = "new";
+		shell.setText(oldValue);
+		tracker = ValueChangeEventTracker.observe(observable);
+	}
+
+	public void tearDown() throws Exception {
+		observable.dispose();
+		observable = null;
+		shell.dispose();
+		shell = null;
+		super.tearDown();
+	}
+
+	public void testGetValueType() {
+		assertEquals(String.class, observable.getValueType());
+	}
+
+	public void testSetValue_FiresValueChangeEvent() {
+		observable.setValue(newValue);
+
+		assertEquals(1, tracker.count);
+		assertEquals(oldValue, tracker.event.diff.getOldValue());
+		assertEquals(newValue, tracker.event.diff.getNewValue());
+	}
+
+	public void testSetValue_NullConvertedToEmptyString() {
+		observable.setValue(null);
+
+		assertEquals("", observable.getValue());
+		assertEquals("", shell.getText());
+	}
+
+	public void testShellSetText_GetValueReturnsSame() {
+		assertEquals(oldValue, observable.getValue());
+
+		shell.setText(newValue);
+
+		assertEquals(newValue, observable.getValue());
+	}
+
+	public void testShellSetText_NoValueChangeEvent() {
+		shell.setText(newValue);
+		assertEquals(0, tracker.count);
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ShellObservableValueTest.class
+				.toString());
+		suite.addTestSuite(ShellObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		Shell shell;
+
+		public void setUp() {
+			super.setUp();
+			shell = new Shell();
+		}
+
+		public void tearDown() {
+			shell.dispose();
+			shell = null;
+			super.tearDown();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text().observe(realm, shell);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		int counter;
+
+		public Object createValue(IObservableValue observable) {
+			return Integer.toString(counter++);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueMaxTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueMaxTest.java
new file mode 100644
index 0000000..e74f66a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueMaxTest.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.2
+ */
+public class SpinnerObservableValueMaxTest extends ObservableDelegateTest {
+	private Delegate delegate;
+
+	private Spinner spinner;
+
+	private IObservableValue observable;
+
+	public SpinnerObservableValueMaxTest() {
+		this(null);
+	}
+
+	public SpinnerObservableValueMaxTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		spinner = delegate.spinner;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testGetValue() throws Exception {
+		int max = 100;
+		spinner.setMaximum(max);
+		assertEquals(new Integer(max), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		int max = 100;
+		observable.setValue(new Integer(max));
+		assertEquals(max, spinner.getMaximum());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SpinnerObservableValueMaxTest.class
+				.toString());
+		suite.addTestSuite(SpinnerObservableValueMaxTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Spinner spinner;
+
+		public void setUp() {
+			shell = new Shell();
+			spinner = new Spinner(shell, SWT.NONE);
+			spinner.setMaximum(1000);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.maximum().observe(realm, spinner);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			return new Integer(((Integer) observable.getValue()).intValue() + 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueMinTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueMinTest.java
new file mode 100644
index 0000000..d0bbca4
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueMinTest.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.2
+ */
+public class SpinnerObservableValueMinTest extends ObservableDelegateTest {
+	private Delegate delegate;
+
+	private Spinner spinner;
+
+	private IObservableValue observable;
+
+	public SpinnerObservableValueMinTest() {
+		this(null);
+	}
+
+	public SpinnerObservableValueMinTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		spinner = delegate.spinner;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testGetValue() throws Exception {
+		int min = 100;
+		spinner.setMinimum(min);
+		assertEquals(new Integer(min), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		int min = 100;
+		observable.setValue(new Integer(min));
+		assertEquals(min, spinner.getMinimum());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(SpinnerObservableValueMinTest.class
+				.toString());
+		suite.addTestSuite(SpinnerObservableValueMinTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Spinner spinner;
+
+		public void setUp() {
+			shell = new Shell();
+			spinner = new Spinner(shell, SWT.NONE);
+			spinner.setMaximum(1000);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.minimum().observe(realm, spinner);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			return new Integer(((Integer) observable.getValue()).intValue() + 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueSelectionTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueSelectionTest.java
new file mode 100644
index 0000000..7cf5566
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueSelectionTest.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.2
+ */
+public class SpinnerObservableValueSelectionTest extends ObservableDelegateTest {
+	private Delegate delegate;
+
+	private Spinner spinner;
+
+	private IObservableValue observable;
+
+	public SpinnerObservableValueSelectionTest() {
+		this(null);
+	}
+
+	public SpinnerObservableValueSelectionTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		spinner = delegate.spinner;
+	}
+
+	protected IObservable doCreateObservable() {
+		return getObservableContractDelegate().createObservable(
+				SWTObservables.getRealm(Display.getDefault()));
+	}
+
+	public void testGetValue() throws Exception {
+		int value = 100;
+		spinner.setSelection(value);
+		assertEquals(new Integer(value), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		int value = 100;
+		observable.setValue(new Integer(value));
+		assertEquals(value, spinner.getSelection());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				SpinnerObservableValueSelectionTest.class.toString());
+		suite.addTestSuite(SpinnerObservableValueSelectionTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Spinner spinner;
+
+		public void setUp() {
+			shell = new Shell();
+			spinner = new Spinner(shell, SWT.NONE);
+			spinner.setMaximum(1000);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.selection().observe(realm, spinner);
+		}
+
+		public void change(IObservable observable) {
+			spinner.setSelection(createIntegerValue(
+					(IObservableValue) observable).intValue());
+			spinner.notifyListeners(SWT.Selection, null);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			return new Integer(((Integer) observable.getValue()).intValue() + 1);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueTest.java
new file mode 100644
index 0000000..a30345d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/SpinnerObservableValueTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.ISWTObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Spinner;
+
+/**
+ * @since 3.2
+ *
+ */
+public class SpinnerObservableValueTest extends AbstractSWTTestCase {
+	public void testDispose() throws Exception {
+		Spinner spinner = new Spinner(getShell(), SWT.NONE);
+		ISWTObservableValue observableValue = SWTObservables.observeSelection(spinner);
+		ValueChangeEventTracker testCounterValueChangeListener = new ValueChangeEventTracker();
+		observableValue.addValueChangeListener(testCounterValueChangeListener);
+
+		assertEquals(0, spinner.getSelection());
+		assertEquals(0, ((Integer) observableValue.getValue()).intValue());
+
+		Integer expected1 = new Integer(1);
+		spinner.setSelection(expected1.intValue());
+
+//		assertEquals(1, testCounterValueChangeListener.counter);
+		assertEquals(expected1.intValue(), spinner.getSelection());
+		assertEquals(expected1, observableValue.getValue());
+
+		observableValue.dispose();
+
+		Integer expected2 = new Integer(2);
+		spinner.setSelection(expected2.intValue());
+
+//		assertEquals(1, testCounterValueChangeListener.counter);
+		assertEquals(expected2.intValue(), spinner.getSelection());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueDefaultSelectionTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueDefaultSelectionTest.java
new file mode 100644
index 0000000..414e907
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueDefaultSelectionTest.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Code 9 Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Code 9 Corporation - initial API and implementation
+ *     Chris Aniszczyk <zx@code9.com> - bug 131435
+ *     Matthew Hall - bug 194734, 195222
+ *         (through StyledTextObservableValueFocusOutText.java)
+ *     Matthew Hall - bug 256543
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for the DefaultSelection version of StyledTextObservableValue.
+ */
+public class StyledTextObservableValueDefaultSelectionTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				StyledTextObservableValueDefaultSelectionTest.class.toString());
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		private StyledText text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new StyledText(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text(SWT.DefaultSelection).observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			text.setText((String) createValue(observableValue));
+
+			text.notifyListeners(SWT.DefaultSelection, null);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			String value = (String) observable.getValue();
+			return value + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueFocusOutTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueFocusOutTest.java
new file mode 100644
index 0000000..5f80217
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueFocusOutTest.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Code 9 Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Code 9 Corporation - initial API and implementation
+ *     Chris Aniszczyk <zx@code9.com> - bug 131435
+ *     Matthew Hall - bugs 194734, 195222, 262287
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for the FocusOut version of StyledTextObservableValue.
+ */
+public class StyledTextObservableValueFocusOutTest extends AbstractSWTTestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				StyledTextObservableValueFocusOutTest.class.toString());
+		suite.addTestSuite(StyledTextObservableValueFocusOutTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	public void testIsStale_AfterModifyBeforeFocusOut() {
+		StyledText text = new StyledText(getShell(), SWT.NONE);
+		text.setText("0");
+
+		IObservableValue observable = WidgetProperties.text(SWT.FocusOut)
+				.observe(text);
+
+		StaleEventTracker staleTracker = StaleEventTracker.observe(observable);
+		ChangeEventTracker changeTracker = ChangeEventTracker
+				.observe(observable);
+
+		assertFalse(observable.isStale());
+		assertEquals(0, staleTracker.count);
+		assertEquals(0, changeTracker.count);
+
+		text.setText("1");
+		text.notifyListeners(SWT.Modify, null);
+
+		assertTrue(observable.isStale());
+		assertEquals(1, staleTracker.count);
+		assertEquals(0, changeTracker.count);
+
+		text.notifyListeners(SWT.FocusOut, null);
+
+		assertFalse(observable.isStale());
+		assertEquals(1, staleTracker.count);
+		assertEquals(1, changeTracker.count);
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		private StyledText text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new StyledText(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text(SWT.FocusOut).observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			text.setFocus();
+
+			IObservableValue observableValue = (IObservableValue) observable;
+			text.setText((String) createValue(observableValue));
+
+			text.notifyListeners(SWT.FocusOut, null);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			String value = (String) observable.getValue();
+			return value + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueModifyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueModifyTest.java
new file mode 100644
index 0000000..5265648
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueModifyTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Code 9 Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Code 9 Corporation - initial API and implementation
+ *     Chris Aniszczyk <zx@code9.com> - bug 131435
+ *     Matthew Hall - bug 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for the Modify version of StyledTextObservableValue.
+ */
+public class StyledTextObservableValueModifyTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				StyledTextObservableValueModifyTest.class.toString());
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		private StyledText text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new StyledText(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text(SWT.Modify).observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			text.setFocus();
+
+			IObservableValue observableValue = (IObservableValue) observable;
+			text.setText((String) createValue(observableValue));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			String value = (String) observable.getValue();
+			return value + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueTest.java
new file mode 100644
index 0000000..233cb99
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/StyledTextObservableValueTest.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Code 9 Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Code 9 Corporation - initial API and implementation
+ *     Chris Aniszczyk <zx@code9.com> - bug 131435
+ *     Matthew Hall - bugs 194734, 256543
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.internal.databinding.swt.StyledTextTextProperty;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests to assert the inputs of the StyledTextObservableValue constructor.
+ */
+public class StyledTextObservableValueTest extends AbstractDefaultRealmTestCase {
+	private StyledText text;
+	private ValueChangeEventTracker listener;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		Shell shell = new Shell();
+		text = new StyledText(shell, SWT.NONE);
+
+		listener = new ValueChangeEventTracker();
+	}
+
+	/**
+	 * Asserts that only valid SWT event types are accepted on construction of
+	 * StyledTextObservableValue.
+	 */
+	public void testConstructorUpdateEventTypes() {
+		try {
+			new StyledTextTextProperty(new int[] { SWT.None });
+			new StyledTextTextProperty(new int[] { SWT.FocusOut });
+			new StyledTextTextProperty(new int[] { SWT.Modify });
+			new StyledTextTextProperty(new int[] { SWT.DefaultSelection });
+			assertTrue(true);
+		} catch (IllegalArgumentException e) {
+			fail();
+		}
+
+		try {
+			new StyledTextTextProperty(new int[] { SWT.Verify });
+			fail();
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	/**
+	 * s
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetValueBeforeFocusOutChangeEventsFire() throws Exception {
+		IObservableValue observableValue = SWTObservables.observeText(text,
+				SWT.FocusOut);
+		observableValue.addValueChangeListener(listener);
+
+		String a = "a";
+		String b = "b";
+
+		text.setText(a);
+		
+		// fetching the value updates the buffered value
+		assertEquals(a, observableValue.getValue()); 
+		assertEquals(1, listener.count);
+
+		text.setText(b);
+
+		text.notifyListeners(SWT.FocusOut, null);
+
+		assertEquals(2, listener.count);
+		assertEquals(a, listener.event.diff.getOldValue());
+		assertEquals(b, listener.event.diff.getNewValue());
+	}
+
+	public void testDispose() throws Exception {
+		IObservableValue observableValue = SWTObservables.observeText(text,
+				SWT.Modify);
+		ValueChangeEventTracker testCounterValueChangeListener = new ValueChangeEventTracker();
+		observableValue.addValueChangeListener(testCounterValueChangeListener);
+
+		String expected1 = "Test123";
+		text.setText(expected1);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected1, text.getText());
+		assertEquals(expected1, observableValue.getValue());
+
+		observableValue.dispose();
+
+		String expected2 = "NewValue123";
+		text.setText(expected2);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected2, text.getText());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TableObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TableObservableValueTest.java
new file mode 100644
index 0000000..12f8a62
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TableObservableValueTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * @since 3.2
+ */
+public class TableObservableValueTest extends AbstractSWTTestCase {
+	public void testDispose() throws Exception {
+		Table table = new Table(getShell(), SWT.NONE);
+		IObservableValue observableValue = SWTObservables
+				.observeSingleSelectionIndex(table);
+
+		TableItem item1 = new TableItem(table, SWT.NONE);
+		item1.setText("Item1");
+		TableItem item2 = new TableItem(table, SWT.NONE);
+		item2.setText("Item2");
+
+		assertEquals(-1, table.getSelectionIndex());
+		assertEquals(-1, ((Integer) observableValue.getValue()).intValue());
+
+		table.select(0);
+		table.notifyListeners(SWT.Selection, null);
+
+		assertEquals(0, table.getSelectionIndex());
+		assertEquals(new Integer(0), observableValue.getValue());
+
+		observableValue.dispose();
+
+		table.select(1);
+		table.notifyListeners(SWT.Selection, null);
+		assertEquals(1, table.getSelectionIndex());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TableSingleSelectionObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TableSingleSelectionObservableValueTest.java
new file mode 100644
index 0000000..af18697
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TableSingleSelectionObservableValueTest.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Brad Reynolds and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bugs 118516, 213145, 194734, 195222
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * @since 3.2
+ */
+public class TableSingleSelectionObservableValueTest extends
+		ObservableDelegateTest {
+	private Delegate delegate;
+	private IObservableValue observable;
+	private Table table;
+
+	public TableSingleSelectionObservableValueTest() {
+		this(null);
+	}
+
+	public TableSingleSelectionObservableValueTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		observable = (IObservableValue) getObservable();
+		delegate = (Delegate) getObservableContractDelegate();
+		table = delegate.table;
+	}
+
+	protected IObservable doCreateObservable() {
+		Delegate delegate = (Delegate) getObservableContractDelegate();
+		return delegate.createObservableValue(SWTObservables.getRealm(Display
+				.getDefault()));
+	}
+
+	public void testSetValue() throws Exception {
+		// preconditions
+		assertEquals(-1, table.getSelectionIndex());
+		assertEquals(-1, ((Integer) observable.getValue()).intValue());
+
+		Integer value = new Integer(0);
+		observable.setValue(value);
+		assertEquals("table selection index", value.intValue(), table
+				.getSelectionIndex());
+		assertEquals("observable value", value, observable.getValue());
+	}
+
+	public void testGetValue() throws Exception {
+		int value = 1;
+		table.setSelection(value);
+
+		assertEquals("table selection index", value, table.getSelectionIndex());
+		assertEquals("observable value", new Integer(value), observable
+				.getValue());
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(
+				TableSingleSelectionObservableValueTest.class.toString());
+		suite.addTestSuite(TableSingleSelectionObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		Table table;
+
+		public void setUp() {
+			shell = new Shell();
+			table = new Table(shell, SWT.NONE);
+			new TableItem(table, SWT.NONE).setText("0");
+			new TableItem(table, SWT.NONE).setText("1");
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.singleSelectionIndex()
+					.observe(realm, table);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Integer.TYPE;
+		}
+
+		public void change(IObservable observable) {
+			int index = createIntegerValue((IObservableValue) observable)
+					.intValue();
+			table.select(index);
+
+			table.notifyListeners(SWT.Selection, null);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return createIntegerValue(observable);
+		}
+
+		private Integer createIntegerValue(IObservableValue observable) {
+			int value = ((Integer) observable.getValue()).intValue();
+			switch (value) {
+			case -1:
+			case 1:
+				return new Integer(0);
+			case 0:
+				return new Integer(1);
+			}
+
+			Assert.isTrue(false);
+			return null;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextEditableObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextEditableObservableValueTest.java
new file mode 100644
index 0000000..58b9d7a
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextEditableObservableValueTest.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.ObservableDelegateTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 1.1
+ */
+public class TextEditableObservableValueTest extends ObservableDelegateTest {
+
+	private Delegate delegate;
+	private Text text;
+	private IObservableValue observable;
+
+	public TextEditableObservableValueTest() {
+		this(null);
+	}
+
+	public TextEditableObservableValueTest(String testName) {
+		super(testName, new Delegate());
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.jface.conformance.databinding.ObservableDelegateTest#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		delegate = (Delegate) getObservableContractDelegate();
+		observable = (IObservableValue) getObservable();
+		text = delegate.text;
+	}
+
+	protected IObservable doCreateObservable() {
+		return super.doCreateObservable();
+	}
+
+	public void testGetValue() throws Exception {
+		text.setEditable(false);
+		assertEquals(Boolean.valueOf(text.getEditable()), observable.getValue());
+
+		text.setEditable(true);
+		assertEquals(Boolean.valueOf(text.getEditable()), observable.getValue());
+	}
+
+	public void testSetValue() throws Exception {
+		text.setEditable(false);
+		observable.setValue(Boolean.TRUE);
+		assertEquals(Boolean.TRUE, Boolean.valueOf(text.getEditable()));
+
+		observable.setValue(Boolean.FALSE);
+		assertEquals(Boolean.FALSE, Boolean.valueOf(text.getEditable()));
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(TextEditableObservableValueTest.class
+				.toString());
+		suite.addTestSuite(TextEditableObservableValueTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+		Text text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new Text(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.editable().observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return Boolean.TYPE;
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue observableValue = (IObservableValue) observable;
+			observableValue.setValue(createValue(observableValue));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return (Boolean.TRUE.equals(observable.getValue()) ? Boolean.FALSE
+					: Boolean.TRUE);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueDefaultSelectionTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueDefaultSelectionTest.java
new file mode 100644
index 0000000..b97153c
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueDefaultSelectionTest.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *           (through TextObservableValueFocusOutText.java)
+ *     Matthew Hall - bug 256543
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Tests for the DefaultSelection version of TextObservableValue.
+ */
+public class TextObservableValueDefaultSelectionTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(TextObservableValueDefaultSelectionTest.class
+				.toString());
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		private Text text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new Text(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text(SWT.DefaultSelection).observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			text.setFocus();
+
+			IObservableValue observableValue = (IObservableValue) observable;
+			text.setText((String) createValue(observableValue));
+
+			text.notifyListeners(SWT.DefaultSelection, null);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			String value = (String) observable.getValue();
+			return value + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueFocusOutTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueFocusOutTest.java
new file mode 100644
index 0000000..a63ee52
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueFocusOutTest.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 - bugs 213145, 194734, 195222, 262287
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Tests for the FocusOut version of TextObservableValue.
+ */
+public class TextObservableValueFocusOutTest extends AbstractSWTTestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(TextObservableValueFocusOutTest.class
+				.toString());
+		suite.addTestSuite(TextObservableValueFocusOutTest.class);
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	public void testIsStale_AfterModifyBeforeFocusOut() {
+		Text text = new Text(getShell(), SWT.NONE);
+		text.setText("0");
+
+		IObservableValue observable = WidgetProperties.text(SWT.FocusOut)
+				.observe(text);
+
+		StaleEventTracker staleTracker = StaleEventTracker.observe(observable);
+		ChangeEventTracker changeTracker = ChangeEventTracker
+				.observe(observable);
+
+		assertFalse(observable.isStale());
+		assertEquals(0, staleTracker.count);
+		assertEquals(0, changeTracker.count);
+
+		text.setText("1");
+		text.notifyListeners(SWT.Modify, null);
+
+		assertTrue(observable.isStale());
+		assertEquals(1, staleTracker.count);
+		assertEquals(0, changeTracker.count);
+
+		text.notifyListeners(SWT.FocusOut, null);
+
+		assertFalse(observable.isStale());
+		assertEquals(1, staleTracker.count);
+		assertEquals(1, changeTracker.count);
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		private Text text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new Text(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text(SWT.FocusOut).observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			text.setFocus();
+
+			IObservableValue observableValue = (IObservableValue) observable;
+			text.setText((String) createValue(observableValue));
+
+			text.notifyListeners(SWT.FocusOut, null);
+		}
+
+		public Object createValue(IObservableValue observable) {
+			String value = (String) observable.getValue();
+			return value + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueModifyTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueModifyTest.java
new file mode 100644
index 0000000..5dc153e
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueModifyTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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 213145, 194734, 195222
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.swt.SWTMutableObservableValueContractTest;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @since 3.2
+ */
+public class TextObservableValueModifyTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(TextObservableValueModifyTest.class
+				.toString());
+		suite.addTest(SWTMutableObservableValueContractTest
+				.suite(new Delegate()));
+		return suite;
+	}
+
+	/* package */static class Delegate extends
+			AbstractObservableValueContractDelegate {
+		private Shell shell;
+
+		private Text text;
+
+		public void setUp() {
+			shell = new Shell();
+			text = new Text(shell, SWT.NONE);
+		}
+
+		public void tearDown() {
+			shell.dispose();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return WidgetProperties.text(SWT.Modify).observe(realm, text);
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return String.class;
+		}
+
+		public void change(IObservable observable) {
+			text.setFocus();
+
+			IObservableValue observableValue = (IObservableValue) observable;
+			text.setText((String) createValue(observableValue));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			String value = (String) observable.getValue();
+			return value + "a";
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueTest.java
new file mode 100644
index 0000000..ad508eb
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/swt/TextObservableValueTest.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.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, 164653
+ *     Ashley Cambrell - bug 198904
+ *     Matthew Hall - bug 194734, 195222, 256543
+ *******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.swt;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Tests to assert the inputs of the TextObservableValue constructor.
+ * 
+ * @since 3.2
+ */
+public class TextObservableValueTest extends AbstractDefaultRealmTestCase {
+	private Text text;
+	private ValueChangeEventTracker listener;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		Shell shell = new Shell();
+		text = new Text(shell, SWT.NONE);
+
+		listener = new ValueChangeEventTracker();
+	}
+
+	/**
+	 * Asserts that only valid SWT event types are accepted on construction of
+	 * TextObservableValue.
+	 */
+	public void testConstructorUpdateEventTypes() {
+		try {
+			WidgetProperties.text(SWT.None);
+			WidgetProperties.text(SWT.FocusOut);
+			WidgetProperties.text(SWT.Modify);
+			WidgetProperties.text(SWT.DefaultSelection);
+			assertTrue(true);
+		} catch (IllegalArgumentException e) {
+			fail();
+		}
+
+		try {
+			WidgetProperties.text(SWT.Verify);
+			fail();
+		} catch (IllegalArgumentException e) {
+			assertTrue(true);
+		}
+	}
+
+	/**
+	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=171132
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetValueBeforeFocusOutChangeEventsFire() throws Exception {
+		IObservableValue observableValue = WidgetProperties.text(SWT.FocusOut)
+				.observe(Realm.getDefault(), text);
+		observableValue.addValueChangeListener(listener);
+
+		String a = "a";
+		String b = "b";
+
+		text.setText(a);
+
+		assertEquals(0, listener.count);
+
+		// fetching the value updates the buffered value
+		assertEquals(a, observableValue.getValue());
+		assertEquals(1, listener.count);
+
+		text.setText(b);
+
+		assertEquals(1, listener.count);
+
+		text.notifyListeners(SWT.FocusOut, null);
+
+		assertEquals(2, listener.count);
+		assertEquals(a, listener.event.diff.getOldValue());
+		assertEquals(b, listener.event.diff.getNewValue());
+	}
+
+	public void testDispose() throws Exception {
+		IObservableValue observableValue = WidgetProperties.text(SWT.Modify)
+				.observe(Realm.getDefault(), text);
+		ValueChangeEventTracker testCounterValueChangeListener = new ValueChangeEventTracker();
+		observableValue.addValueChangeListener(testCounterValueChangeListener);
+
+		String expected1 = "Test123";
+		text.setText(expected1);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected1, text.getText());
+		assertEquals(expected1, observableValue.getValue());
+
+		observableValue.dispose();
+
+		String expected2 = "NewValue123";
+		text.setText(expected2);
+
+		assertEquals(1, testCounterValueChangeListener.count);
+		assertEquals(expected2, text.getText());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/CheckableCheckedElementsObservableSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/CheckableCheckedElementsObservableSetTest.java
new file mode 100644
index 0000000..d5983c8
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/CheckableCheckedElementsObservableSetTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2009 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 283204)
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ICheckable;
+
+public class CheckableCheckedElementsObservableSetTest extends
+		AbstractDefaultRealmTestCase {
+	public void testClear() {
+		// init
+		ICheckable checkable = new ICheckable() {
+
+			public void addCheckStateListener(ICheckStateListener listener) {
+			}
+
+			public boolean getChecked(Object element) {
+				return false;
+			}
+
+			public void removeCheckStateListener(ICheckStateListener listener) {
+
+			}
+
+			public boolean setChecked(Object element, boolean state) {
+				return false;
+			}
+
+		};
+
+		// CheckableCheckedElementsObservableSet set = new
+		// CheckableCheckedElementsObservableSet(Realm.getDefault(),
+		// checkable, String.class);
+		IObservableSet set = ViewersObservables.observeCheckedElements(
+				checkable, String.class);
+		set.add("Test1");
+		set.add("Test2");
+		assertEquals(2, set.size());
+
+		// test
+		set.clear();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableCollectionContentProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableCollectionContentProviderTest.java
new file mode 100644
index 0000000..13fd039
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableCollectionContentProviderTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 266038)
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ObservableCollectionContentProviderTest extends
+		AbstractDefaultRealmTestCase {
+	private Shell shell;
+	private TableViewer viewer;
+	ObservableListContentProvider contentProvider;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		viewer = new TableViewer(shell);
+	}
+
+	protected void tearDown() throws Exception {
+		shell.dispose();
+		shell = null;
+		viewer = null;
+		super.tearDown();
+	}
+
+	public void testGetKnownElements_DisposedWithoutModificationOnContentProviderDispose() {
+		final IObservableList input = new WritableList(Collections
+				.singletonList("element"), null);
+		contentProvider = new ObservableListContentProvider();
+		contentProvider.inputChanged(viewer, null, input);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+
+		// ensure there is an element in knownElements so we're sure that
+		// "no modification" is not due to the set being already empty.
+		contentProvider.getElements(input);
+		assertEquals(1, knownElements.size());
+
+		DisposeEventTracker disposeTracker = DisposeEventTracker
+				.observe(knownElements);
+		ChangeEventTracker changeTracker = ChangeEventTracker
+				.observe(knownElements);
+
+		contentProvider.dispose();
+
+		assertEquals(0, changeTracker.count);
+		assertEquals(1, disposeTracker.count);
+		assertTrue(knownElements.isDisposed());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableCollectionTreeContentProviderTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableCollectionTreeContentProviderTest.java
new file mode 100644
index 0000000..cc22b3d
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableCollectionTreeContentProviderTest.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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 239015)
+ *     Matthew Hall - bug 266038
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
+import org.eclipse.jface.databinding.conformance.util.DisposeEventTracker;
+import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ObservableCollectionTreeContentProviderTest extends
+		AbstractDefaultRealmTestCase {
+	private Shell shell;
+	private TreeViewer viewer;
+	ObservableListTreeContentProvider contentProvider;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		shell = new Shell();
+		viewer = new TreeViewer(shell);
+	}
+
+	protected void tearDown() throws Exception {
+		shell.dispose();
+		shell = null;
+		viewer = null;
+		super.tearDown();
+	}
+
+	public void testGetKnownElements_ExcludesInput() {
+		final Object input = new Object();
+		Object[] rootElements = new Object[] { "one", "two", "three" };
+		final IObservableList rootElementList = new WritableList(Arrays
+				.asList(rootElements), null);
+		contentProvider = new ObservableListTreeContentProvider(
+				new IObservableFactory() {
+					public IObservable createObservable(Object target) {
+						if (target == input)
+							return rootElementList;
+						return null;
+					}
+				}, null);
+		viewer.setContentProvider(contentProvider);
+		viewer.setInput(input);
+		contentProvider.getElements(input);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+		assertFalse(knownElements.contains(input));
+		assertEquals(new HashSet(Arrays.asList(rootElements)), knownElements);
+	}
+
+	public void testGetKnownElements_DisposedWithoutModificationOnContentProviderDispose() {
+		final Object input = new Object();
+		final IObservableList rootElementList = new WritableList(Collections
+				.singletonList("element"), null);
+		contentProvider = new ObservableListTreeContentProvider(
+				new IObservableFactory() {
+					public IObservable createObservable(Object target) {
+						if (target == input)
+							return rootElementList;
+						return null;
+					}
+				}, null);
+		contentProvider.inputChanged(viewer, null, input);
+
+		IObservableSet knownElements = contentProvider.getKnownElements();
+
+		// ensure there is an element in knownElements so we're sure that
+		// "no modification" is not due to the set being already empty.
+		contentProvider.getElements(input);
+		assertEquals(1, knownElements.size());
+
+		DisposeEventTracker disposeTracker = DisposeEventTracker
+				.observe(knownElements);
+		ChangeEventTracker changeTracker = ChangeEventTracker
+				.observe(knownElements);
+
+		contentProvider.dispose();
+
+		assertEquals(0, changeTracker.count);
+		assertEquals(1, disposeTracker.count);
+		assertTrue(knownElements.isDisposed());
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableViewerElementSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableViewerElementSetTest.java
new file mode 100644
index 0000000..ad54db7
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ObservableViewerElementSetTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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 213145
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.internal.databinding.viewers.ObservableViewerElementSet;
+import org.eclipse.jface.viewers.IElementComparer;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class ObservableViewerElementSetTest extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ObservableViewerElementSetTest.class.getName());
+		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	private static class Delegate extends
+			AbstractObservableCollectionContractDelegate {
+
+		public IObservableCollection createObservableCollection(Realm realm,
+				int elementCount) {
+			ObservableViewerElementSet set = new ObservableViewerElementSet(realm,
+					Object.class, new IdentityElementComparer());
+			for (int i = 0; i < elementCount; i++)
+				set.add(createElement(set));
+			return set;
+		}
+
+		public Object createElement(IObservableCollection collection) {
+			return new Object();
+		}
+
+		public void change(IObservable observable) {
+			IObservableSet set = (IObservableSet) observable;
+			set.add(createElement(set));
+		}
+
+		public Object getElementType(IObservableCollection collection) {
+			return Object.class;
+		}
+	}
+
+	private static class IdentityElementComparer implements IElementComparer {
+		public boolean equals(Object a, Object b) {
+			return a == b;
+		}
+
+		public int hashCode(Object element) {
+			return System.identityHashCode(element);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/SelectionProviderMultiSelectionObservableListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/SelectionProviderMultiSelectionObservableListTest.java
new file mode 100644
index 0000000..8777702
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/SelectionProviderMultiSelectionObservableListTest.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 Brad Reynolds.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *     Brad Reynolds - bug 116920
+ *     Matthew Hall - bug 194734
+ *     Ovidio Mallo - bug 270494
+ *******************************************************************************/
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for SelectionProviderMultiSelectionObservableList.
+ * 
+ * @since 1.2
+ */
+public class SelectionProviderMultiSelectionObservableListTest extends TestCase {
+	private IPostSelectionProvider selectionProvider;
+
+	private TableViewer viewer;
+
+	private static String[] model = new String[] { "element0", "element1",
+			"element2", "element3" };
+
+	protected void setUp() throws Exception {
+		Shell shell = new Shell();
+		viewer = new TableViewer(shell, SWT.MULTI);
+		viewer.setContentProvider(new ArrayContentProvider());
+		viewer.setInput(model);
+		selectionProvider = viewer;
+	}
+
+	protected void tearDown() throws Exception {
+		Shell shell = viewer.getTable().getShell();
+		if (!shell.isDisposed())
+			shell.dispose();
+	}
+
+	public void testConstructorIllegalArgumentException() {
+		try {
+			ViewersObservables.observeMultiSelection(null);
+			fail();
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testAddRemove_NormalSelection() {
+		doTestAddRemove(false);
+	}
+
+	public void testAddRemove_PostSelection() {
+		doTestAddRemove(true);
+	}
+
+	/**
+	 * Asserts that when a selection is set on the viewer:
+	 * <ul>
+	 * <li>the selection is available in the observable</li>
+	 * <li>Value change events are fired with appropriate diff values</li>
+	 * </ul>
+	 * 
+	 * @param postSelection
+	 *            <code>true</code> for observing the post selection,
+	 *            <code>false</code> for observing the normal selection.
+	 */
+	private void doTestAddRemove(boolean postSelection) {
+		IObservableList observable;
+		if (postSelection) {
+			observable = ViewersObservables
+					.observeMultiPostSelection(selectionProvider);
+		} else {
+			observable = ViewersObservables
+					.observeMultiSelection(selectionProvider);
+		}
+
+		ListChangeEventTracker listener = ListChangeEventTracker
+				.observe(observable);
+		assertEquals(0, observable.size());
+
+		selectionProvider.setSelection(new StructuredSelection(model[0]));
+		assertEquals(1, listener.count);
+		assertDiff(listener.event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList(model[0]));
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(1, observable.size());
+		assertEquals(model[0], observable.get(0));
+
+		selectionProvider.setSelection(new StructuredSelection(model[1]));
+		assertEquals(2, listener.count);
+		assertEquals(2, listener.event.diff.getDifferences().length);
+		assertDiff(listener.event.diff, Collections.singletonList(model[0]),
+				Collections.singletonList(model[1]));
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(1, observable.size());
+		assertEquals(model[1], observable.get(0));
+
+		selectionProvider.setSelection(new StructuredSelection(new Object[] {
+				model[2], model[3] }));
+		assertEquals(3, listener.count);
+		assertEquals(3, listener.event.diff.getDifferences().length);
+		assertDiff(listener.event.diff, Collections.singletonList(model[1]),
+				Arrays.asList(new Object[] { model[2], model[3] }));
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(2, observable.size());
+		assertEquals(model[2], observable.get(0));
+		assertEquals(model[3], observable.get(1));
+
+		selectionProvider.setSelection(StructuredSelection.EMPTY);
+		assertEquals(4, listener.count);
+		assertEquals(2, listener.event.diff.getDifferences().length);
+		assertDiff(listener.event.diff, Arrays.asList(new Object[] { model[2],
+				model[3] }), Collections.EMPTY_LIST);
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(0, observable.size());
+
+		observable.add(model[1]);
+		assertEquals(5, listener.count);
+		assertEquals(1, listener.event.diff.getDifferences().length);
+		assertDiff(listener.event.diff, Collections.EMPTY_LIST, Collections
+				.singletonList(model[1]));
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(1, ((IStructuredSelection) viewer.getSelection()).size());
+
+		observable.add(0, model[2]);
+		assertEquals(6, listener.count);
+		assertEquals(1, listener.event.diff.getDifferences().length);
+		// This is a bit surprising (we added at index 0 but the event says
+		// index 1). It is to the fact that the observable list tracks the
+		// underlying selection provider's notion of which element is at which
+		// index.
+		assertDiff(listener.event.diff, Collections.singletonList(model[1]),
+				Arrays.asList(new Object[] { model[1], model[2] }));
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(2, ((IStructuredSelection) viewer.getSelection()).size());
+
+		observable.clear();
+		assertEquals(7, listener.count);
+		assertEquals(2, listener.event.diff.getDifferences().length);
+		assertDiff(listener.event.diff, Arrays.asList(new Object[] { model[1],
+				model[2] }), Collections.EMPTY_LIST);
+		assertEquals(observable, listener.event.getObservableList());
+		assertEquals(0, ((IStructuredSelection) viewer.getSelection()).size());
+	}
+
+	private void assertDiff(ListDiff diff, List oldList, List newList) {
+		// defensive copy in case arg is unmodifiable
+		oldList = new ArrayList(oldList);
+		diff.applyTo(oldList);
+		assertEquals("applying diff to list did not produce expected result",
+				newList, oldList);
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValueTest.java
new file mode 100644
index 0000000..0a5dda6
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValueTest.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 Brad Reynolds.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *    Brad Reynolds - bug 116920
+ *    Ashley Cambrell - bug 198906
+ *    Matthew Hall - bug 194734
+ *    Ovidio Mallo - bug 270494
+ *******************************************************************************/
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import junit.framework.TestCase;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for SelectionProviderSingleSelectionObservableValue.
+ * 
+ * @since 1.1
+ */
+public class SelectionProviderSingleSelectionObservableValueTest extends
+		TestCase {
+	private IPostSelectionProvider selectionProvider;
+
+	private TableViewer viewer;
+
+	private static String[] model = new String[] { "0", "1" };
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		Shell shell = new Shell();
+		viewer = new TableViewer(shell, SWT.NONE);
+		viewer.setContentProvider(new ArrayContentProvider());
+		viewer.setInput(model);
+		selectionProvider = viewer;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	protected void tearDown() throws Exception {
+		Shell shell = viewer.getTable().getShell();
+		if (!shell.isDisposed())
+			shell.dispose();
+	}
+
+	public void testConstructorIllegalArgumentException() {
+		try {
+			ViewersObservables.observeSingleSelection(null);
+			fail();
+		} catch (IllegalArgumentException e) {
+		}
+	}
+
+	public void testSetValue() {
+		IObservableValue observable = ViewersObservables
+				.observeSingleSelection(selectionProvider);
+
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+		assertNull(observable.getValue());
+		assertEquals(0, listener.count);
+
+		observable.setValue(model[0]);
+		assertEquals(model[0], getSelectedElement(selectionProvider));
+		assertEquals(model[0], observable.getValue());
+		assertEquals(1, listener.count);
+
+		observable.setValue(model[1]);
+		assertEquals(model[1], getSelectedElement(selectionProvider));
+		assertEquals(model[1], observable.getValue());
+		assertEquals(2, listener.count);
+
+		observable.setValue(null);
+		assertTrue(selectionProvider.getSelection().isEmpty());
+		assertEquals(3, listener.count);
+	}
+
+	public void testSelectionChangesTracked() {
+		doTestSelectionChangesTracked(false);
+	}
+
+	public void testPostSelectionChangesTracked() {
+		doTestSelectionChangesTracked(true);
+	}
+
+	/**
+	 * Asserts that when a selection is set on the viewer:
+	 * <ul>
+	 * <li>the selection is available in the observable</li>
+	 * <li>Value change events are fired with appropriate diff values</li>
+	 * </ul>
+	 * 
+	 * @param postSelection
+	 *            <code>true</code> for observing the post selection,
+	 *            <code>false</code> for observing the normal selection.
+	 */
+	private void doTestSelectionChangesTracked(boolean postSelection) {
+		IObservableValue observable;
+		if (postSelection) {
+			observable = ViewersObservables
+					.observeSinglePostSelection(selectionProvider);
+		} else {
+			observable = ViewersObservables
+					.observeSingleSelection(selectionProvider);
+		}
+
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+		assertNull(observable.getValue());
+
+		selectionProvider.setSelection(new StructuredSelection(model[0]));
+		assertEquals(1, listener.count);
+		assertNull(listener.event.diff.getOldValue());
+		assertEquals(model[0], listener.event.diff.getNewValue());
+		assertEquals(observable, listener.event.getObservableValue());
+		assertEquals(model[0], observable.getValue());
+
+		selectionProvider.setSelection(new StructuredSelection(model[1]));
+		assertEquals(2, listener.count);
+		assertEquals(model[0], listener.event.diff.getOldValue());
+		assertEquals(model[1], listener.event.diff.getNewValue());
+		assertEquals(observable, listener.event.getObservableValue());
+		assertEquals(model[1], observable.getValue());
+
+		selectionProvider.setSelection(StructuredSelection.EMPTY);
+		assertEquals(3, listener.count);
+		assertEquals(model[1], listener.event.diff.getOldValue());
+		assertNull(listener.event.diff.getNewValue());
+		assertEquals(observable, listener.event.getObservableValue());
+		assertEquals(null, observable.getValue());
+	}
+
+	public void testDispose() throws Exception {
+		IObservableValue observable = ViewersObservables
+				.observeSingleSelection(selectionProvider);
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+
+		selectionProvider.setSelection(new StructuredSelection(model[0]));
+		assertEquals(1, listener.count);
+		assertNull(listener.event.diff.getOldValue());
+		assertEquals(model[0], listener.event.diff.getNewValue());
+		assertEquals(observable, listener.event.getObservableValue());
+		assertEquals(model[0], observable.getValue());
+
+		observable.dispose();
+		selectionProvider.setSelection(new StructuredSelection(model[1]));
+		assertEquals(1, listener.count);
+	}
+
+	private static Object getSelectedElement(
+			ISelectionProvider selectionProvider) {
+		return ((IStructuredSelection) selectionProvider.getSelection())
+				.getFirstElement();
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementMapTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementMapTest.java
new file mode 100644
index 0000000..1aece17
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementMapTest.java
@@ -0,0 +1,535 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jface.internal.databinding.viewers.ViewerElementMap;
+import org.eclipse.jface.viewers.IElementComparer;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 1.2
+ */
+public class ViewerElementMapTest extends TestCase {
+	IdentityElementComparer comparer;
+	ViewerElementMap map;
+
+	Object key;
+	Object value;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		comparer = new IdentityElementComparer();
+		map = new ViewerElementMap(comparer);
+		key = new Object();
+		value = new Object();
+	}
+
+	public void testConstructor_NullComparer() {
+		try {
+			new ViewerElementMap(null);
+			fail("Constructor should throw exception when null comparer passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_NullCollection() {
+		try {
+			new ViewerElementMap(null, new IdentityElementComparer());
+			fail("Constructor should throw exception when null collection passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_ContainsAllEntries() {
+		Map toCopy = new HashMap();
+		toCopy.put(new Object(), new Object());
+		map = new ViewerElementMap(toCopy, new IdentityElementComparer());
+		assertEquals(toCopy, map);
+	}
+
+	public void testIsEmpty() {
+		assertTrue(map.isEmpty());
+		map.put(key, value);
+		assertFalse(map.isEmpty());
+	}
+
+	public void testSize() {
+		assertEquals(0, map.size());
+		map.put(key, value);
+		assertEquals(1, map.size());
+	}
+
+	public void testClear() {
+		map.put(key, value);
+		assertFalse(map.isEmpty());
+		map.clear();
+		assertTrue(map.isEmpty());
+	}
+
+	public void testGet() {
+		assertNull(map.get(key));
+		map.put(key, value);
+		assertEquals(value, map.get(key));
+	}
+
+	public void testContainsKey() {
+		String key1 = new String("key");
+		String key2 = new String("key"); // equal but distinct instances
+		assertFalse(map.containsKey(key1));
+		map.put(key1, value);
+		assertTrue(map.containsKey(key1));
+		assertFalse(map.containsKey(key2));
+	}
+
+	public void testContainsValue() {
+		assertFalse(map.containsValue(value));
+		map.put(key, value);
+		assertTrue(map.containsValue(value));
+	}
+
+	public void testPutAll() {
+		Map other = new HashMap();
+		other.put(key, value);
+
+		assertTrue(map.isEmpty());
+		map.putAll(other);
+		assertEquals(1, map.size());
+		assertTrue(map.containsKey(key));
+		assertEquals(value, map.get(key));
+	}
+
+	public void testRemove() {
+		map.put(key, value);
+		assertTrue(map.containsKey(key));
+		map.remove(key);
+		assertFalse(map.containsKey(key));
+	}
+
+	public void testValues() {
+		Collection values = map.values();
+		assertTrue(values.isEmpty());
+
+		map.put(key, value);
+
+		assertEquals(1, values.size());
+		assertEquals(value, values.iterator().next());
+
+		map.remove(key);
+		assertTrue(map.values().isEmpty());
+	}
+
+	public void testKeySet() {
+		Set keySet = map.keySet();
+		assertTrue(keySet.isEmpty());
+
+		map.put(key, value);
+		assertEquals(1, keySet.size());
+		assertTrue(keySet.contains(key));
+
+		map.remove(key);
+		assertTrue(keySet.isEmpty());
+	}
+
+	public void testKeySet_Add() {
+		try {
+			map.keySet().add(key);
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testKeySet_AddAll() {
+		try {
+			map.keySet().addAll(Collections.singleton(key));
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testKeySet_Clear() {
+		map.put(key, value);
+		Set keySet = map.keySet();
+		assertTrue(keySet.contains(key));
+		keySet.clear();
+		assertTrue(keySet.isEmpty());
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_Contains() {
+		Set keySet = map.keySet();
+		assertFalse(keySet.contains(key));
+		map.put(key, value);
+		assertTrue(keySet.contains(key));
+	}
+
+	public void testKeySet_ContainsAll() {
+		Set keySet = map.keySet();
+		assertFalse(keySet.containsAll(Collections.singleton(key)));
+		map.put(key, value);
+		assertTrue(keySet.containsAll(Collections.singleton(key)));
+	}
+
+	public void testKeySet_IsEmpty() {
+		Set keySet = map.keySet();
+		assertTrue(keySet.isEmpty());
+		map.put(key, value);
+		assertFalse(keySet.isEmpty());
+	}
+
+	public void testKeySet_Iterator() {
+		map.put(key, value);
+		Iterator iterator = map.keySet().iterator();
+		assertTrue(iterator.hasNext());
+		assertEquals(key, iterator.next());
+
+		assertEquals(1, map.size());
+		iterator.remove();
+		assertTrue(map.isEmpty());
+
+		assertFalse(iterator.hasNext());
+	}
+
+	public void testKeySet_Remove() {
+		map.put(key, value);
+		assertEquals(1, map.size());
+		map.keySet().remove(key);
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_RemoveAll() {
+		map.put(key, value);
+		Set keySet = map.keySet();
+		assertFalse(keySet.removeAll(Collections.EMPTY_SET));
+		assertEquals(1, map.size());
+		assertTrue(keySet.removeAll(Collections.singleton(key)));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_RetainAll() {
+		map.put(key, value);
+		Set keySet = map.keySet();
+		assertFalse(keySet.retainAll(Collections.singleton(key)));
+		assertEquals(1, map.size());
+		assertTrue(keySet.retainAll(Collections.EMPTY_SET));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testKeySet_Size() {
+		Set keySet = map.keySet();
+		assertEquals(0, keySet.size());
+		map.put(key, value);
+		assertEquals(1, keySet.size());
+		map.clear();
+		assertEquals(0, keySet.size());
+	}
+
+	public void testKeySet_ToArray() {
+		Set keySet = map.keySet();
+		map.put(key, value);
+		Object[] array = keySet.toArray();
+		assertEquals(1, array.length);
+		assertSame(key, array[0]);
+	}
+
+	public void testKeySet_ToArrayWithObjectArray() {
+		key = new String("key");
+		map.put(key, value);
+		String[] array = (String[]) map.keySet().toArray(new String[0]);
+		assertEquals(1, array.length);
+		assertSame(key, array[0]);
+	}
+
+	public void testKeySet_Equals() {
+		Set keySet = map.keySet();
+		assertFalse(keySet.equals(null));
+		assertTrue(keySet.equals(keySet));
+
+		assertTrue(keySet.equals(Collections.EMPTY_SET));
+		map.put(key, value);
+		assertTrue(keySet.equals(Collections.singleton(key)));
+	}
+
+	public void testKeySet_HashCode() {
+		Set keySet = map.keySet();
+		assertEquals(0, keySet.hashCode());
+		map.put(key, value);
+		int hash = comparer.hashCode(key);
+		assertEquals(hash, keySet.hashCode());
+	}
+
+	public void testEntrySet() {
+		Set entrySet = map.entrySet();
+		assertTrue(entrySet.isEmpty());
+
+		map.put(key, value);
+		assertEquals(1, entrySet.size());
+		Map.Entry entry = (Map.Entry) entrySet.iterator().next();
+		assertEquals(key, entry.getKey());
+		assertEquals(value, entry.getValue());
+
+		map.remove(key);
+		assertTrue(entrySet.isEmpty());
+	}
+
+	public void testEntrySet_Add() {
+		try {
+			map.entrySet().add(key);
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testEntrySet_AddAll() {
+		try {
+			map.entrySet().addAll(Collections.EMPTY_SET);
+			fail("Expected UnsupportedOperationException");
+		} catch (UnsupportedOperationException expected) {
+		}
+	}
+
+	public void testEntrySet_Clear() {
+		map.put(key, value);
+		assertEquals(1, map.size());
+		map.entrySet().clear();
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_Contains() {
+		map.put(key, value);
+		Set entrySet = map.entrySet();
+		assertTrue(entrySet.contains(new MapEntryStub(key, value)));
+		map.remove(key);
+		assertFalse(entrySet.contains(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_ContainsAll() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.containsAll(Collections
+				.singleton(new MapEntryStub(key, value))));
+		assertTrue(entrySet.containsAll(Collections.EMPTY_SET));
+
+		map.put(key, value);
+		assertTrue(entrySet.containsAll(Collections.singleton(new MapEntryStub(
+				key, value))));
+	}
+
+	public void testEntrySet_IsEmpty() {
+		Set entrySet = map.entrySet();
+		assertTrue(entrySet.isEmpty());
+		map.put(key, value);
+		assertFalse(entrySet.isEmpty());
+	}
+
+	public void testEntrySet_Iterator() {
+		map.put(key, value);
+		Iterator iterator = map.entrySet().iterator();
+		assertTrue(iterator.hasNext());
+		Map.Entry entry = (Map.Entry) iterator.next();
+		assertTrue(entry.equals(new MapEntryStub(key, value)));
+
+		assertEquals(1, map.size());
+		iterator.remove();
+		assertTrue(map.isEmpty());
+		assertFalse(iterator.hasNext());
+	}
+
+	public void testEntrySet_Remove() {
+		map.put(key, value);
+		assertEquals(1, map.size());
+
+		assertTrue(map.entrySet().remove(new MapEntryStub(key, value)));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_RemoveAll() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.removeAll(Collections.EMPTY_SET));
+
+		map.put(key, value);
+		assertEquals(1, map.size());
+		assertTrue(entrySet.removeAll(Collections.singleton(new MapEntryStub(
+				key, value))));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_RetainAll() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.retainAll(Collections.EMPTY_SET));
+
+		map.put(key, value);
+		assertEquals(1, map.size());
+		assertFalse(entrySet.retainAll(Collections.singleton(new MapEntryStub(
+				key, value))));
+		assertEquals(1, map.size());
+		assertTrue(entrySet.retainAll(Collections.EMPTY_SET));
+		assertTrue(map.isEmpty());
+	}
+
+	public void testEntrySet_Size() {
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.size());
+		map.put(key, value);
+		assertEquals(1, entrySet.size());
+	}
+
+	public void testEntrySet_ToArray() {
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.toArray().length);
+
+		map.put(key, value);
+		Object[] array = entrySet.toArray();
+		assertEquals(1, array.length);
+		assertTrue(array[0].equals(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_ToArrayWithObjectArray() {
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.toArray(new Object[0]).length);
+
+		map.put(key, value);
+		Map.Entry[] array = (Map.Entry[]) entrySet.toArray(new Map.Entry[0]);
+		assertEquals(1, array.length);
+		assertTrue(array[0].equals(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_Equals() {
+		Set entrySet = map.entrySet();
+		assertFalse(entrySet.equals(null));
+		assertTrue(entrySet.equals(entrySet));
+
+		assertTrue(entrySet.equals(Collections.EMPTY_SET));
+		assertFalse(entrySet.equals(Collections.singleton(new MapEntryStub(key,
+				value))));
+
+		map.put(key, value);
+		assertFalse(entrySet.equals(Collections.EMPTY_SET));
+		assertTrue(entrySet.equals(Collections.singleton(new MapEntryStub(key,
+				value))));
+	}
+
+	public void testEntrySet_HashCode() {
+		// hash formula mandated by Map contract
+		Set entrySet = map.entrySet();
+		assertEquals(0, entrySet.hashCode());
+
+		map.put(key, value);
+		int hash = comparer.hashCode(key) ^ value.hashCode();
+		assertEquals(hash, entrySet.hashCode());
+	}
+
+	public void testEntrySet_Entry_SetValue() {
+		map.put(key, value);
+
+		Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
+
+		Object newValue = new Object();
+		Object oldValue = entry.setValue(newValue);
+		assertEquals(value, oldValue);
+		assertEquals(newValue, entry.getValue());
+		assertEquals(newValue, map.get(key));
+	}
+
+	public void testEntrySet_Entry_Equals() {
+		map.put(key, value);
+
+		Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
+		assertFalse(entry.equals(null));
+		assertTrue(entry.equals(entry));
+		assertTrue(entry.equals(new MapEntryStub(key, value)));
+	}
+
+	public void testEntrySet_Entry_HashCode() {
+		map.put(key, value);
+
+		// hash computed as required by Map contract
+		int hash = comparer.hashCode(key) ^ value.hashCode();
+		assertEquals(hash, map.entrySet().iterator().next().hashCode());
+	}
+
+	public void testEquals() {
+		assertFalse(map.equals(null));
+		assertTrue(map.equals(map));
+
+		Map other = new HashMap();
+		other.put(key, value);
+
+		assertTrue(map.equals(Collections.EMPTY_MAP));
+		assertFalse(map.equals(other));
+
+		map.put(key, value);
+
+		assertFalse(map.equals(Collections.EMPTY_MAP));
+		assertTrue(map.equals(other));
+	}
+
+	public void testHashCode() {
+		assertEquals(0, map.hashCode());
+
+		map.put(key, value);
+		int hash = comparer.hashCode(key) ^ value.hashCode();
+		assertEquals(hash, map.hashCode());
+	}
+
+	public void testWithComparer() {
+		assertFalse(ViewerElementMap.withComparer(null) instanceof ViewerElementMap);
+		assertTrue(ViewerElementMap.withComparer(comparer) instanceof ViewerElementMap);
+	}
+
+	static class IdentityElementComparer implements IElementComparer {
+		public boolean equals(Object a, Object b) {
+			return a == b;
+		}
+
+		public int hashCode(Object element) {
+			return System.identityHashCode(element);
+		}
+	}
+
+	static class MapEntryStub implements Map.Entry {
+		private final Object key;
+		private final Object value;
+
+		public MapEntryStub(Object key, Object value) {
+			this.key = key;
+			this.value = value;
+		}
+
+		public Object getKey() {
+			return key;
+		}
+
+		public Object getValue() {
+			return value;
+		}
+
+		public Object setValue(Object value) {
+			throw new UnsupportedOperationException();
+		}
+
+		public boolean equals(Object obj) {
+			throw new UnsupportedOperationException();
+		}
+
+		public int hashCode() {
+			throw new UnsupportedOperationException();
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementSetTest.java
new file mode 100644
index 0000000..4d4a55b
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementSetTest.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * 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)
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.eclipse.jface.internal.databinding.viewers.ViewerElementSet;
+import org.eclipse.jface.viewers.IElementComparer;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 1.2
+ */
+public class ViewerElementSetTest extends TestCase {
+	IdentityElementComparer comparer;
+	ViewerElementSet set;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		comparer = new IdentityElementComparer();
+		set = new ViewerElementSet(comparer);
+	}
+
+	public void testConstructor_NullComparer() {
+		try {
+			new ViewerElementSet(null);
+			fail("Constructor should throw exception when null comparer passed in");
+		} catch (RuntimeException expected) {
+		}
+	}
+
+	public void testConstructorWithCollection_NullCollection() {
+		try {
+			new ViewerElementSet(null, new IdentityElementComparer());
+			fail("Constructor should throw exception when null collection passed in");
+		} catch (RuntimeException expected) {}
+	}
+
+	public void testConstructorWithCollection_AddsAllElements() {
+		Collection toCopy = Collections.singleton(new Object());
+		set = new ViewerElementSet(toCopy, new IdentityElementComparer());
+		assertTrue(set.containsAll(toCopy));
+	}
+
+	public void testAdd_ContainsHonorsComparer() {
+		Object o1 = new String("string");
+		Object o2 = new String("string"); // distinct instances
+		assertTrue(o1.equals(o2));
+		assertNotSame(o1, o2);
+
+		assertTrue(set.add(o1));
+
+		assertTrue(set.contains(o1));
+		assertFalse(set.contains(o2));
+	}
+
+	public void testAdd_FilterDuplicateElements() {
+		Object o = new Object();
+
+		assertTrue(set.add(o));
+		assertFalse(set.add(o)); // no change--element already in set
+
+		assertEquals(1, set.size());
+		assertTrue(set.contains(o));
+	}
+
+	public void testAddAll_ContainsAllHonorsComparer() {
+		String o1 = new String("o1");
+		String o2 = new String("o2");
+		Collection items = Arrays.asList(new Object[] { o1, o2 });
+		assertTrue(set.addAll(items));
+
+		assertTrue(set.containsAll(items));
+		assertFalse(set.containsAll(Collections.singleton(new String("o1"))));
+		assertFalse(set.containsAll(Collections.singleton(new String("o2"))));
+	}
+
+	public void testAddAll_FiltersDuplicateElements() {
+		Object o = new Object();
+		set.add(o);
+
+		assertFalse(set.addAll(Collections.singleton(o)));
+	}
+
+	public void testClear() {
+		set.add(new Object());
+		assertEquals(1, set.size());
+
+		set.clear();
+		assertEquals(0, set.size());
+	}
+
+	public void testIsEmpty() {
+		assertTrue(set.isEmpty());
+		set.add(new Object());
+		assertFalse(set.isEmpty());
+	}
+
+	public void testIterator() {
+		Object o = new Object();
+		set.add(o);
+
+		Iterator iterator = set.iterator();
+		assertTrue(iterator.hasNext());
+		assertSame(o, iterator.next());
+
+		assertTrue(set.contains(o));
+		iterator.remove();
+		assertFalse(set.contains(o));
+
+		assertFalse(iterator.hasNext());
+	}
+
+	public void testRemove() {
+		Object o = new Object();
+		assertFalse(set.remove(o));
+
+		assertTrue(set.add(o));
+		assertTrue(set.contains(o));
+		assertTrue(set.remove(o));
+
+		assertFalse(set.contains(o));
+	}
+
+	public void testRemoveAll() {
+		assertFalse(set.removeAll(Collections.EMPTY_SET));
+
+		Object o1 = new Object();
+		Object o2 = new Object();
+		set.addAll(Arrays.asList(new Object[] { o1, o2 }));
+
+		assertTrue(set.removeAll(Collections.singleton(o1)));
+		assertFalse(set.contains(o1));
+		assertFalse(set.removeAll(Collections.singleton(o1)));
+
+		assertTrue(set.removeAll(Arrays.asList(new Object[] { o2, "some",
+				"other", "objects" })));
+		assertFalse(set.contains(o2));
+	}
+
+	public void testRetainAll() {
+		Object o1 = new Object();
+		Object o2 = new Object();
+		set.add(o1);
+		set.add(o2);
+
+		assertFalse(set.retainAll(Arrays.asList(new Object[] { o1, o2 }))); // no
+																			// change
+
+		assertTrue(set.contains(o2));
+		assertTrue(set.retainAll(Collections.singleton(o1)));
+		assertFalse(set.contains(o2));
+
+		assertTrue(set.contains(o1));
+		assertTrue(set.retainAll(Collections.EMPTY_SET));
+		assertFalse(set.contains(o1));
+	}
+
+	public void testSize() {
+		assertEquals(0, set.size());
+
+		Object o = new Object();
+		set.add(o);
+		assertEquals(1, set.size());
+
+		set.remove(o);
+		assertEquals(0, set.size());
+	}
+
+	public void testToArray() {
+		assertEquals(0, set.toArray().length);
+
+		Object o = new Object();
+		set.add(o);
+		assertTrue(Arrays.equals(new Object[] { o }, set.toArray()));
+	}
+
+	public void testToArrayWithObjectArray() {
+		Object o = new String("unique");
+		set.add(o);
+
+		String[] array = (String[]) set.toArray(new String[0]);
+		assertEquals(1, array.length);
+		assertSame(o, array[0]);
+	}
+
+	public void testEquals() {
+		assertTrue(set.equals(set));
+		assertFalse(set.equals(null));
+		assertFalse(set.equals(new Object()));
+
+		assertTrue(set.equals(Collections.EMPTY_SET));
+
+		Object o = new String("string");
+		Object distinct = new String("string");
+		set.add(o);
+		assertTrue(set.equals(Collections.singleton(o)));
+		assertFalse(set.equals(Collections.singleton(distinct)));
+	}
+
+	public void testHashCode() {
+		// Hash code implementation is mandated
+		assertEquals(0, set.hashCode());
+
+		Object o = new Object();
+		set.add(o);
+		int hash = comparer.hashCode(o);
+		assertEquals(hash, set.hashCode());
+
+		Object o2 = new Object();
+		set.add(o2);
+		hash += comparer.hashCode(o2);
+		assertEquals(hash, set.hashCode());
+	}
+
+	static class IdentityElementComparer implements IElementComparer {
+		public boolean equals(Object a, Object b) {
+			return a == b;
+		}
+
+		public int hashCode(Object element) {
+			return System.identityHashCode(element);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementWrapperTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementWrapperTest.java
new file mode 100644
index 0000000..f85d811
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerElementWrapperTest.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import org.eclipse.jface.internal.databinding.viewers.ViewerElementWrapper;
+import org.eclipse.jface.viewers.IElementComparer;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 3.2
+ * 
+ */
+public class ViewerElementWrapperTest extends TestCase {
+	private ViewerElementWrapper wrapper;
+	private Object element;
+	private IElementComparer comparer;
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		element = new ElementStub(0);
+		comparer = new IdentityElementComparer();
+		wrapper = new ViewerElementWrapper(element, comparer);
+	}
+
+	public void testConstructor_NullComparer() {
+		try {
+			new ViewerElementWrapper(element, null);
+			fail("Expected NullPointerException");
+		} catch (NullPointerException expected) {
+		}
+	}
+
+	public void testEquals() {
+		assertFalse(wrapper.equals(null));
+		assertTrue(wrapper.equals(wrapper));
+		assertTrue(wrapper.equals(new ViewerElementWrapper(element, comparer)));
+	}
+
+	public void testHashCode() {
+		int hash = 0;
+		element = new ElementStub(hash);
+		wrapper = new ViewerElementWrapper(element, comparer);
+		assertEquals(System.identityHashCode(element), wrapper.hashCode());
+		assertEquals(hash, element.hashCode());
+	}
+
+	static class ElementStub {
+		private final int hash;
+
+		public ElementStub(int hash) {
+			this.hash = hash;
+		}
+
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj == null)
+				return false;
+			if (obj.getClass() != getClass())
+				return false;
+			ElementStub that = (ElementStub) obj;
+			return this.hash == that.hash;
+		}
+
+		public int hashCode() {
+			return hash;
+		}
+	}
+
+	static class IdentityElementComparer implements IElementComparer {
+		public boolean equals(Object a, Object b) {
+			return a == b;
+		}
+
+		public int hashCode(Object element) {
+			return System.identityHashCode(element);
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerInputObservableValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerInputObservableValueTest.java
new file mode 100644
index 0000000..bb75b25
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/internal/databinding/viewers/ViewerInputObservableValueTest.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 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)
+ *     Matthew Hall - bug 213145, 194734, 195222
+ *******************************************************************************/
+package org.eclipse.jface.tests.internal.databinding.viewers;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.databinding.viewers.ViewersObservables;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Tests for ViewerInputObservableValue.
+ * 
+ * @since 1.2
+ */
+public class ViewerInputObservableValueTest extends
+		AbstractDefaultRealmTestCase {
+	private TableViewer viewer;
+	private static String[] model = new String[] { "0", "1" };
+
+	protected void setUp() throws Exception {
+		super.setUp();
+		Shell shell = new Shell();
+		viewer = new TableViewer(shell, SWT.NONE);
+		viewer.setContentProvider(new ContentProvider());
+	}
+
+	protected void tearDown() throws Exception {
+		Shell shell = viewer.getTable().getShell();
+		if (!shell.isDisposed())
+			shell.dispose();
+		super.tearDown();
+	}
+
+	public void testConstructor_IllegalArgumentException() {
+		try {
+			ViewersObservables.observeInput(null);
+			fail("Expected IllegalArgumentException for null argument");
+		} catch (IllegalArgumentException expected) {
+		}
+	}
+
+	public void testSetInputOnViewer_FiresChangeEventOnGetValue() {
+		IObservableValue observable = ViewersObservables.observeInput(viewer);
+		ValueChangeEventTracker listener = ValueChangeEventTracker
+				.observe(observable);
+
+		assertNull(viewer.getInput());
+		assertEquals(0, listener.count);
+
+		viewer.setInput(model);
+
+		assertEquals(model, viewer.getInput());
+		assertEquals(0, listener.count);
+
+		// Call to getValue() causes observable to discover change
+		assertEquals(model, observable.getValue());
+		assertEquals(1, listener.count);
+
+		viewer.setInput(null);
+		assertEquals(null, viewer.getInput());
+
+		assertEquals(null, observable.getValue());
+		assertEquals(2, listener.count);
+	}
+
+	public void testGetSetValue_FiresChangeEvents() {
+		IObservableValue observable = ViewersObservables.observeInput(viewer);
+		ValueChangeEventTracker listener = new ValueChangeEventTracker();
+		observable.addValueChangeListener(listener);
+
+		assertNull(observable.getValue());
+		assertEquals(0, listener.count);
+
+		observable.setValue(model);
+
+		assertEquals(model, observable.getValue());
+		assertEquals(1, listener.count);
+		assertValueChangeEventEquals(observable, null, model, listener.event);
+
+		observable.setValue(null);
+
+		assertNull(observable.getValue());
+		assertEquals(2, listener.count);
+		assertValueChangeEventEquals(observable, model, null, listener.event);
+	}
+
+	public void testGetValueType_AlwaysNull() throws Exception {
+		IObservableValue observable = ViewersObservables.observeInput(viewer);
+		assertEquals(null, observable.getValueType());
+	}
+
+	public void testDispose() throws Exception {
+		IObservableValue observable = ViewersObservables.observeInput(viewer);
+		observable.dispose();
+		try {
+			observable.setValue(model);
+			fail("Expected NullPointerException");
+		} catch (NullPointerException expected) {
+		}
+	}
+
+	private void assertValueChangeEventEquals(
+			IObservableValue expectedObservable, Object expectedOldValue,
+			Object expectedNewValue, ValueChangeEvent event) {
+		assertSame(expectedObservable, event.getObservableValue());
+		assertEquals(expectedOldValue, event.diff.getOldValue());
+		assertEquals(expectedNewValue, event.diff.getNewValue());
+	}
+
+	static class ContentProvider implements IStructuredContentProvider {
+		public void dispose() {
+		}
+
+		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+		}
+
+		public Object[] getElements(Object inputElement) {
+			return (String[]) inputElement;
+		}
+	}
+
+	public static Test suite() {
+		TestSuite suite = new TestSuite(ViewerInputObservableValueTest.class
+				.getName());
+		suite.addTestSuite(ViewerInputObservableValueTest.class);
+		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
+		return suite;
+	}
+
+	static class Delegate extends AbstractObservableValueContractDelegate {
+		TableViewer viewer;
+
+		public void setUp() {
+			super.setUp();
+			Shell shell = new Shell();
+			viewer = new TableViewer(shell, SWT.NONE);
+			viewer.setContentProvider(new ContentProvider());
+		}
+
+		public void tearDown() {
+			Shell shell = viewer.getTable().getShell();
+			if (!shell.isDisposed())
+				shell.dispose();
+			super.tearDown();
+		}
+
+		public IObservableValue createObservableValue(Realm realm) {
+			return ViewerProperties.input().observe(realm, viewer);
+		}
+
+		public void change(IObservable observable) {
+			IObservableValue value = (IObservableValue) observable;
+			value.setValue(createValue(value));
+		}
+
+		public Object createValue(IObservableValue observable) {
+			return new String[] { "one", "two" };
+		}
+
+		public Object getValueType(IObservableValue observable) {
+			return null;
+		}
+	}
+}
diff --git a/tests/org.eclipse.jface.tests.databinding/test.xml b/tests/org.eclipse.jface.tests.databinding/test.xml
new file mode 100644
index 0000000..73284d5
--- /dev/null
+++ b/tests/org.eclipse.jface.tests.databinding/test.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<project name="testsuite" default="run" basedir=".">
+  <!-- The property ${eclipse-home} should be passed into this script -->
+  <!-- Set a meaningful default value for when it is not. -->
+  <property name="eclipse-home" value="${basedir}\..\.."/>
+
+  <!-- sets the properties eclipse-home, and library-file -->
+  <property name="plugin-name" value="org.eclipse.jface.tests.databinding"/>
+  <property name="library-file"
+            value="${eclipse-home}/plugins/org.eclipse.test/library.xml"/>
+
+  <!-- This target holds all initialization code that needs to be done for -->
+  <!-- all tests that are to be run. Initialization for individual tests -->
+  <!-- should be done within the body of the suite target. -->
+  <target name="init">
+    <tstamp/>
+    <delete>
+      <fileset dir="${eclipse-home}" includes="org*.xml"/>
+    </delete>
+  </target>
+
+  <!-- This target defines the tests that need to be run. -->
+  <target name="suite">
+    <property name="sniff-folder"
+              value="${eclipse-home}/databinding_sniff_folder"/>
+    <delete dir="${sniff-folder}" quiet="true"/>
+    <ant target="core-test" antfile="${library-file}" dir="${eclipse-home}">
+      <property name="data-dir" value="${sniff-folder}"/>
+      <property name="plugin-name" value="${plugin-name}"/>
+      <property name="classname"
+                value="org.eclipse.jface.tests.databinding.BindingTestSuite"/>
+    </ant>
+  </target>
+
+  <!-- This target holds code to cleanup the testing environment after -->
+  <!-- after all of the tests have been run. You can use this target to -->
+  <!-- delete temporary files that have been created. -->
+  <target name="cleanup">
+  </target>
+
+  <!-- This target runs the test suite. Any actions that need to happen -->
+  <!-- after all the tests have been run should go here. -->
+  <target name="run" depends="init,suite,cleanup">
+    <ant target="collect" antfile="${library-file}" dir="${eclipse-home}">
+      <property name="includes" value="org*.xml"/>
+      <property name="output-file" value="${plugin-name}.xml"/>
+    </ant>
+  </target>
+</project>