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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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 "children" property of a Person object,
+ * // where the elements are Person objects
+ * IBeanListProperty children = BeanProperties.list(Person.class, "children",
+ * Person.class);
+ * // Observes the string-typed "name" property of a Person object
+ * IBeanValueProperty name = BeanProperties.value(Person.class, "name");
+ * // 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 "children" property of a Person object,
+ * // where the elements are Person objects
+ * IBeanSetProperty children = BeanProperties.set(Person.class, "children",
+ * Person.class);
+ * // Observes the string-typed "name" property of a Person object
+ * IBeanValueProperty name = BeanProperties.value(Person.class, "name");
+ * // 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 "parent" property of a Node object
+ * IBeanValueProperty parent = BeanProperties.value(Node.class, "parent");
+ * // Observes the string-typed "name" property of a Node object
+ * IBeanValueProperty name = BeanProperties.value(Node.class, "name");
+ * // 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 "parent" property of a Node object.
+ * IBeanValueProperty parent = BeanProperties.value(Node.class, "parent");
+ * // Observes the List-typed "children" property of a Node object
+ * // where the elements are Node objects
+ * IBeanListProperty children = BeanProperties.list(Node.class, "children",
+ * 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 "parent" property of a Node object.
+ * IBeanValueProperty parent = BeanProperties.value(Node.class, "parent");
+ * // Observes the Set-typed "children" property of a Node object
+ * // where the elements are Node objects
+ * IBeanSetProperty children = BeanProperties.set(Node.class, "children",
+ * 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 "supervisor" property of a
+ * // Contact class
+ * IBeanValueProperty supervisor = BeanProperties.value(Contact.class,
+ * "supervisor");
+ * // Observes the property "phoneNumbers" of a Contact object--a property mapping
+ * // from PhoneNumberType to PhoneNumber "set-typed "children",
+ * IBeanMapProperty phoneNumbers = BeanProperties.map(Contact.class,
+ * "phoneNumbers", 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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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 <= oldIndex < size()</code>.
+ * @param newIndex
+ * the element's position after the move. Must be within the
+ * range <code>0 <= newIndex < size()</code>.
+ * @return the element that was moved.
+ * @throws IndexOutOfBoundsException
+ * if either argument is out of range (<code>0 <= index < 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} < {@link Integer} >.
+ * </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 < 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); // => "[]"
+ *
+ * count.setValue(new Integer(5));
+ * System.out.println(fibonacci); // => "[0, 1, 1, 2, 3]"
+ * </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 <= oldIndex < size()</code>.
+ * @param newIndex
+ * the element's position after the move. Must be within the
+ * range <code>0 <= newIndex < size()</code>.
+ * @return the element that was moved.
+ * @throws IndexOutOfBoundsException
+ * if either argument is out of range (<code>0 <= index < 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 <= oldIndex < size()</code>.
+ * @param newIndex
+ * the element's position after the move. Must be within the
+ * range <code>0 <= newIndex < size()</code>.
+ * @return the element that was moved.
+ * @throws IndexOutOfBoundsException
+ * if either argument is out of range (
+ * <code>0 <= index < 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} < {@link Integer} >.
+ * </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 < 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); // => "[]"
+ *
+ * max.setValue(new Integer(20));
+ * System.out.println(primes); // => "[2, 3, 5, 7, 11, 13, 17, 19]"
+ * </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} <
+ * {@link Integer} >.
+ * </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()); // => 3
+ *
+ * addends.add(new Integer(10));
+ * System.out.println(sum.getValue()); // => 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} < {@link java.util.Date} > 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} < {@link java.util.Date}
+ * >) and the hour, minute, second, and millisecond portion of the time
+ * observable (also an {@link IObservableValue} < {@link java.util.Date}
+ * >).
+ * <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 <select> and
+ * <option> 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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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} <
+ * {@link IObservableValue} >.
+ * <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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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} < {@link Binding} > for observing
+ * the bindings of a {@link DataBindingContext}.
+ *
+ * @return an {@link IListProperty} < {@link Binding} > for observing
+ * the bindings of a {@link DataBindingContext}.
+ */
+ public static IListProperty bindings() {
+ return new DataBindingContextBindingsProperty();
+ }
+
+ /**
+ * Returns an {@link IValueProperty} < {@link IObservable} > for
+ * observing the model of a {@link Binding}.
+ *
+ * @return an {@link IValueProperty} < {@link IObservable} > for
+ * observing the model of a {@link Binding}.
+ */
+ public static IValueProperty model() {
+ return new BindingModelProperty();
+ }
+
+ /**
+ * Returns an {@link IListProperty} < {@link IObservable} > for
+ * observing the models of a {@link ValidationStatusProvider}.
+ *
+ * @return an {@link IListProperty} < {@link IObservable} > for
+ * observing the models of a {@link ValidationStatusProvider}.
+ */
+ public static IListProperty models() {
+ return new ValidationStatusProviderModelsProperty();
+ }
+
+ /**
+ * Returns an {@link IValueProperty} < {@link IObservable} > for
+ * observing the target of a {@link Binding}.
+ *
+ * @return an {@link IValueProperty} < {@link IObservable} > for
+ * observing the target of a {@link Binding}.
+ */
+ public static IValueProperty target() {
+ return new BindingTargetProperty();
+ }
+
+ /**
+ * Returns an {@link IListProperty} < {@link IObservable} > for
+ * observing the targets of a {@link ValidationStatusProvider}.
+ *
+ * @return an {@link IListProperty} < {@link IObservable} > for
+ * observing the targets of a {@link ValidationStatusProvider}.
+ */
+ public static IListProperty targets() {
+ return new ValidationStatusProviderTargetsProperty();
+ }
+
+ /**
+ * Returns an {@link IValueProperty} < {@link IStatus} > for observing
+ * the validation status of a {@link ValidationStatusProvider}.
+ *
+ * @return an {@link IValueProperty} < {@link IStatus} > 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} < {@link ValidationStatusProvider}
+ * > for observing the validation status providers of a
+ * {@link DataBindingContext}.
+ *
+ * @return an {@link IListProperty} < {@link ValidationStatusProvider}
+ * > 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} < {@link Binding} >
+ * of all bindings in order by time of addition.
+ *
+ * @return an unmodifiable {@link IObservableList} < {@link Binding} >
+ * of all bindings
+ */
+ public final IObservableList getBindings() {
+ return unmodifiableBindings;
+ }
+
+ /**
+ * Returns an unmodifiable an unmodifiable {@link IObservableList} <
+ * {@link ValidationStatusProvider} > of all validation status providers
+ * in order by time of addition.
+ *
+ * @return an unmodifiable {@link IObservableList} <
+ * {@link ValidationStatusProvider} > of all validation status
+ * providers
+ * @since 1.1
+ */
+ public final IObservableList getValidationStatusProviders() {
+ return unmodifiableStatusProviders;
+ }
+
+ /**
+ * Returns an {@link IObservableMap} < {@link Binding}, {@link IStatus}
+ * > 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}< {@link IStatus} > containing
+ * the current validation status
+ */
+ public abstract IObservableValue getValidationStatus();
+
+ /**
+ * Returns an {@link IObservableList} < {@link IObservable} >
+ * containing the target observables (if any) that are being tracked by this
+ * validation status provider.
+ *
+ * @return an {@link IObservableList} < {@link IObservable} > (may be
+ * empty)
+ */
+ public abstract IObservableList getTargets();
+
+ /**
+ * Returns an {@link IObservableList} < {@link IObservable} >
+ * containing the model observables (if any) that are being tracked by this
+ * validation status provider.
+ *
+ * @return an {@link IObservableList} < {@link IObservable} > (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("Values must be both even or both odd");
+ * return ValidationStatus.ok();
+ * }
+ * };
+ * dbc.addValidationStatusProvider(validator);
+ *
+ * // Bind the middle observables to the model observables.
+ * IObservableValue model0 = new WritableValue(new Integer(2), Integer.TYPE);
+ * IObservableValue model1 = new WritableValue(new Integer(4), Integer.TYPE);
+ * dbc.bind(middle0, model0, null, null);
+ * dbc.bind(middle1, model1, null, null);
+ * </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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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 ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). 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, "Program" 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 ("Redistributor") 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<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>