Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2014-05-23 02:44:04 +0000
committerCamille Letavernier2014-08-07 09:17:38 +0000
commitec9e7064748f00f1cbaea710103064b1738356b8 (patch)
treee14655f44e32bb9127a06e1ea2618297d9bed062
parentb0896efe2e99b9eb52b80d5caea3bf3693c9d661 (diff)
downloadorg.eclipse.papyrus-ec9e7064748f00f1cbaea710103064b1738356b8.tar.gz
org.eclipse.papyrus-ec9e7064748f00f1cbaea710103064b1738356b8.tar.xz
org.eclipse.papyrus-ec9e7064748f00f1cbaea710103064b1738356b8.zip
417409: [Performances - Properties view] Delay in UI when reorganizing diagram layout.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=417409 Make property sheet views reusable, with updating of the bound selection when the selection changes to another element that shows the same views. This employs new capability of the DataSource to update the selection that it encapsulates, pushing the new selection into the ModelElements that it creates, using a new delegating observable framework. Property sheet controls are re-used on a per-tab basis. Because of the new delegation pattern introduced here, we need to be able to ensure that delegate observables are disposed of when they are no longer needed. This includes not only the delegates of the new DelegatingObservables, but also the delegates of MultipleObservableValue and similar aggregates. As these delegates can be shared amongst multiple wrappers of different kinds, we use a simple reference counting scheme to ensure that observables are not disposed while they are still in use. This averts the exceptions discovered in multi-observable (multiple selection) scenarios on a previous iteration of this patch set. Change-Id: Ide8f3fcea4228083a68bc9d5d39dc5a50217af62
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/editor/preview/Preview.java9
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElement.java6
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElementFactory.java27
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElement.java8
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElementFactory.java22
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericPropertyModelElementFactory.java22
-rw-r--r--plugins/customization/org.eclipse.papyrus.customization/src/org/eclipse/papyrus/customization/modelelement/CustomizationModelElementFactory.java8
-rw-r--r--plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSModelElementFactory.java13
-rw-r--r--plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSPreferencesModelElementFactory.java8
-rw-r--r--plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElement.java9
-rw-r--r--plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElementFactory.java22
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/AppearanceModelElementFactory.java4
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/CustomStyleModelElementFactory.java12
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/DecoratedModelElementFactory.java24
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/NotationModelElementFactory.java42
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulerAndGridModelElement.java11
-rw-r--r--plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulersAndGridModelElementFactory.java34
-rw-r--r--plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.properties/src/org/eclipse/papyrus/infra/nattable/properties/modelelement/NatTableFactory.java19
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF3
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java174
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java348
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java131
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java205
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java128
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java140
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java53
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java14
-rw-r--r--plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java372
-rw-r--r--plugins/infra/widget/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/AbstractValueEditor.java19
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/BehaviorDisplayHelper.java22
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/GateModelElementFactory.java20
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/InteractionOperandModelElementFactory.java23
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/LinkRouteModelElementFactory.java11
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/MessageStyleElementFactory.java32
-rw-r--r--plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/PreferencesModelElementFactory.java23
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CommentModelElementFactory.java12
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CustomImageModelElementFactory.java21
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/MemberEndModelElement.java5
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/ProfileDefinitionModelElementFactory.java20
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceFactory.java23
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceModelElement.java4
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationFactory.java24
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationModelElement.java10
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeModelElementFactory.java13
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLEditorFactory.java21
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLModelElementFactory.java11
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationFactory.java19
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationModelElement.java3
-rw-r--r--plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/widgets/StereotypeApplication.java32
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/AbstractUMLAggregatedObservableValue.java8
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/MultiplicityObservableValue.java5
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/NavigationObservableValue.java5
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/OwnerObservableValue.java5
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/PapyrusObservableValue.java20
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractEMFModelElementFactory.java41
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElement.java98
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElementFactory.java40
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AnnotationModelElementFactory.java18
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/CompositeModelElement.java70
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSource.java57
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceChangedEvent.java32
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceFactory.java11
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/EMFModelElementFactory.java10
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/IDataSourceListener.java30
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/PreferencesModelElementFactory.java21
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/DefaultDisplayEngine.java95
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/TabModel.java165
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/widgets/AbstractPropertyEditor.java59
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSection.java18
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSectionDescriptor.java34
-rw-r--r--plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTTabDescriptor.java39
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF9
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java31
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollectionTest.java302
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableListTest.java350
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSetTest.java210
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableTest.java358
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValueTest.java210
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/ObservableFixture.java31
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java207
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/TrackedGetterTest.java34
-rw-r--r--tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/tests/AllTests.java3
-rw-r--r--tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/META-INF/MANIFEST.MF3
83 files changed, 4544 insertions, 321 deletions
diff --git a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/editor/preview/Preview.java b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/editor/preview/Preview.java
index 176eb5e4ced..50befdfc36a 100644
--- a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/editor/preview/Preview.java
+++ b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/editor/preview/Preview.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010, 2013 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,6 +9,8 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - Use URIs to support non-URL-compatible storage (CDO)
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.properties.editor.preview;
@@ -327,6 +329,11 @@ public class Preview extends ViewPart implements ISelectionChangedListener, IPar
setPreviewError(null);
+ if(displayEngine != null) {
+ // Dispose of the old engine before employing a new one
+ displayEngine.dispose();
+ }
+
displayEngine = new DefaultDisplayEngine();
Map<Tab, Composite> tabs = new HashMap<Tab, Composite>();
diff --git a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElement.java b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElement.java
index 08963971b18..63d01c00974 100644
--- a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElement.java
+++ b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.properties.modelelement;
@@ -65,7 +67,7 @@ import org.eclipse.papyrus.views.properties.ui.PropertyEditor;
*/
public class CustomizationModelElement extends AbstractModelElement {
- private EMFModelElement delegate;
+ protected EMFModelElement delegate;
private static Map<EClassifier, IStaticContentProvider> providers;
diff --git a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElementFactory.java b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElementFactory.java
index 63c2495ac89..9a3d00d99d1 100644
--- a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElementFactory.java
+++ b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/CustomizationModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,23 +8,40 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.properties.modelelement;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
import org.eclipse.papyrus.views.properties.modelelement.EMFModelElement;
import org.eclipse.papyrus.views.properties.modelelement.EMFModelElementFactory;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
/**
* A Factory for build {@link CustomizationModelElement}s
*
* @author Camille Letavernier
*/
-public class CustomizationModelElementFactory extends EMFModelElementFactory {
+public class CustomizationModelElementFactory extends AbstractModelElementFactory<CustomizationModelElement> {
+ private static final EMFModelElementFactory emfFactory = new EMFModelElementFactory();
+
+ @Override
+ protected CustomizationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
+ return new CustomizationModelElement((EMFModelElement)emfFactory.createFromSource(sourceElement, context));
+ }
+
@Override
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
- return new CustomizationModelElement((EMFModelElement)super.createFromSource(sourceElement, context));
+ protected void updateModelElement(CustomizationModelElement modelElement, Object newSourceElement) {
+ EObject eObject = EMFHelper.getEObject(newSourceElement);
+ if(eObject == null) {
+ throw new IllegalArgumentException("Cannot resolve EObject selection: " + newSourceElement);
+ }
+
+ AbstractEMFModelElementFactory.updateEMFModelElement(modelElement.delegate, eObject);
}
}
diff --git a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElement.java b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElement.java
index b5e124cca1e..06f1e8ebbde 100644
--- a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElement.java
+++ b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.properties.modelelement;
@@ -63,9 +65,9 @@ import org.eclipse.papyrus.views.properties.ui.WidgetAttribute;
*/
public class GenericAttributeModelElement extends AbstractModelElement {
- private EObject source;
+ protected EObject source;
- private EditingDomain domain;
+ protected EditingDomain domain;
private EStructuralFeature createIn;
diff --git a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElementFactory.java b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElementFactory.java
index 6dcb389a966..063125a3c1f 100644
--- a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElementFactory.java
+++ b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericAttributeModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.properties.modelelement;
@@ -19,21 +21,22 @@ import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.customization.properties.Activator;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
import org.eclipse.papyrus.views.properties.ui.UiFactory;
import org.eclipse.papyrus.views.properties.ui.UiPackage;
+import org.eclipse.papyrus.views.properties.ui.WidgetAttribute;
/**
* A ModelElementFactory for handling {@link WidgetAttribute} properties
*
* @author Camille Letavernier
*/
-public class GenericAttributeModelElementFactory implements ModelElementFactory {
+public class GenericAttributeModelElementFactory extends AbstractModelElementFactory<GenericAttributeModelElement> {
//Source : Group
//context : Custom:Attribute:Group
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected GenericAttributeModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the source element to an EObject"); //$NON-NLS-1$
@@ -53,4 +56,13 @@ public class GenericAttributeModelElementFactory implements ModelElementFactory
// throw new UnsupportedOperationException();
// }
+ @Override
+ protected void updateModelElement(GenericAttributeModelElement modelElement, Object newSourceElement) {
+ EObject eObject = EMFHelper.getEObject(newSourceElement);
+ if(eObject == null) {
+ throw new IllegalArgumentException("Cannot resolve EObject selection: " + newSourceElement);
+ }
+ modelElement.source = eObject;
+ modelElement.domain = EMFHelper.resolveEditingDomain(eObject);
+ }
}
diff --git a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericPropertyModelElementFactory.java b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericPropertyModelElementFactory.java
index 15df8d144f7..4745a06dc1c 100644
--- a/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericPropertyModelElementFactory.java
+++ b/plugins/customization/org.eclipse.papyrus.customization.properties/src/org/eclipse/papyrus/customization/properties/modelelement/GenericPropertyModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.properties.modelelement;
@@ -17,23 +19,24 @@ import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.customization.properties.Activator;
+import org.eclipse.papyrus.infra.constraints.ConfigProperty;
import org.eclipse.papyrus.infra.constraints.ConstraintsFactory;
import org.eclipse.papyrus.infra.constraints.ConstraintsPackage;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
/**
* A ModelElementFactory for handling {@link ConfigProperty} properties
*
* @author Camille Letavernier
*/
-public class GenericPropertyModelElementFactory implements ModelElementFactory {
+public class GenericPropertyModelElementFactory extends AbstractModelElementFactory<GenericAttributeModelElement> {
//Source : Group
//context : Custom:Attribute:Group
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected GenericAttributeModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
@@ -54,4 +57,13 @@ public class GenericPropertyModelElementFactory implements ModelElementFactory {
// throw new UnsupportedOperationException();
// }
+ @Override
+ protected void updateModelElement(GenericAttributeModelElement modelElement, Object newSourceElement) {
+ EObject eObject = EMFHelper.getEObject(newSourceElement);
+ if(eObject == null) {
+ throw new IllegalArgumentException("Cannot resolve EObject selection: " + newSourceElement);
+ }
+ modelElement.source = eObject;
+ modelElement.domain = EMFHelper.resolveEditingDomain(eObject);
+ }
}
diff --git a/plugins/customization/org.eclipse.papyrus.customization/src/org/eclipse/papyrus/customization/modelelement/CustomizationModelElementFactory.java b/plugins/customization/org.eclipse.papyrus.customization/src/org/eclipse/papyrus/customization/modelelement/CustomizationModelElementFactory.java
index 9a52966638f..5f98a4a45ed 100644
--- a/plugins/customization/org.eclipse.papyrus.customization/src/org/eclipse/papyrus/customization/modelelement/CustomizationModelElementFactory.java
+++ b/plugins/customization/org.eclipse.papyrus.customization/src/org/eclipse/papyrus/customization/modelelement/CustomizationModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.customization.modelelement;
@@ -17,14 +19,14 @@ import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.customization.Activator;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
+import org.eclipse.papyrus.views.properties.modelelement.EMFModelElement;
import org.eclipse.papyrus.views.properties.modelelement.EMFModelElementFactory;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
public class CustomizationModelElementFactory extends EMFModelElementFactory {
@Override
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ protected EMFModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
diff --git a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSModelElementFactory.java b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSModelElementFactory.java
index d71749709c0..b20a7c20261 100644
--- a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSModelElementFactory.java
+++ b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.css.properties.modelelement;
@@ -16,14 +18,15 @@ import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.infra.gmfdiag.css.properties.Activator;
+import org.eclipse.papyrus.infra.gmfdiag.properties.modelelement.CustomStyleModelElement;
+import org.eclipse.papyrus.infra.gmfdiag.properties.modelelement.CustomStyleModelElementFactory;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
-public class CSSModelElementFactory implements ModelElementFactory {
+public class CSSModelElementFactory extends CustomStyleModelElementFactory {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected CustomStyleModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View view = NotationHelper.findView(sourceElement);
if(view == null) {
Activator.log.warn("The selected element cannot be resolved to a GMF View");
diff --git a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSPreferencesModelElementFactory.java b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSPreferencesModelElementFactory.java
index d335bb1eb20..e7ee6f2327f 100644
--- a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSPreferencesModelElementFactory.java
+++ b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSPreferencesModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,18 +8,20 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.css.properties.modelelement;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
+import org.eclipse.papyrus.views.properties.modelelement.PreferencesModelElement;
import org.eclipse.papyrus.views.properties.modelelement.PreferencesModelElementFactory;
public class CSSPreferencesModelElementFactory extends PreferencesModelElementFactory {
@Override
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ protected PreferencesModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
return new CSSPreferencesModelElement(context);
}
}
diff --git a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElement.java b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElement.java
index 6d54d2890d9..5be7b852581 100644
--- a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElement.java
+++ b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014 CEA LIST.
+ * Copyright (c) 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -7,7 +7,9 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- * Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Initial API and implementation
+ * Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.css.properties.modelelement;
@@ -17,7 +19,6 @@ import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.papyrus.infra.gmfdiag.css.properties.provider.CSSStyleSheetLabelProvider;
import org.eclipse.papyrus.views.properties.modelelement.EMFModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
/**
@@ -27,7 +28,7 @@ import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
* @author gpascual
*
*/
-public class CSSThemesModelElement extends EMFModelElement implements ModelElement {
+public class CSSThemesModelElement extends EMFModelElement {
/** UI view path for theme style sheets. */
private static final String THEME_STYLESHEETS_PATH = "stylesheets"; //$NON-NLS-1$
diff --git a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElementFactory.java b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElementFactory.java
index ade28c59815..f486fb3b640 100644
--- a/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElementFactory.java
+++ b/plugins/infra/gmfdiag/css/org.eclipse.papyrus.infra.gmfdiag.css.properties/src/org/eclipse/papyrus/infra/gmfdiag/css/properties/modelelement/CSSThemesModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014 CEA LIST.
+ * Copyright (c) 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -7,7 +7,9 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- * Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Initial API and implementation
+ * Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.css.properties.modelelement;
@@ -17,8 +19,7 @@ import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
/**
@@ -28,7 +29,7 @@ import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
* @author gpascual
*
*/
-public class CSSThemesModelElementFactory implements ModelElementFactory {
+public class CSSThemesModelElementFactory extends AbstractEMFModelElementFactory<CSSThemesModelElement> {
/**
@@ -39,15 +40,8 @@ public class CSSThemesModelElementFactory implements ModelElementFactory {
super();
}
- /**
- * @see org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory#createFromSource(java.lang.Object,
- * org.eclipse.papyrus.views.properties.contexts.DataContextElement)
- *
- * @param sourceElement
- * @param context
- * @return
- */
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected CSSThemesModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/AppearanceModelElementFactory.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/AppearanceModelElementFactory.java
index 11f5520683c..de8b9efcdd8 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/AppearanceModelElementFactory.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/AppearanceModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.properties.modelelement;
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/CustomStyleModelElementFactory.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/CustomStyleModelElementFactory.java
index 87769b7b6d4..a2c8553afea 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/CustomStyleModelElementFactory.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/CustomStyleModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.properties.modelelement;
@@ -17,13 +19,13 @@ import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.infra.gmfdiag.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
-public class CustomStyleModelElementFactory implements ModelElementFactory {
+public class CustomStyleModelElementFactory extends AbstractEMFModelElementFactory<CustomStyleModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected CustomStyleModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View view = NotationHelper.findView(sourceElement);
if(view != null) {
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/DecoratedModelElementFactory.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/DecoratedModelElementFactory.java
index e549d8f8dd4..3b7b2cc602b 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/DecoratedModelElementFactory.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/DecoratedModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,25 +8,28 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.properties.modelelement;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.gmf.runtime.notation.View;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.infra.gmfdiag.common.providers.ShapeDecorator;
import org.eclipse.papyrus.infra.gmfdiag.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
/**
* A factory for handling the GMF decorated elements
*/
-public class DecoratedModelElementFactory implements ModelElementFactory {
+public class DecoratedModelElementFactory extends AbstractModelElementFactory<DecoratedModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected DecoratedModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View view = NotationHelper.findView(sourceElement);
if(view != null) {
@@ -34,11 +37,20 @@ public class DecoratedModelElementFactory implements ModelElementFactory {
if(ShapeDecorator.isDecorable(view)) {
return new DecoratedModelElement(view, domain);
}
-
+
}
Activator.log.warn("The selected element cannot be resolved to a Decorated element");
return null;
}
+ @Override
+ protected void updateModelElement(DecoratedModelElement modelElement, Object newSourceElement) {
+ View view = NotationHelper.findView(newSourceElement);
+ if((view == null) || !ShapeDecorator.isDecorable(view)) {
+ throw new IllegalArgumentException("Cannot resolve decorable View selection: " + newSourceElement);
+ }
+ modelElement.source = view;
+ modelElement.domain = EMFHelper.resolveEditingDomain(view);
+ }
}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/NotationModelElementFactory.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/NotationModelElementFactory.java
index 6a0889ccf7d..a0895d13c2d 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/NotationModelElementFactory.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/NotationModelElementFactory.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 323802
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.properties.modelelement;
@@ -21,17 +22,18 @@ import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.infra.gmfdiag.properties.Activator;
import org.eclipse.papyrus.infra.gmfdiag.properties.databinding.ObservableGradientData;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElement;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
/**
* A factory for handling the GMF Notation elements
*
* @author Camille Letavernier
*/
-public class NotationModelElementFactory implements ModelElementFactory {
-
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+public class NotationModelElementFactory extends AbstractModelElementFactory<AbstractModelElement> {
+ @Override
+ protected AbstractModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
if (sourceElement instanceof ObservableGradientData) {
ObservableGradientData gradientData = (ObservableGradientData)sourceElement;
@@ -50,4 +52,34 @@ public class NotationModelElementFactory implements ModelElementFactory {
return null;
}
+ @Override
+ protected void updateModelElement(AbstractModelElement modelElement, Object newSourceElement) {
+ if(modelElement instanceof GMFModelElement) {
+ updateModelElement((GMFModelElement)modelElement, newSourceElement);
+ } else if(modelElement instanceof GradientDataModelElement) {
+ updateModelElement((GradientDataModelElement)modelElement, newSourceElement);
+ }
+ }
+
+ void updateModelElement(GradientDataModelElement modelElement, Object newSourceElement) {
+ if(newSourceElement instanceof ObservableGradientData) {
+ ObservableGradientData ogd = (ObservableGradientData)newSourceElement;
+ modelElement.sourceElement = ogd;
+ modelElement.owner = ogd.getOwner();
+ } else if(newSourceElement instanceof GradientData) {
+ modelElement.sourceElement = (GradientData)newSourceElement;
+ modelElement.owner = null;
+ } else {
+ throw new IllegalArgumentException("Cannot resolve GradientData selection: " + newSourceElement);
+ }
+ }
+
+ void updateModelElement(GMFModelElement modelElement, Object newSourceElement) {
+ View view = NotationHelper.findView(newSourceElement);
+ if(view == null) {
+ throw new IllegalArgumentException("Cannot resolve View selection: " + newSourceElement);
+ }
+
+ AbstractEMFModelElementFactory.updateEMFModelElement(modelElement, view);
+ }
}
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulerAndGridModelElement.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulerAndGridModelElement.java
index 87f0f8cfd2b..cae3f549ea8 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulerAndGridModelElement.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulerAndGridModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -7,8 +7,9 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- *
- * CEA LIST - Initial API and implementation
+ *
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.properties.modelelement;
@@ -47,12 +48,12 @@ public class RulerAndGridModelElement extends AbstractModelElement {
/**
* the diagram for which we are editing preferences
*/
- private Diagram diagram;
+ protected Diagram diagram;
/**
* the edited preference store
*/
- private IPreferenceStore store;
+ protected IPreferenceStore store;
/**
*
diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulersAndGridModelElementFactory.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulersAndGridModelElementFactory.java
index 0709999a89e..8b7e6e1abfe 100644
--- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulersAndGridModelElementFactory.java
+++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.properties/src/org/eclipse/papyrus/infra/gmfdiag/properties/modelelement/RulersAndGridModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -7,8 +7,9 @@
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
- *
- * CEA LIST - Initial API and implementation
+ *
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.properties.modelelement;
@@ -23,26 +24,17 @@ import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.infra.gmfdiag.common.utils.DiagramEditPartsUtil;
import org.eclipse.papyrus.infra.gmfdiag.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
/**
*
* @author vl222926
* The factory used to edit Rulers and Grid properties
*/
-public class RulersAndGridModelElementFactory implements ModelElementFactory {
+public class RulersAndGridModelElementFactory extends AbstractModelElementFactory<RulerAndGridModelElement> {
- /**
- *
- * @see org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory#createFromSource(java.lang.Object,
- * org.eclipse.papyrus.views.properties.contexts.DataContextElement)
- *
- * @param sourceElement
- * @param context
- * @return
- */
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected RulerAndGridModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
if(sourceElement instanceof EditPart) {
final IPreferenceStore preferenceStore = DiagramEditPartsUtil.getDiagramWorkspacePreferenceStore((EditPart)sourceElement);
View view = NotationHelper.findView(DiagramEditPartsUtil.getDiagramEditPart((EditPart)sourceElement));
@@ -56,6 +48,14 @@ public class RulersAndGridModelElementFactory implements ModelElementFactory {
return null;
}
-
+ @Override
+ protected void updateModelElement(RulerAndGridModelElement modelElement, Object newSourceElement) {
+ if(!(newSourceElement instanceof EditPart)) {
+ throw new IllegalArgumentException("Cannot resolve EditPart selection: " + newSourceElement);
+ }
+ EditPart editPart = (EditPart)newSourceElement;
+ modelElement.store = DiagramEditPartsUtil.getDiagramWorkspacePreferenceStore(editPart);
+ modelElement.diagram = (Diagram)NotationHelper.findView(DiagramEditPartsUtil.getDiagramEditPart(editPart));
+ }
}
diff --git a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.properties/src/org/eclipse/papyrus/infra/nattable/properties/modelelement/NatTableFactory.java b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.properties/src/org/eclipse/papyrus/infra/nattable/properties/modelelement/NatTableFactory.java
index 11b51492418..0c7e6645c70 100644
--- a/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.properties/src/org/eclipse/papyrus/infra/nattable/properties/modelelement/NatTableFactory.java
+++ b/plugins/infra/nattable/org.eclipse.papyrus.infra.nattable.properties/src/org/eclipse/papyrus/infra/nattable/properties/modelelement/NatTableFactory.java
@@ -1,7 +1,6 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST and others.
*
- *
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,6 +8,7 @@
*
* Contributors:
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.infra.nattable.properties.modelelement;
@@ -20,23 +20,14 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.infra.nattable.model.nattable.Table;
import org.eclipse.papyrus.infra.nattable.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
+import org.eclipse.papyrus.views.properties.modelelement.EMFModelElement;
import org.eclipse.papyrus.views.properties.modelelement.EMFModelElementFactory;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
public class NatTableFactory extends EMFModelElementFactory {
- /**
- *
- * @see org.eclipse.papyrus.views.properties.modelelement.EMFModelElementFactory#createFromSource(java.lang.Object,
- * org.eclipse.papyrus.views.properties.contexts.DataContextElement)
- *
- * @param sourceElement
- * @param context
- * @return
- */
@Override
- public ModelElement createFromSource(final Object sourceElement, final DataContextElement context) {
+ protected EMFModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
@@ -46,7 +37,7 @@ public class NatTableFactory extends EMFModelElementFactory {
EditingDomain domain = AdapterFactoryEditingDomain.getEditingDomainFor(source);
return new NatTableModelElement((Table)source, domain);
} else {
- return super.createFromSource(sourceElement, context);
+ return super.doCreateFromSource(sourceElement, context);
}
}
}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF b/plugins/infra/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF
index 69b756e3f75..b2b0b9839a1 100644
--- a/plugins/infra/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF
@@ -13,7 +13,8 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.emf.ecore;bundle-version="2.9.0",
org.eclipse.emf.ecore.xmi;bundle-version="2.9.0",
org.eclipse.emf.edit;bundle-version="2.9.0",
- org.eclipse.core.expressions;bundle-version="3.4.500"
+ org.eclipse.core.expressions;bundle-version="3.4.500",
+ com.google.guava;bundle-version="11.0.0"
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-Version: 1.0.0.qualifier
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java
new file mode 100644
index 00000000000..6dd91f9e37b
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+
+/**
+ * An invocation handler for dynamic proxies that wrap {@link DelegatingObservable}s to implement other interfaces of those delegators' delegates,
+ * such as the papyrus {@code ICommitListener} interface from the Widgets API.
+ */
+class DelegatingInvocationHandler implements InvocationHandler {
+
+ private final IDelegatingObservable delegator;
+
+ private final Class<? extends IObservable> delegatedInterface;
+
+ private DelegatingInvocationHandler(IDelegatingObservable delegator, Class<? extends IObservable> delegatedInterface) {
+ super();
+
+ this.delegator = delegator;
+ this.delegatedInterface = delegatedInterface;
+ }
+
+ public static <T extends IObservable> T wrap(IDelegatingObservable delegator, Class<T> delegatedInterface) {
+ T result;
+
+ List<Class<?>> mixins = null;
+
+ IObservable delegate = delegator.getDelegate();
+
+ for(Class<?> next : allInterfaces(delegate.getClass())) {
+ // Already have the core observable interfaces covered
+ if(!next.isAssignableFrom(delegatedInterface)) {
+ if(mixins == null) {
+ mixins = new ArrayList<Class<?>>(1);
+ }
+ mixins.add(next);
+ }
+ }
+
+ if(mixins == null) {
+ result = delegatedInterface.cast(delegator);
+ } else {
+ // This class loader is sure to be able to see all of the interfaces implemented by the delegate.
+ // But the question is, can it see the IDelegatingObservable interface?
+ ClassLoader loader = delegator.getDelegate().getClass().getClassLoader();
+ try {
+ if(loader.loadClass(IDelegatingObservable.class.getName()) != IDelegatingObservable.class) {
+ // This loader can't see the same class. Use my loader, instead
+ loader = DelegatingInvocationHandler.class.getClassLoader();
+ }
+ } catch (Exception e) {
+ // This loader can't see the class. Use my loader, instead
+ loader = DelegatingInvocationHandler.class.getClassLoader();
+ }
+
+ result = wrap(delegator, delegatedInterface, loader, mixins.toArray(new Class<?>[mixins.size()]));
+ }
+
+ return result;
+ }
+
+ static Set<Class<?>> allInterfaces(Class<?> clazz) {
+ Set<Class<?>> result = new HashSet<Class<?>>();
+ collectAllInterfaces(clazz, result);
+ return result;
+ }
+
+ private static void collectAllInterfaces(Class<?> clazz, Collection<Class<?>> result) {
+ Class<?>[] interfaces = clazz.getInterfaces();
+ for(int i = 0; i < interfaces.length; i++) {
+ // Don't need to collect super-interfaces because they are inherited
+ result.add(interfaces[i]);
+ }
+
+ // Climb the type hierarchy to get interfaces of superclasses (which may be unrelated to direct interfaces)
+ Class<?> zuper = clazz.getSuperclass();
+ if(zuper != null) {
+ collectAllInterfaces(zuper, result);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T extends IObservable> T wrap(IDelegatingObservable delegator, Class<T> delegatedInterface, ClassLoader loader, Class<?>... mixins) {
+ T result;
+
+ if((loader == null) || (mixins.length == 0)) {
+ // Nothing to wrap
+ result = delegatedInterface.cast(delegator);
+ } else {
+ List<Class<?>> interfaces = new ArrayList<Class<?>>(mixins.length + 2);
+ interfaces.add(delegatedInterface);
+ interfaces.add(IDelegatingObservable.class);
+ interfaces.addAll(Arrays.asList(mixins));
+ InvocationHandler handler = new DelegatingInvocationHandler(delegator, delegatedInterface);
+
+ result = delegatedInterface.cast(Proxy.newProxyInstance(loader, interfaces.toArray(new Class<?>[interfaces.size()]), handler));
+ ((DelegatingObservable<T>)delegator).setRealObservable(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * The interesting case of wrapping an observable that is already one of our delegating dynamic proxies.
+ *
+ * @param proxy
+ * a dynamic proxy implementing the {@link IDelegatingObservable} interface
+ *
+ * @return another dynamic proxy of the same class, which delegates to the supplied {@code proxy}
+ *
+ * @throws Exception
+ * on failure to create a new dynamic proxy of the same kind as the delegate {@code proxy}
+ */
+ @SuppressWarnings("unchecked")
+ static <T extends IObservable> T wrapDynamicProxy(T proxy) throws Exception {
+ final DelegatingInvocationHandler proxyHandler = (DelegatingInvocationHandler)Proxy.getInvocationHandler(proxy);
+
+ // Create a new delegator of the appropriate class
+ DelegatingObservable<T> proxyDelegator = (DelegatingObservable<T>)proxyHandler.delegator;
+ DelegatingObservable<T> delegator = proxyDelegator.getClass().getDeclaredConstructor(proxyHandler.delegatedInterface).newInstance(proxy);
+
+ // Create an invocation handler for the same delegated interface as the wrapped proxy
+ DelegatingInvocationHandler handler = new DelegatingInvocationHandler(delegator, proxyHandler.delegatedInterface);
+
+ // And create a new delegating proxy of the same class
+ return (T)proxy.getClass().getDeclaredConstructor(InvocationHandler.class).newInstance(handler);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ Object result = null;
+
+ Class<?> owner = method.getDeclaringClass();
+
+ try {
+ if((owner == delegatedInterface) || (owner == IDelegatingObservable.class) || (owner == ReferenceCountedObservable.class) || (owner == Object.class) || owner.isAssignableFrom(delegatedInterface)) {
+ // Refer this to our delegate
+ result = method.invoke(delegator, args);
+ } else {
+ // Refer this to the delegator's delegate
+ result = method.invoke(delegator.getDelegate(), args);
+ }
+ } catch (InvocationTargetException e) {
+ // Don't just re-throw this because chances are it's triggered by a run-time exception (doesn't need to
+ // be declared) or by a declared exception. The ITE type is not usually declared in API signatures
+ // (in fact, it really should only be declared by the Method::invoke(...) API!)
+ throw e.getTargetException();
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java
new file mode 100644
index 00000000000..55393927999
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.lang.reflect.Proxy;
+
+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.IObserving;
+import org.eclipse.core.databinding.observable.IStaleListener;
+import org.eclipse.core.databinding.observable.ObservableEvent;
+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;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+
+/**
+ * Abstract implementation of the {@link IDelegatingObservable} protocol with factory methods for creation of delegators.
+ *
+ * @see #wrap(IObservable)
+ * @see #create(Realm, Class)
+ * @see #create(Realm, Class, ClassLoader, Class...)
+ */
+public abstract class DelegatingObservable<T extends IObservable> extends ReferenceCountedObservable.Abstract implements IDelegatingObservable {
+
+ private final Class<T> delegateType;
+
+ private T delegate;
+
+ @SuppressWarnings("unchecked")
+ private T realObservable = (T)this;
+
+ private IChangeListener forwardingChangeListener;
+
+ private IStaleListener forwardingStaleListener;
+
+ private IDisposeListener delegateDisposeListener;
+
+ DelegatingObservable(T delegate, Class<T> delegateType) {
+ super(delegate.getRealm());
+
+ this.delegateType = delegateType;
+
+ setDelegate(delegate);
+ }
+
+ DelegatingObservable(Realm realm, Class<T> delegateType) {
+ super(realm);
+
+ this.delegateType = delegateType;
+ }
+
+ /**
+ * Wraps an {@code observable} in a delegator, returning an {@link IDelegatingObservable} of the appropriate kind.
+ *
+ * @param observable
+ * an observable to wrap in a delegator
+ * @return the delegator, which will be an instance of the {@link IDelegatingObservable} interface
+ *
+ * @throws IllegalArgumentException
+ * if the {@code observable} is of a kind for which no delegator is currently implemented
+ */
+ public static IObservable wrap(IObservable observable) {
+ IObservable result;
+
+ if(Proxy.isProxyClass(observable.getClass()) && (Proxy.getInvocationHandler(observable) instanceof DelegatingInvocationHandler)) {
+ // Already have a delegator and it's a dynamic proxy. Just create another like it
+ try {
+ result = DelegatingInvocationHandler.wrapDynamicProxy(observable);
+ } catch (Exception e) {
+ // Seems unlikely as I must have created the observable in the first place
+ throw new IllegalArgumentException("observable is an invalid implementation of IDelegatingObservable", e); //$NON-NLS-1$
+ }
+ } else if(observable instanceof IObservableList) {
+ result = DelegatingObservableList.wrap((IObservableList)observable);
+ } else if(observable instanceof IObservableSet) {
+ result = DelegatingObservableSet.wrap((IObservableSet)observable);
+ } else if(observable instanceof IObservableValue) {
+ result = DelegatingObservableValue.wrap((IObservableValue)observable);
+ } else {
+ throw new IllegalArgumentException("no delegating wrapper implementation available for observable"); //$NON-NLS-1$
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a new empty delegator suitable for observables of the specified type without any other mix-in interfaces such as {@link IObserving}.
+ * Observable types must be specified by their abstract interface, and currently the following types are supported:
+ * <ul>
+ * <li>{@link IObservableValue}</li>
+ * <li>{@link IObservableList}</li>
+ * <li>{@link IObservableSet}</li>
+ * </ul>
+ *
+ * @param observableType
+ * the kind of observable that will be the new delegator's delegate
+ * @return the delegator, which will be an instance of the {@link IDelegatingObservable} interface
+ *
+ * @throws IllegalArgumentException
+ * if the {@code observable} is of a kind for which no delegator is currently implemented
+ *
+ * @see #create(Realm, Class, ClassLoader, Class...)
+ */
+ public static <T extends IObservable> T create(Realm realm, Class<T> observableType) {
+ return create(realm, observableType, null); // Class loader not needed without any mix-ins
+ }
+
+ /**
+ * Creates a new empty delegator suitable for observables of the specified type with optional mix-in interfaces such as {@link IObserving},
+ * which is implemented by detail observables in a master/detail relationship. Observable types must be specified
+ * by their abstract interface, and currently the following types are supported:
+ * <ul>
+ * <li>{@link IObservableValue}</li>
+ * <li>{@link IObservableList}</li>
+ * <li>{@link IObservableSet}</li>
+ * </ul>
+ *
+ * @param observableType
+ * the kind of observable that will be the new delegator's delegate
+ * @param loader
+ * a class loader that can see all of the {@code mixins}, if any
+ * @param mixins
+ * optional mix-in interfaces that the resulting observable should refer to its delegate. These must all have
+ * {@linkplain #registerMixinHandler handlers already registered}
+ * @return the delegator, which will be an instance of the {@link IDelegatingObservable} interface
+ *
+ * @throws IllegalArgumentException
+ * if the {@code observable} is of a kind for which no delegator is currently implemented
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends IObservable> T create(Realm realm, Class<T> observableType, ClassLoader loader, Class<?>... mixins) {
+ if(observableType == IObservableList.class) {
+ return (T)DelegatingObservableList.create(realm, loader, mixins);
+ } else if(observableType == IObservableSet.class) {
+ return (T)DelegatingObservableSet.create(realm, loader, mixins);
+ } else if(observableType == IObservableValue.class) {
+ return (T)DelegatingObservableValue.create(realm, loader, mixins);
+ } else {
+ throw new IllegalArgumentException("observableType"); //$NON-NLS-1$
+ }
+ }
+
+ public final void setDelegate(final IObservable delegate) {
+ if(isDisposed()) {
+ throw new IllegalStateException("disposed"); //$NON-NLS-1$
+ }
+
+ final T oldDelegate = this.delegate;
+
+ if(delegate != oldDelegate) {
+ final T newDelegate = (delegate == null) ? null : delegateType.cast(delegate);
+
+ if(oldDelegate != null) {
+ unhookDelegate(oldDelegate);
+
+ // Release it only after this iteration of the event loop so that UI refreshes can still access it for now
+ // in case its retain count will go to zero and it will be disposed
+ ReferenceCountedObservable.Util.autorelease(oldDelegate);
+ }
+
+ this.delegate = newDelegate;
+
+ if(newDelegate != null) {
+ ReferenceCountedObservable.Util.retain(newDelegate);
+ hookDelegate(newDelegate);
+ }
+
+ delegateChanged(oldDelegate, newDelegate);
+ }
+ }
+
+ final void clearDelegate() {
+ // Can do this even if disposed
+
+ if(delegate != null) {
+ unhookDelegate(delegate);
+
+ delegate = null;
+
+ // Let listeners know that we've changed. We cannot fire an accurate value change event
+ // because we can no longer access the old delegate's value, as it is now disposed
+ fireChange();
+ }
+ }
+
+ public final T getDelegate() {
+ return delegate;
+ }
+
+ /**
+ * Notifies of a change from one delegate to another. Subclasses overriding this must call {@code super}.
+ *
+ * @param oldDelegate
+ * the previous delegate, or {@code null} if there was none
+ * @param newDelegate
+ * the new delegate, or {@code null} if now I have none
+ */
+ protected void delegateChanged(T oldDelegate, T newDelegate) {
+ fireChange();
+ }
+
+ protected void hookDelegate(T delegate) {
+ delegate.addChangeListener(getForwardingChangeListener());
+ delegate.addStaleListener(getForwardingStaleListener());
+
+ // Don't forward dispose events because the delegate has its own lifecycle. However, when our delegate
+ // is disposed, we forget about it
+ delegate.addDisposeListener(getDelegateDisposeListener());
+ }
+
+ protected void unhookDelegate(T delegate) {
+ delegate.removeChangeListener(getForwardingChangeListener());
+ delegate.removeStaleListener(getForwardingStaleListener());
+ delegate.removeDisposeListener(getDelegateDisposeListener());
+ }
+
+ public boolean isStale() {
+ getterCalled();
+
+ return (delegate != null) && delegate.isStale();
+ }
+
+ protected void getterCalled() {
+ ObservableTracker.getterCalled(getRealObservable());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this) || ((delegate == null) ? false : delegate.equals(obj));
+ }
+
+ @Override
+ public int hashCode() {
+ return (delegate == null) ? 0 : delegate.hashCode();
+ }
+
+ /**
+ * Sets the real observable (which may be a dynamic proxy) to report as the source of events and the target of getter calls in the
+ * {@link ObservableTracker}.
+ *
+ * @param realObservable
+ * my event source
+ */
+ final void setRealObservable(T realObservable) {
+ this.realObservable = realObservable;
+ }
+
+ /**
+ * Gets the source to report for events (which may be a dynamic proxy).
+ *
+ * @return my event source
+ */
+ final T getRealObservable() {
+ return realObservable;
+ }
+
+ @Override
+ protected final void fireChange() {
+ fireEvent(new ChangeEvent(getRealObservable()));
+ }
+
+ @Override
+ protected final void fireStale() {
+ fireEvent(new StaleEvent(getRealObservable()));
+ }
+
+ public void dispose() {
+ if(!isDisposed()) {
+ if(delegate != null) {
+ unhookDelegate(delegate);
+
+ // Release it only after this iteration of the event loop so that UI refreshes can still access it for now
+ // in case its retain count will go to zero and it will be disposed
+ ReferenceCountedObservable.Util.autorelease(delegate);
+ delegate = null;
+ }
+ super.dispose();
+ }
+ }
+
+ @Override
+ protected void fireEvent(ObservableEvent event) {
+ // ensure the correct source for events fired by the superclass
+ if((event instanceof DisposeEvent) && (event.getSource() != getRealObservable())) {
+ event = new DisposeEvent(getRealObservable());
+ }
+
+ super.fireEvent(event);
+ }
+
+ private IChangeListener getForwardingChangeListener() {
+ if(forwardingChangeListener == null) {
+ forwardingChangeListener = new IChangeListener() {
+
+ public void handleChange(ChangeEvent event) {
+ DelegatingObservable.this.fireChange();
+ }
+ };
+ }
+
+ return forwardingChangeListener;
+ }
+
+ private IStaleListener getForwardingStaleListener() {
+ if(forwardingStaleListener == null) {
+ forwardingStaleListener = new IStaleListener() {
+
+ public void handleStale(StaleEvent staleEvent) {
+ DelegatingObservable.this.fireStale();
+ }
+ };
+ }
+
+ return forwardingStaleListener;
+ }
+
+ private IDisposeListener getDelegateDisposeListener() {
+ if(delegateDisposeListener == null) {
+ delegateDisposeListener = new IDisposeListener() {
+
+ public void handleDispose(DisposeEvent event) {
+ if(event.getObservable() == getDelegate()) {
+ clearDelegate();
+ }
+ }
+ };
+ }
+
+ return delegateDisposeListener;
+ }
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java
new file mode 100644
index 00000000000..f4597404f19
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+
+
+/**
+ * This is the DelegatingObservableCollection type. Enjoy.
+ */
+public abstract class DelegatingObservableCollection<T extends IObservableCollection> extends DelegatingObservable<T> implements IObservableCollection {
+
+ private static final Object[] EMPTY_ARRAY = {};
+
+ DelegatingObservableCollection(T delegate, Class<T> delegateType) {
+ super(delegate, delegateType);
+ }
+
+ DelegatingObservableCollection(Realm realm, Class<T> delegateType) {
+ super(realm, delegateType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ getterCalled();
+
+ return super.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ getterCalled();
+
+ return super.hashCode();
+ }
+
+ public int size() {
+ getterCalled();
+
+ return (getDelegate() == null) ? 0 : getDelegate().size();
+ }
+
+ public boolean isEmpty() {
+ getterCalled();
+
+ return (getDelegate() == null) ? true : getDelegate().isEmpty();
+ }
+
+ public boolean contains(Object o) {
+ getterCalled();
+
+ return (getDelegate() == null) ? false : getDelegate().contains(o);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public Iterator iterator() {
+ getterCalled();
+
+ return (getDelegate() == null) ? Collections.EMPTY_LIST.iterator() : getDelegate().iterator();
+ }
+
+ public Object[] toArray() {
+ getterCalled();
+
+ return (getDelegate() == null) ? EMPTY_ARRAY : getDelegate().toArray();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object[] toArray(Object[] a) {
+ getterCalled();
+
+ return (getDelegate() == null) ? Collections.EMPTY_LIST.toArray(a) : getDelegate().toArray(a);
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean add(Object e) {
+ return (getDelegate() == null) ? false : getDelegate().add(e);
+ }
+
+ public boolean remove(Object o) {
+ return (getDelegate() == null) ? false : getDelegate().remove(o);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public boolean containsAll(Collection c) {
+ getterCalled();
+
+ return (getDelegate() == null) ? false : getDelegate().containsAll(c);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public boolean addAll(Collection c) {
+ return (getDelegate() == null) ? false : getDelegate().addAll(c);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public boolean removeAll(Collection c) {
+ return (getDelegate() == null) ? false : getDelegate().removeAll(c);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public boolean retainAll(Collection c) {
+ return (getDelegate() == null) ? false : getDelegate().retainAll(c);
+ }
+
+ public void clear() {
+ if(getDelegate() != null) {
+ getDelegate().clear();
+ }
+ }
+
+ public Object getElementType() {
+ return (getDelegate() == null) ? null : getDelegate().getElementType();
+ }
+
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java
new file mode 100644
index 00000000000..2c7e7807aaa
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+
+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.ListDiff;
+
+
+/**
+ * This is the DelegatingObservableList type. Enjoy.
+ */
+public class DelegatingObservableList extends DelegatingObservableCollection<IObservableList> implements IObservableList {
+
+ private static final Object LIST_EVENT_TYPE = new Object();
+
+ private IListChangeListener forwardingListChangeListener;
+
+ DelegatingObservableList(IObservableList delegate) {
+ super(delegate, IObservableList.class);
+ }
+
+ DelegatingObservableList(Realm realm) {
+ super(realm, IObservableList.class);
+ }
+
+ public static IObservableList wrap(IObservableList observable) {
+ IObservableList result;
+
+ if(observable instanceof IDelegatingObservable) {
+ // Already have a delegator. Just create another like it
+ try {
+ result = (IObservableList)observable.getClass().getDeclaredConstructor(IObservableList.class).newInstance(observable);
+ } catch (Exception e) {
+ // Seems unlikely as I must have created the observable in the first place
+ throw new IllegalArgumentException("observable is an invalid implementation of IDelegatingObservable", e); //$NON-NLS-1$
+ }
+ } else {
+ result = DelegatingInvocationHandler.wrap(new DelegatingObservableList(observable), IObservableList.class);
+ }
+
+ return result;
+ }
+
+ public static IObservableList create(Realm realm, ClassLoader loader, Class<?>... mixins) {
+ return DelegatingInvocationHandler.wrap(new DelegatingObservableList(realm), IObservableList.class, loader, mixins);
+ }
+
+ public void addListChangeListener(IListChangeListener listener) {
+ addListener(LIST_EVENT_TYPE, listener);
+ }
+
+ public void removeListChangeListener(IListChangeListener listener) {
+ removeListener(LIST_EVENT_TYPE, listener);
+ }
+
+ @Override
+ protected void hookDelegate(IObservableList delegate) {
+ super.hookDelegate(delegate);
+ delegate.addListChangeListener(getForwardingListChangeListener());
+ }
+
+ @Override
+ protected void unhookDelegate(IObservableList delegate) {
+ delegate.removeListChangeListener(getForwardingListChangeListener());
+ super.unhookDelegate(delegate);
+ }
+
+ @Override
+ protected void delegateChanged(IObservableList oldDelegate, IObservableList newDelegate) {
+ super.delegateChanged(oldDelegate, newDelegate);
+
+ List<?> oldList = ((oldDelegate == null) || oldDelegate.isDisposed()) ? Collections.EMPTY_LIST : oldDelegate;
+ List<?> newList = (newDelegate == null) ? Collections.EMPTY_LIST : newDelegate;
+
+ fireEvent(new MyListChangeEvent(Diffs.computeListDiff(oldList, newList)));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void add(int index, Object element) {
+ if(getDelegate() == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ getDelegate().add(index, element);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public boolean addAll(int index, Collection c) {
+ if(getDelegate() == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return getDelegate().addAll(index, c);
+ }
+
+ public Object get(int index) {
+ getterCalled();
+
+ if(getDelegate() == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return getDelegate().get(index);
+ }
+
+ public Object set(int index, Object element) {
+ if(getDelegate() == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return getDelegate().set(index, element);
+ }
+
+ public Object move(int oldIndex, int newIndex) {
+ if(getDelegate() == null) {
+ throw new IndexOutOfBoundsException();
+ }
+ return getDelegate().move(oldIndex, newIndex);
+ }
+
+ public Object remove(int index) {
+ return (getDelegate() == null) ? Collections.EMPTY_LIST.remove(index) : getDelegate().remove(index);
+ }
+
+ public int indexOf(Object o) {
+ getterCalled();
+
+ return (getDelegate() == null) ? -1 : getDelegate().indexOf(o);
+ }
+
+ public int lastIndexOf(Object o) {
+ getterCalled();
+
+ return (getDelegate() == null) ? -1 : getDelegate().lastIndexOf(o);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public ListIterator listIterator() {
+ getterCalled();
+
+ return (getDelegate() == null) ? Collections.EMPTY_LIST.listIterator() : getDelegate().listIterator();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public ListIterator listIterator(int index) {
+ getterCalled();
+
+ return (getDelegate() == null) ? Collections.EMPTY_LIST.listIterator(index) : getDelegate().listIterator(index);
+ }
+
+ @SuppressWarnings("rawtypes")
+ public List subList(int fromIndex, int toIndex) {
+ getterCalled();
+
+ return (getDelegate() == null) ? Collections.EMPTY_LIST.subList(fromIndex, toIndex) : getDelegate().subList(fromIndex, toIndex);
+ }
+
+ private IListChangeListener getForwardingListChangeListener() {
+ if(forwardingListChangeListener == null) {
+ forwardingListChangeListener = new IListChangeListener() {
+
+ public void handleListChange(ListChangeEvent event) {
+ ListChangeEvent myEvent = new MyListChangeEvent(event.diff);
+ fireEvent(myEvent);
+ }
+ };
+ }
+
+ return forwardingListChangeListener;
+ }
+
+ //
+ // Nested types
+ //
+
+ class MyListChangeEvent extends ListChangeEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ MyListChangeEvent(ListDiff diff) {
+ super(getRealObservable(), diff);
+ }
+
+ @Override
+ protected Object getListenerType() {
+ // We implement our own listener type because the type from the core framework is not accessible
+ return LIST_EVENT_TYPE;
+ }
+ }
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java
new file mode 100644
index 00000000000..4111577f027
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.util.Collections;
+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.ISetChangeListener;
+import org.eclipse.core.databinding.observable.set.SetChangeEvent;
+import org.eclipse.core.databinding.observable.set.SetDiff;
+
+
+/**
+ * This is the DelegatingObservableSet type. Enjoy.
+ */
+public class DelegatingObservableSet extends DelegatingObservableCollection<IObservableSet> implements IObservableSet {
+
+ private static final Object SET_EVENT_TYPE = new Object();
+
+ private ISetChangeListener forwardingSetChangeListener;
+
+ DelegatingObservableSet(IObservableSet delegate) {
+ super(delegate, IObservableSet.class);
+ }
+
+ DelegatingObservableSet(Realm realm) {
+ super(realm, IObservableSet.class);
+ }
+
+ public static IObservableSet wrap(IObservableSet observable) {
+ IObservableSet result;
+
+ if(observable instanceof IDelegatingObservable) {
+ // Already have a delegator. Just create another like it
+ try {
+ result = (IObservableSet)observable.getClass().getDeclaredConstructor(IObservableSet.class).newInstance(observable);
+ } catch (Exception e) {
+ // Seems unlikely as I must have created the observable in the first place
+ throw new IllegalArgumentException("observable is an invalid implementation of IDelegatingObservable", e); //$NON-NLS-1$
+ }
+ } else {
+ result = DelegatingInvocationHandler.wrap(new DelegatingObservableSet(observable), IObservableSet.class);
+ }
+
+ return result;
+ }
+
+ public static IObservableSet create(Realm realm, ClassLoader loader, Class<?>... mixins) {
+ return DelegatingInvocationHandler.wrap(new DelegatingObservableSet(realm), IObservableSet.class, loader, mixins);
+ }
+
+ public void addSetChangeListener(ISetChangeListener listener) {
+ addListener(SET_EVENT_TYPE, listener);
+ }
+
+ public void removeSetChangeListener(ISetChangeListener listener) {
+ removeListener(SET_EVENT_TYPE, listener);
+ }
+
+ @Override
+ protected void hookDelegate(IObservableSet delegate) {
+ super.hookDelegate(delegate);
+ delegate.addSetChangeListener(getForwardingSetChangeListener());
+ }
+
+ @Override
+ protected void unhookDelegate(IObservableSet delegate) {
+ delegate.removeSetChangeListener(getForwardingSetChangeListener());
+ super.unhookDelegate(delegate);
+ }
+
+ @Override
+ protected void delegateChanged(IObservableSet oldDelegate, IObservableSet newDelegate) {
+ super.delegateChanged(oldDelegate, newDelegate);
+
+ Set<?> oldSet = ((oldDelegate == null) || oldDelegate.isDisposed()) ? Collections.EMPTY_SET : oldDelegate;
+ Set<?> newSet = (newDelegate == null) ? Collections.EMPTY_SET : newDelegate;
+
+ fireEvent(new MySetChangeEvent(Diffs.computeSetDiff(oldSet, newSet)));
+ }
+
+ private ISetChangeListener getForwardingSetChangeListener() {
+ if(forwardingSetChangeListener == null) {
+ forwardingSetChangeListener = new ISetChangeListener() {
+
+ public void handleSetChange(SetChangeEvent event) {
+ SetChangeEvent myEvent = new MySetChangeEvent(event.diff);
+ fireEvent(myEvent);
+ }
+ };
+ }
+
+ return forwardingSetChangeListener;
+ }
+
+ //
+ // Nested types
+ //
+
+ class MySetChangeEvent extends SetChangeEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ MySetChangeEvent(SetDiff diff) {
+ super(getRealObservable(), diff);
+ }
+
+ @Override
+ protected Object getListenerType() {
+ // We implement our own listener type because the type from the core framework is not accessible
+ return SET_EVENT_TYPE;
+ }
+ }
+
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java
new file mode 100644
index 00000000000..6c75786a1aa
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+
+
+/**
+ * This is the DelegatingObservableValue type. Enjoy.
+ */
+public class DelegatingObservableValue extends DelegatingObservable<IObservableValue> implements IObservableValue {
+
+ private static final Object VALUE_EVENT_TYPE = new Object();
+
+ private IValueChangeListener forwardingValueChangeListener;
+
+ DelegatingObservableValue(IObservableValue delegate) {
+ super(delegate, IObservableValue.class);
+ }
+
+ DelegatingObservableValue(Realm realm) {
+ super(realm, IObservableValue.class);
+ }
+
+ public static IObservableValue wrap(IObservableValue observable) {
+ IObservableValue result;
+
+ if(observable instanceof IDelegatingObservable) {
+ // Already have a delegator. Just create another like it
+ try {
+ result = (IObservableValue)observable.getClass().getDeclaredConstructor(IObservableValue.class).newInstance(observable);
+ } catch (Exception e) {
+ // Seems unlikely as I must have created the observable in the first place
+ throw new IllegalArgumentException("observable is an invalid implementation of IDelegatingObservable", e); //$NON-NLS-1$
+ }
+ } else {
+ result = DelegatingInvocationHandler.wrap(new DelegatingObservableValue(observable), IObservableValue.class);
+ }
+
+ return result;
+ }
+
+ public static IObservableValue create(Realm realm, ClassLoader loader, Class<?>... mixins) {
+ return DelegatingInvocationHandler.wrap(new DelegatingObservableValue(realm), IObservableValue.class, loader, mixins);
+ }
+
+ public void addValueChangeListener(IValueChangeListener listener) {
+ addListener(VALUE_EVENT_TYPE, listener);
+ }
+
+ public void removeValueChangeListener(IValueChangeListener listener) {
+ removeListener(VALUE_EVENT_TYPE, listener);
+ }
+
+ @Override
+ protected void hookDelegate(IObservableValue delegate) {
+ super.hookDelegate(delegate);
+ delegate.addValueChangeListener(getForwardingValueChangeListener());
+ }
+
+ @Override
+ protected void unhookDelegate(IObservableValue delegate) {
+ delegate.removeValueChangeListener(getForwardingValueChangeListener());
+ super.unhookDelegate(delegate);
+ }
+
+ @Override
+ protected void delegateChanged(IObservableValue oldDelegate, IObservableValue newDelegate) {
+ super.delegateChanged(oldDelegate, newDelegate);
+
+ Object oldValue = ((oldDelegate == null) || oldDelegate.isDisposed()) ? null : oldDelegate.getValue();
+ Object newValue = (newDelegate == null) ? null : newDelegate.getValue();
+
+ fireEvent(new MyValueChangeEvent(Diffs.createValueDiff(oldValue, newValue)));
+ }
+
+ public Object getValueType() {
+ return (getDelegate() == null) ? Void.class : getDelegate().getValueType();
+ }
+
+ public Object getValue() {
+ getterCalled();
+
+ return (getDelegate() == null) ? null : getDelegate().getValue();
+ }
+
+ public void setValue(Object value) {
+ if(getDelegate() != null) {
+ getDelegate().setValue(value);
+ }
+ }
+
+ private IValueChangeListener getForwardingValueChangeListener() {
+ if(forwardingValueChangeListener == null) {
+ forwardingValueChangeListener = new IValueChangeListener() {
+
+ public void handleValueChange(ValueChangeEvent event) {
+ ValueChangeEvent myEvent = new MyValueChangeEvent(event.diff);
+ fireEvent(myEvent);
+ }
+ };
+ }
+
+ return forwardingValueChangeListener;
+ }
+
+ //
+ // Nested types
+ //
+
+ class MyValueChangeEvent extends ValueChangeEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ MyValueChangeEvent(ValueDiff diff) {
+ super(getRealObservable(), diff);
+ }
+
+ @Override
+ protected Object getListenerType() {
+ // We implement our own listener type because the type from the core framework is not accessible
+ return VALUE_EVENT_TYPE;
+ }
+ }
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java
new file mode 100644
index 00000000000..2fa0e07bc86
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+
+
+/**
+ * <p>
+ * An {@linkplain IObservable observable} that delegates its function (including notification of state changes) to a wrapped instance. The delegate
+ * may be {@linkplain #setDelegate(IObservable) replaced} at any time. Thus, the lifecycle of a delegator is independent of its delegate and
+ * {@link #dispose() disposing} a delegator only disposes it, not its delegate if it has one at the time.
+ * </p>
+ * <p>
+ * Delegating observables may be created via factory methods provided by the {@link DelegatingObservable} class.
+ * </p>
+ *
+ * @see DelegatingObservable
+ */
+public interface IDelegatingObservable extends IObservable, ReferenceCountedObservable {
+
+ /**
+ * Assigns me a new delegate, or at least forgets my delegate if {@code null}.
+ *
+ * @param delegate
+ * my delegate (may be {@code null})
+ */
+ void setDelegate(IObservable delegate);
+
+ /**
+ * Obtains my current delegate, if any.
+ *
+ * @return my delegate, or {@code null} if I have none
+ */
+ IObservable getDelegate();
+
+ /**
+ * Disposes of me and my own resources only. In particular, if I have a {@linkplain #getDelegate() delegate}, it is <em>not</em> disposed, but
+ * must be disposed independently.
+ */
+ void dispose();
+}
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java
index bd8dadf1e11..a326d80dab1 100644
--- a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.tools.databinding;
@@ -18,7 +20,6 @@ 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;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.ValueDiff;
@@ -32,7 +33,7 @@ import org.eclipse.core.databinding.observable.value.ValueDiff;
* All sub-elements will be edited at the same time, with the same value.
*/
//TODO : Add listeners on sub-observables, and remove them on dispose
-public class MultipleObservableValue extends AbstractObservableValue implements AggregatedObservable, IChangeListener {
+public class MultipleObservableValue extends ReferenceCountedObservable.Value implements AggregatedObservable, IChangeListener {
/**
*
@@ -98,6 +99,7 @@ public class MultipleObservableValue extends AbstractObservableValue implements
public AggregatedObservable aggregate(IObservable observable) {
if(observable instanceof IObservableValue) {
+ ReferenceCountedObservable.Util.retain(observable);
observableValues.add((IObservableValue)observable);
observable.addChangeListener(this);
return this;
@@ -128,8 +130,12 @@ public class MultipleObservableValue extends AbstractObservableValue implements
super.dispose();
for(IObservableValue observable : observableValues) {
observable.removeChangeListener(this);
- observable.dispose();
+
+ // I don't own my observables, so I just release them
+ ReferenceCountedObservable.Util.release(observable);
}
+
+ observableValues.clear();
}
/**
diff --git a/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
new file mode 100644
index 00000000000..41a9ebf5ee9
--- /dev/null
+++ b/plugins/infra/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+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.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.MapMaker;
+
+
+
+/**
+ * A mix-in interface for {@link IObservable}s that support reference counting as a means to automatically dispose of them while ensuring that
+ * they may be freely shared without any client finding itself interacting with a disposed observable.
+ */
+public interface ReferenceCountedObservable extends IObservable {
+
+ /**
+ * Retains me for use by the caller, which should be sure to {@linkplain #release() release me} when I am no longer needed.
+ * As long as I have been retained by at least one client, I shall not {@linkplain IObservable#dispose() dispose} myself.
+ *
+ * @see #release()
+ */
+ void retain();
+
+ /**
+ * Releases me, indicating that I am no longer being used by the caller. When my {@linkplain #retain() retain count} reaches zero,
+ * I automatically {@linkplain IObservable#dispose() dispose} myself, because no client needs me.
+ *
+ * @see #retain()
+ * @see #autorelease()
+ * @see IObservable#dispose()
+ */
+ void release();
+
+ /**
+ * Automatically releases me some time after the current iteration of the UI event loop. This is useful when it is
+ * expected that the UI will still need to be able to access the observable for refreshes while processing the
+ * current event.
+ *
+ * @see #release()
+ */
+ void autorelease();
+
+ /**
+ * A composable assistant to implement the {@link ReferenceCountedObservable} protocol by delegating of its API.
+ */
+ class Support {
+
+ private final IObservable observable;
+
+ private final AtomicInteger refCount = new AtomicInteger();
+
+ /**
+ * Creates a new instance to support reference counting on behalf of an {@code observable}.
+ *
+ * @param observable
+ * the observable for which to provide reference counting
+ */
+ public Support(IObservable observable) {
+ this.observable = observable;
+ }
+
+ public void retain() {
+ refCount.incrementAndGet();
+ }
+
+ public void release() {
+ if((refCount.decrementAndGet() <= 0) && !observable.isDisposed()) {
+ observable.dispose();
+ }
+ }
+
+ public void autorelease() {
+ AutoReleasePool.get(observable.getRealm()).add(observable);
+ }
+ }
+
+ /**
+ * A convenient superclass for reference-counted observables that don't need any other more specific superclass.
+ */
+ abstract class Abstract extends AbstractObservable implements ReferenceCountedObservable {
+
+ private final Support refCount = new Support(this);
+
+ public Abstract(Realm realm) {
+ super(realm);
+ }
+
+ public void retain() {
+ refCount.retain();
+ }
+
+ public void release() {
+ refCount.release();
+ }
+
+ public void autorelease() {
+ refCount.autorelease();
+ }
+ }
+
+ /**
+ * A convenient superclass for reference-counted observable values that don't need any other more specific superclass.
+ */
+ abstract class Value extends AbstractObservableValue implements ReferenceCountedObservable {
+
+ private final Support refCount = new Support(this);
+
+ public Value() {
+ super();
+ }
+
+ public Value(Realm realm) {
+ super(realm);
+ }
+
+ public void retain() {
+ refCount.retain();
+ }
+
+ public void release() {
+ refCount.release();
+ }
+
+ public void autorelease() {
+ refCount.autorelease();
+ }
+ }
+
+ /**
+ * A pool of {@link IObservable}s to be released automatically after the completion of the current iteration
+ * of the UI event loop (or whatever determines the asynchronous execution of tasks in a given {@link Realm}).
+ */
+ final class AutoReleasePool {
+
+ private static final ConcurrentMap<Realm, AutoReleasePool> pools = new MapMaker().concurrencyLevel(1).makeMap();
+
+ private final Realm realm;
+
+ private Collection<IObservable> pool = Lists.newArrayList();
+
+ private AutoReleasePool(Realm realm) {
+ this.realm = realm;
+
+ realm.asyncExec(new ReleaseRunnable());
+ }
+
+ public static AutoReleasePool get(Realm realm) {
+ AutoReleasePool result = pools.get(realm);
+ if(result == null) {
+ result = new AutoReleasePool(realm);
+
+ // Double-check
+ AutoReleasePool oops = pools.putIfAbsent(realm, result);
+ if(oops != null) {
+ result = oops;
+ }
+ }
+
+ return result;
+ }
+
+ public synchronized void add(IObservable observable) {
+ if(pool == null) {
+ pool = Lists.newArrayList();
+ }
+
+ pool.add(observable);
+ }
+
+ public void release() {
+ pools.remove(realm);
+
+ for(;;) {
+ Iterable<IObservable> toDrain;
+
+ synchronized(this) {
+ toDrain = pool;
+ pool = null;
+ }
+
+ if(toDrain != null) {
+ // Drain this pool
+ for(IObservable next : toDrain) {
+ Util.release(next);
+ }
+ } else {
+ // Done. No more pools to drain
+ break;
+ }
+ }
+ }
+
+ private final class ReleaseRunnable implements Runnable {
+
+ public void run() {
+ release();
+ }
+ }
+ }
+
+ /**
+ * Utility APIs for working with reference-counted observables. In particular, this provides external reference-counting
+ * for observables that don't implement it internally via the {@link ReferenceCountedObservable} protocol.
+ */
+ final class Util {
+
+ // Use the Guava weak map because that uses object identity for comparisons, which is critical
+ // to avoid using equals() which will often fail on an assertion violation for accessing a
+ // getter of a disposed observable
+ private static final Map<IObservable, WeakRefCount> adapters = new MapMaker().concurrencyLevel(1).weakKeys().makeMap();
+
+ private Util() {
+ super();
+ }
+
+ /**
+ * Provides a unified interface to retaining observables, delegating to the {@link ReferenceCountedObservable} protocol
+ * for observables that implement it, otherwise providing an external reference-count (which is GC-safe).
+ *
+ * @param observable
+ * an observable to retain
+ *
+ * @return the same {@code observable} (useful for call chaining)
+ *
+ * @see ReferenceCountedObservable#retain()
+ */
+ public static <T extends IObservable> T retain(T observable) {
+ if(observable instanceof ReferenceCountedObservable) {
+ ((ReferenceCountedObservable)observable).retain();
+ } else if(!observable.isDisposed()) { // Don't bother counting if already disposed
+ WeakRefCount adapter = adapt(observable, true);
+ adapter.retain();
+ }
+
+ return observable;
+ }
+
+ /**
+ * Provides a unified interface to releasing observables, delegating to the {@link ReferenceCountedObservable} protocol
+ * for observables that implement it, otherwise providing an external reference-count (which is GC-safe). Note that
+ * for externally reference-counted observables, they are automatically disposed as usual when the retain count drops
+ * to zero, just as though they implemented reference counting internally.
+ *
+ * @param observable
+ * an observable to release. If its retain count is zero as a result (whether intrinsic or extrinsic), it will be disposed
+ *
+ * @return the same {@code observable} (useful for call chaining)
+ *
+ * @see ReferenceCountedObservable#release()
+ */
+ public static <T extends IObservable> T release(T observable) {
+ if(observable instanceof ReferenceCountedObservable) {
+ ((ReferenceCountedObservable)observable).release();
+ } else if(!observable.isDisposed()) { // Don't bother counting if already disposed
+ WeakRefCount adapter = adapt(observable, false);
+
+ // There won't be an adapter if there was no prior retain (of course) or if it was already disposed
+ if(adapter != null) {
+ adapter.release();
+ }
+ }
+
+ return observable;
+ }
+
+ /**
+ * Provides a unified interface to auto-releasing observables, delegating to the {@link ReferenceCountedObservable} protocol
+ * for observables that implement it, otherwise providing an external reference-count (which is GC-safe). Note that
+ * for externally reference-counted observables, they are automatically disposed as usual when the retain count drops
+ * to zero, just as though they implemented reference counting internally.
+ *
+ * @param observable
+ * an observable to release. If its retain count is zero as a result (whether intrinsic or extrinsic), it will be disposed
+ *
+ * @return the same {@code observable} (useful for call chaining)
+ *
+ * @see ReferenceCountedObservable#autorelease()
+ */
+ public static <T extends IObservable> T autorelease(T observable) {
+ if(observable instanceof ReferenceCountedObservable) {
+ ((ReferenceCountedObservable)observable).autorelease();
+ } else if(!observable.isDisposed()) { // Don't bother counting if already disposed
+ WeakRefCount adapter = adapt(observable, false);
+
+ // There won't be an adapter if there was no prior retain (of course) or if it was already disposed
+ if(adapter != null) {
+ adapter.autorelease();
+ }
+ }
+
+ return observable;
+ }
+
+ private static WeakRefCount adapt(IObservable observable, boolean create) {
+ WeakRefCount result = adapters.get(observable);
+
+ if((result == null) && create) {
+ result = new WeakRefCount(observable);
+ adapters.put(observable, result);
+ }
+
+ return result;
+ }
+
+ private static final class WeakRefCount extends WeakReference<IObservable> implements IDisposeListener {
+
+ private final AtomicInteger refCount = new AtomicInteger();
+
+ WeakRefCount(IObservable observable) {
+ super(observable);
+
+ observable.addDisposeListener(this);
+ }
+
+ public void retain() {
+ refCount.incrementAndGet();
+ }
+
+ public void release() {
+ if(refCount.decrementAndGet() <= 0) {
+ IObservable observable = get();
+
+ if(observable != null) {
+ if(!observable.isDisposed()) {
+ observable.dispose();
+ }
+
+ clear();
+ }
+ }
+ }
+
+ public void autorelease() {
+ IObservable observable = get();
+
+ // If it's null, then it's already disposed, so auto-release is meaningless
+ if(observable != null) {
+ AutoReleasePool.get(observable.getRealm()).add(observable);
+ }
+ }
+
+ public void handleDispose(DisposeEvent event) {
+ if(event.getObservable() == get()) {
+ clear();
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/infra/widget/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/AbstractValueEditor.java b/plugins/infra/widget/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/AbstractValueEditor.java
index f99ed5ba9a9..f7249f3146c 100644
--- a/plugins/infra/widget/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/AbstractValueEditor.java
+++ b/plugins/infra/widget/org.eclipse.papyrus.infra.widgets/src/org/eclipse/papyrus/infra/widgets/editors/AbstractValueEditor.java
@@ -11,6 +11,7 @@
* Thibault Le Ouay t.leouay@sherpa-eng.com - Add binding implementation
* Christian W. Damus (CEA) - bug 402525
* Mickaël ADAM (ALL4TEC) mickael.adam@all4tec.net - bug 435415
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.infra.widgets.editors;
@@ -217,13 +218,19 @@ public abstract class AbstractValueEditor extends AbstractEditor {
@Override
public void handleValueChange(ValueChangeEvent event) {
- //Check if the widget is disposed before isReadOnly() to avoid NPE
- if(!AbstractValueEditor.this.isDisposed() && !isReadOnly()) { //Bug 434787 : Shouldn't not execute the timer thread if the widget is disposed
- IStatus status = (IStatus)binding.getValidationStatus().getValue(); //Bug 435415 : Update the status only if the widget isn't disposed
- updateStatus(status);
- changeColorField();
+ // Don't handle validation changes if we don't have a validator, because then it could only be green and it isn't useful.
+ // Also, if we're showing in a dialog, then our widget may have been disposed already if we're validating a change applied
+ // by hitting the OK button
+ if((modelValidator) != null) {
+ //Check if the widget is disposed before isReadOnly() to avoid NPE
+ if(!AbstractValueEditor.this.isDisposed() && !isReadOnly()) { //Bug 434787 : Shouldn't not execute the timer thread if the widget is disposed
+ IStatus status = (IStatus)binding.getValidationStatus().getValue(); //Bug 435415 : Update the status only if the widget isn't disposed
+ updateStatus(status);
+ changeColorField();
+ }
}
}
+
});
}
@@ -248,11 +255,9 @@ public abstract class AbstractValueEditor extends AbstractEditor {
* @param modelValidator
*/
public void setModelValidator(IValidator targetToModelValidator) {
- if(targetToModelValidator != null) {
this.modelValidator = targetToModelValidator;
targetToModelStrategy.setBeforeSetValidator(targetToModelValidator);
modelToTargetStrategy.setAfterGetValidator(targetToModelValidator);
- }
}
/**
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/BehaviorDisplayHelper.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/BehaviorDisplayHelper.java
index 5e35abe5b4c..91063f64d34 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/BehaviorDisplayHelper.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/BehaviorDisplayHelper.java
@@ -1,7 +1,6 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA
+ * Copyright (c) 2013, 2014 CEA and others.
*
- *
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,6 +8,7 @@
*
* Contributors:
* Soyatec - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
@@ -34,27 +34,17 @@ import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElementFactory;
/**
* @author Jin Liu (jin.liu@soyatec.com)
*/
-public class BehaviorDisplayHelper implements ModelElementFactory {
+public class BehaviorDisplayHelper extends AnnotationModelElementFactory {
private static final String DISPLAY_BEHAVIOR = "displayBehavior";
- /**
- * @see org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory#createFromSource(java.lang.Object,
- * org.eclipse.papyrus.views.properties.contexts.DataContextElement)
- *
- * @param sourceElement
- * @param context
- * @return
- */
-
- public ModelElement createFromSource(Object sourceElement,
- DataContextElement context) {
+ @Override
+ protected AnnotationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View source = NotationHelper.findView(sourceElement);
if (source == null) {
Activator.log
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/GateModelElementFactory.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/GateModelElementFactory.java
index 35db9fe7361..0c9f1c21a43 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/GateModelElementFactory.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/GateModelElementFactory.java
@@ -1,6 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA
- *
+ * Copyright (c) 2013, 2014 CEA and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,6 +8,7 @@
*
* Contributors:
* Soyatec - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
@@ -27,25 +27,17 @@ import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElementFactory;
/**
* @author Jin Liu (jin.liu@soyatec.com)
*/
-public class GateModelElementFactory implements ModelElementFactory {
+public class GateModelElementFactory extends AnnotationModelElementFactory {
private static final String GATE_SHOW_NAME = "showName";
- /**
- * @see org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory#createFromSource(java.lang.Object,
- * org.eclipse.papyrus.views.properties.contexts.DataContextElement)
- *
- * @param sourceElement
- * @param context
- * @return
- */
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected AnnotationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View source = NotationHelper.findView(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/InteractionOperandModelElementFactory.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/InteractionOperandModelElementFactory.java
index 4d2e6dda83a..c5b9df45aac 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/InteractionOperandModelElementFactory.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/InteractionOperandModelElementFactory.java
@@ -1,7 +1,6 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA
- *
- *
+ * Copyright (c) 2010, 2014 CEA and others.
+ *
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -9,6 +8,8 @@
*
* Contributors:
* Soyatec - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
@@ -29,8 +30,7 @@ import org.eclipse.papyrus.uml.diagram.sequence.preferences.CustomInteractionOpe
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElementFactory;
/**
* Add the possibility to display or hide the guard of Operand
@@ -39,19 +39,12 @@ import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
*
* @author Jin Liu (jin.liu@soyatec.com)
*/
-public class InteractionOperandModelElementFactory implements ModelElementFactory {
+public class InteractionOperandModelElementFactory extends AnnotationModelElementFactory {
public static final String GUARD_VISIBILITY_KEY = "guard.visibility";
- /**
- * @see org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory#createFromSource(java.lang.Object,
- * org.eclipse.papyrus.views.properties.contexts.DataContextElement)
- *
- * @param sourceElement
- * @param context
- * @return
- */
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected AnnotationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View source = NotationHelper.findView(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/LinkRouteModelElementFactory.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/LinkRouteModelElementFactory.java
index b93042e29e0..e083a906ea5 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/LinkRouteModelElementFactory.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/LinkRouteModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA
+ * Copyright (c) 2013, 2014 CEA and others
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,6 +9,7 @@
*
* Contributors:
* Soyatec - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
@@ -36,10 +37,9 @@ import org.eclipse.papyrus.infra.widgets.providers.EmptyContentProvider;
import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AnnotationModelElementFactory;
-public class LinkRouteModelElementFactory implements ModelElementFactory {
+public class LinkRouteModelElementFactory extends AnnotationModelElementFactory {
public static final String STYLE = "style";
@@ -51,7 +51,8 @@ public class LinkRouteModelElementFactory implements ModelElementFactory {
public static final Map<Object, LinkRouteModelElement> elements = new HashMap<Object, LinkRouteModelElement>();
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected AnnotationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View view = NotationHelper.findView(sourceElement);
if(view != null && view instanceof Edge) {
EditingDomain domain = EMFHelper.resolveEditingDomain(view);
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/MessageStyleElementFactory.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/MessageStyleElementFactory.java
index c4ee9ded95f..35893c76684 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/MessageStyleElementFactory.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/MessageStyleElementFactory.java
@@ -1,3 +1,16 @@
+/*****************************************************************************
+ * Copyright (c) 2012, 2014 CEA LIST and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
+ *****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
import org.eclipse.core.databinding.observable.IObservable;
@@ -12,24 +25,33 @@ import org.eclipse.papyrus.infra.gmfdiag.common.helper.NotationHelper;
import org.eclipse.papyrus.uml.tools.databinding.PapyrusObservableValue;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
-public class MessageStyleElementFactory implements ModelElementFactory {
+public class MessageStyleElementFactory extends AbstractModelElementFactory<MessageStyleElementFactory.MessageStyleModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected MessageStyleModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
View view = NotationHelper.findView(sourceElement);
if(view != null && view instanceof Edge) {
return new MessageStyleModelElement((Edge)view, context);
}
return null;
}
+
+ @Override
+ protected void updateModelElement(MessageStyleModelElement modelElement, Object newSourceElement) {
+ View view = NotationHelper.findView(newSourceElement);
+ if(!(view instanceof Edge)) {
+ throw new IllegalArgumentException("Cannot resolve Edge selection: " + newSourceElement);
+ }
+ modelElement.source = (Edge)view;
+ }
static class MessageStyleModelElement extends AbstractModelElement {
protected DataContextElement context;
- private Edge source;
+ protected Edge source;
public MessageStyleModelElement(Edge source, DataContextElement context) {
this.context = context;
diff --git a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/PreferencesModelElementFactory.java b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/PreferencesModelElementFactory.java
index 77b9f96fad9..6de72de94a0 100644
--- a/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/PreferencesModelElementFactory.java
+++ b/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence/custom-src/org/eclipse/papyrus/uml/diagram/sequence/util/PreferencesModelElementFactory.java
@@ -1,13 +1,20 @@
+/*****************************************************************************
+ * Copyright (c) 2012, 2014 CEA LIST and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
+ *****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
-import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
-import org.eclipse.papyrus.views.properties.modelelement.PreferencesModelElement;
-public class PreferencesModelElementFactory implements ModelElementFactory {
+public class PreferencesModelElementFactory extends org.eclipse.papyrus.views.properties.modelelement.PreferencesModelElementFactory {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
- return new PreferencesModelElement(context);
- }
+ // Empty
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CommentModelElementFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CommentModelElementFactory.java
index 5c0d6c429b2..a8de726dc5a 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CommentModelElementFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CommentModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Sebastien Poissonnet (CEA LIST) sebastien.poissonnet@cea.fr
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -16,13 +18,13 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
import org.eclipse.uml2.uml.Element;
-public class CommentModelElementFactory implements ModelElementFactory {
+public class CommentModelElementFactory extends AbstractEMFModelElementFactory<CommentModelElement> {
- public ModelElement createFromSource(Object source, DataContextElement context) {
+ @Override
+ protected CommentModelElement doCreateFromSource(Object source, DataContextElement context) {
Element umlSource = UMLUtil.resolveUMLElement(source);
if(umlSource == null) {
Activator.log.warn("Unable to resolve the selected element to a UML Element"); //$NON-NLS-1$
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CustomImageModelElementFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CustomImageModelElementFactory.java
index 11f4eb409de..eb8c6cf40bd 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CustomImageModelElementFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/CustomImageModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -16,8 +18,7 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.properties.Activator;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Image;
@@ -27,9 +28,10 @@ import org.eclipse.uml2.uml.Image;
* @author Camille Letavernier
*
*/
-public class CustomImageModelElementFactory implements ModelElementFactory {
+public class CustomImageModelElementFactory extends AbstractModelElementFactory<CustomImageModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected CustomImageModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
Element umlSource = UMLUtil.resolveUMLElement(sourceElement);
if(umlSource == null) {
Activator.log.warn("Unable to resolve the selected element to a UML Element"); //$NON-NLS-1$
@@ -45,4 +47,13 @@ public class CustomImageModelElementFactory implements ModelElementFactory {
return null;
}
+ @Override
+ protected void updateModelElement(CustomImageModelElement modelElement, Object newSourceElement) {
+ Element element = UMLUtil.resolveUMLElement(newSourceElement);
+ if(!(element instanceof Image)) {
+ throw new IllegalArgumentException("Cannot resolve UML Image selection: " + newSourceElement);
+ }
+ modelElement.image = (Image)element;
+ modelElement.editingDomain = EMFHelper.resolveEditingDomain(element);
+ }
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/MemberEndModelElement.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/MemberEndModelElement.java
index 326e894de72..d10b6c090f2 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/MemberEndModelElement.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/MemberEndModelElement.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 323802
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -45,9 +46,9 @@ import org.eclipse.uml2.uml.UMLPackage;
*/
public class MemberEndModelElement extends AbstractModelElement {
- private EObject source;
+ protected EObject source;
- private EditingDomain domain;
+ protected EditingDomain domain;
/**
* The "multiplicity" virtual property
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/ProfileDefinitionModelElementFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/ProfileDefinitionModelElementFactory.java
index ed2226ab56e..5e0917db1f0 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/ProfileDefinitionModelElementFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/ProfileDefinitionModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2013 CEA LIST.
+ * Copyright (c) 2013, 2014 CEA LIST. and others
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,21 +8,23 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Profile;
-public class ProfileDefinitionModelElementFactory implements ModelElementFactory {
+public class ProfileDefinitionModelElementFactory extends AbstractModelElementFactory<ProfileDefinitionModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected ProfileDefinitionModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
Element umlSource = UMLUtil.resolveUMLElement(sourceElement);
if(umlSource instanceof Profile) {
//EditingDomain domain = EMFHelper.resolveEditingDomain(umlSource);
@@ -33,4 +35,12 @@ public class ProfileDefinitionModelElementFactory implements ModelElementFactory
return null;
}
+ @Override
+ protected void updateModelElement(ProfileDefinitionModelElement modelElement, Object newSourceElement) {
+ Element element = org.eclipse.papyrus.uml.tools.utils.UMLUtil.resolveUMLElement(newSourceElement);
+ if(!(element instanceof Profile)) {
+ throw new IllegalArgumentException("Cannot resolve UML Profile selection: " + newSourceElement);
+ }
+ modelElement.profile = (Profile)element;
+ }
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceFactory.java
index 815c2f19be6..588a3d899e0 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -18,8 +20,7 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
import org.eclipse.uml2.uml.Element;
/**
@@ -28,9 +29,10 @@ import org.eclipse.uml2.uml.Element;
* @author Camille Letavernier
*
*/
-public class StereotypeAppearanceFactory implements ModelElementFactory {
+public class StereotypeAppearanceFactory extends AbstractModelElementFactory<StereotypeAppearanceModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected StereotypeAppearanceModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
Element umlSource = UMLUtil.resolveUMLElement(sourceElement);
if(umlSource == null) {
@@ -48,4 +50,15 @@ public class StereotypeAppearanceFactory implements ModelElementFactory {
return null;
}
+ @Override
+ protected void updateModelElement(StereotypeAppearanceModelElement modelElement, Object newSourceElement) {
+ if(!(newSourceElement instanceof EditPart)) {
+ throw new IllegalArgumentException("Cannot resolve EditPart selection: " + newSourceElement);
+ }
+
+ Element umlSource = UMLUtil.resolveUMLElement(newSourceElement);
+ modelElement.umlSource = umlSource;
+ modelElement.diagramElement = (EModelElement)((EditPart)newSourceElement).getModel();
+ modelElement.domain = EMFHelper.resolveEditingDomain(umlSource);
+ }
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceModelElement.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceModelElement.java
index 033b0071f2b..53612a08ae8 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceModelElement.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeAppearanceModelElement.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 323802
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -36,7 +37,6 @@ import org.eclipse.papyrus.infra.widgets.providers.StaticContentProvider;
import org.eclipse.papyrus.uml.properties.Activator;
import org.eclipse.papyrus.uml.properties.databinding.StereotypeAppearanceObservableValue;
import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
import org.eclipse.uml2.uml.Element;
/**
@@ -48,7 +48,7 @@ import org.eclipse.uml2.uml.Element;
* @author Camille Letavernier
*
*/
-public class StereotypeAppearanceModelElement extends AbstractModelElement implements ModelElement {
+public class StereotypeAppearanceModelElement extends AbstractModelElement {
/**
* The current UML Element
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationFactory.java
index b9720d0b759..c1d89b32dd7 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -17,8 +19,7 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
import org.eclipse.uml2.uml.Element;
/**
@@ -28,9 +29,10 @@ import org.eclipse.uml2.uml.Element;
* @author Camille Letavernier
*
*/
-public class StereotypeApplicationFactory implements ModelElementFactory {
+public class StereotypeApplicationFactory extends AbstractEMFModelElementFactory<StereotypeApplicationModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected StereotypeApplicationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
Element umlSource = UMLUtil.resolveUMLElement(sourceElement);
if(umlSource == null) {
Activator.log.warn("Unable to resolve the selected element to a UML Element"); //$NON-NLS-1$
@@ -45,4 +47,16 @@ public class StereotypeApplicationFactory implements ModelElementFactory {
}
}
+ @Override
+ protected void updateModelElement(StereotypeApplicationModelElement modelElement, Object newSourceElement) {
+ Element element = UMLUtil.resolveUMLElement(newSourceElement);
+ if(element == null) {
+ throw new IllegalArgumentException("Cannot resolve UML element selection: " + newSourceElement);
+ }
+ modelElement.umlSource = element;
+ modelElement.domain = EMFHelper.resolveEditingDomain(element);
+ modelElement.sourceElement = (newSourceElement instanceof EditPart) ? (EditPart)newSourceElement : null;
+
+ super.updateModelElement(modelElement, newSourceElement);
+ }
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationModelElement.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationModelElement.java
index 814a1519a5a..2ff1bf70958 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationModelElement.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeApplicationModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -41,11 +43,11 @@ import org.eclipse.uml2.uml.UMLPackage;
*/
public class StereotypeApplicationModelElement extends EMFModelElement {
- private Element umlSource;
+ protected Element umlSource;
- private EditingDomain domain;
+ protected EditingDomain domain;
- private EditPart sourceElement;
+ protected EditPart sourceElement;
/**
* The "stereotypeApplication" pseudo-property for a UML Element
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeModelElementFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeModelElementFactory.java
index 9a53ab7eaf6..fd4cde0488e 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeModelElementFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/StereotypeModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -17,8 +19,8 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.properties.Activator;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.EMFModelElement;
+import org.eclipse.papyrus.views.properties.modelelement.EMFModelElementFactory;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Stereotype;
@@ -41,9 +43,10 @@ import org.eclipse.uml2.uml.Stereotype;
*
* @author Camille Letavernier
*/
-public class StereotypeModelElementFactory implements ModelElementFactory {
+public class StereotypeModelElementFactory extends EMFModelElementFactory {
- public ModelElement createFromSource(Object source, DataContextElement context) {
+ @Override
+ protected EMFModelElement doCreateFromSource(Object source, DataContextElement context) {
Element umlElement = UMLUtil.resolveUMLElement(source);
if(umlElement != null) {
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLEditorFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLEditorFactory.java
index 4416258fe15..ef56f526563 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLEditorFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLEditorFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -16,8 +18,7 @@ import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
/**
* A Factory for building ModelElements for specific UML properties
@@ -25,9 +26,10 @@ import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
*
* @author Camille Letavernier
*/
-public class UMLEditorFactory implements ModelElementFactory {
+public class UMLEditorFactory extends AbstractModelElementFactory<MemberEndModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected MemberEndModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
@@ -39,4 +41,13 @@ public class UMLEditorFactory implements ModelElementFactory {
return new MemberEndModelElement(source, domain);
}
+ @Override
+ protected void updateModelElement(MemberEndModelElement modelElement, Object newSourceElement) {
+ EObject eObject = EMFHelper.getEObject(newSourceElement);
+ if(eObject == null) {
+ throw new IllegalArgumentException("Cannot resolve EObject selection: " + newSourceElement);
+ }
+ modelElement.source = eObject;
+ modelElement.domain = EMFHelper.resolveEditingDomain(eObject);
+ }
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLModelElementFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLModelElementFactory.java
index 38fff4b2838..fb909dd50bf 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLModelElementFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -16,8 +18,7 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.uml.tools.utils.UMLUtil;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.EMFModelElementFactory;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractEMFModelElementFactory;
import org.eclipse.uml2.uml.Element;
/**
@@ -25,10 +26,10 @@ import org.eclipse.uml2.uml.Element;
*
* @author Camille Letavernier
*/
-public class UMLModelElementFactory extends EMFModelElementFactory {
+public class UMLModelElementFactory extends AbstractEMFModelElementFactory<UMLModelElement> {
@Override
- public ModelElement createFromSource(Object source, DataContextElement context) {
+ protected UMLModelElement doCreateFromSource(Object source, DataContextElement context) {
Element umlSource = UMLUtil.resolveUMLElement(source);
if(umlSource == null) {
Activator.log.warn("Unable to resolve the selected element to a UML Element"); //$NON-NLS-1$
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationFactory.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationFactory.java
index 8bc23caf53e..a1ba5c2c92c 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationFactory.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,13 +8,14 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
import org.eclipse.gef.EditPart;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElement;
-import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
+import org.eclipse.papyrus.views.properties.modelelement.AbstractModelElementFactory;
/**
* A ModelElementFactory for handling UML-specific appearance properties
@@ -22,13 +23,21 @@ import org.eclipse.papyrus.views.properties.modelelement.ModelElementFactory;
* @author Camille Letavernier
*
*/
-public class UMLNotationFactory implements ModelElementFactory {
+public class UMLNotationFactory extends AbstractModelElementFactory<UMLNotationModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected UMLNotationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
if(sourceElement instanceof EditPart) {
return new UMLNotationModelElement((EditPart)sourceElement);
}
return null;
}
+ @Override
+ protected void updateModelElement(UMLNotationModelElement modelElement, Object newSourceElement) {
+ if(!(newSourceElement instanceof EditPart)) {
+ throw new IllegalArgumentException("Cannot resolve EditPart selection: " + newSourceElement);
+ }
+ modelElement.sourceElement = (EditPart)newSourceElement;
+ }
}
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationModelElement.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationModelElement.java
index c6c680cd2d8..e72a6ce32d2 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationModelElement.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/modelelement/UMLNotationModelElement.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 323802
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.modelelement;
@@ -68,7 +69,7 @@ public class UMLNotationModelElement extends AbstractModelElement {
/**
* The GMF EditPart represented by this ModelElement
*/
- private EditPart sourceElement;
+ protected EditPart sourceElement;
/**
*
diff --git a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/widgets/StereotypeApplication.java b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/widgets/StereotypeApplication.java
index 966bbcd8bdb..a582e5baa79 100644
--- a/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/widgets/StereotypeApplication.java
+++ b/plugins/uml/properties/org.eclipse.papyrus.uml.properties/src/org/eclipse/papyrus/uml/properties/widgets/StereotypeApplication.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.properties.widgets;
@@ -59,24 +61,34 @@ public class StereotypeApplication extends AbstractPropertyEditor {
// for editing the data
ModelElement element = input.getModelElement(propertyPath);
if(element instanceof StereotypeApplicationModelElement) {
+ internalDoBinding();
+
+ input.getObservable(propertyPath).addChangeListener(new IChangeListener() {
+ public void handleChange(ChangeEvent event) {
+ // re-do the injection into the stereotype composite because the
+ // underlying model element selection may have been changed
+ internalDoBinding();
+ }
+ });
+ }
+ }
+
+ protected void internalDoBinding() {
+ ModelElement element = input.getModelElement(propertyPath);
+ if(element instanceof StereotypeApplicationModelElement) {
StereotypeApplicationModelElement modelElement = (StereotypeApplicationModelElement)element;
View diagramElement = (View)modelElement.getGraphicalElement();
//EditPart editPart = ((StereotypeApplicationModelElement)element).getEditPart();
final Element umlElement = modelElement.getUMLElement();
- //stereotypeComposite.setSelection(new StructuredSelection(Collections.singletonList(editPart)));
- stereotypeComposite.setElement(umlElement);
- stereotypeComposite.setInput(new StereotypedElementTreeObject(umlElement));
+ if(stereotypeComposite.getElement() != umlElement) {
+ stereotypeComposite.setElement(umlElement);
+ stereotypeComposite.setInput(new StereotypedElementTreeObject(umlElement));
+ }
stereotypeComposite.setDiagramElement(diagramElement);
stereotypeComposite.refresh();
-
- input.getObservable(propertyPath).addChangeListener(new IChangeListener() {
- public void handleChange(ChangeEvent event) {
- stereotypeComposite.refresh();
- }
- });
}
}
}
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/AbstractUMLAggregatedObservableValue.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/AbstractUMLAggregatedObservableValue.java
index 9732eb47bb0..3af456b7a22 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/AbstractUMLAggregatedObservableValue.java
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/AbstractUMLAggregatedObservableValue.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,13 +8,15 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.databinding;
import org.eclipse.core.databinding.observable.IObservable;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
/**
* An Abstract Class for Papyrus Command-based observable values
@@ -22,7 +24,7 @@ import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
* @author Camille Letavernier
*
*/
-public abstract class AbstractUMLAggregatedObservableValue extends AbstractObservableValue implements AggregatedObservable, CommandBasedObservableValue {
+public abstract class AbstractUMLAggregatedObservableValue extends ReferenceCountedObservable.Value implements AggregatedObservable, CommandBasedObservableValue {
protected EditingDomain domain;
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/MultiplicityObservableValue.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/MultiplicityObservableValue.java
index e62380f34b5..3fa6dc61457 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/MultiplicityObservableValue.java
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/MultiplicityObservableValue.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - 402525
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.databinding;
@@ -17,7 +18,6 @@ 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.IObserving;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.ValueDiff;
import org.eclipse.emf.common.command.Command;
@@ -26,6 +26,7 @@ import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.papyrus.uml.tools.Activator;
import org.eclipse.papyrus.uml.tools.commands.SetMultiplicityCommand;
import org.eclipse.papyrus.uml.tools.helper.UMLDatabindingHelper;
@@ -43,7 +44,7 @@ import org.eclipse.uml2.uml.UMLPackage;
*
* @author Camille Letavernier
*/
-public class MultiplicityObservableValue extends AbstractObservableValue implements IChangeListener, CommandBasedObservableValue, AggregatedObservable, IObserving {
+public class MultiplicityObservableValue extends ReferenceCountedObservable.Value implements IChangeListener, CommandBasedObservableValue, AggregatedObservable, IObserving {
private IObservableValue lowerBound, upperBound, lowerValue, upperValue, lowerValueSpecification, upperValueSpecification;
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/NavigationObservableValue.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/NavigationObservableValue.java
index f2308f0b186..58d689875a9 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/NavigationObservableValue.java
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/NavigationObservableValue.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - 402525
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.databinding;
@@ -23,7 +24,6 @@ import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.list.IObservableList;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.command.UnexecutableCommand;
@@ -37,6 +37,7 @@ import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper;
import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils;
import org.eclipse.papyrus.infra.services.edit.service.IElementEditService;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Property;
@@ -48,7 +49,7 @@ import org.eclipse.uml2.uml.UMLPackage;
*
* @author Camille Letavernier
*/
-public class NavigationObservableValue extends AbstractObservableValue implements IChangeListener, CommandBasedObservableValue, AggregatedObservable, IObserving {
+public class NavigationObservableValue extends ReferenceCountedObservable.Value implements IChangeListener, CommandBasedObservableValue, AggregatedObservable, IObserving {
private Property memberEnd;
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/OwnerObservableValue.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/OwnerObservableValue.java
index 1fab72a0498..0523525d03e 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/OwnerObservableValue.java
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/OwnerObservableValue.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - 402525
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.databinding;
@@ -22,7 +23,6 @@ import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.list.IObservableList;
-import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.UnexecutableCommand;
import org.eclipse.emf.common.util.EList;
@@ -36,6 +36,7 @@ import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper;
import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils;
import org.eclipse.papyrus.infra.services.edit.service.IElementEditService;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.papyrus.uml.tools.Activator;
import org.eclipse.uml2.uml.Artifact;
import org.eclipse.uml2.uml.Association;
@@ -60,7 +61,7 @@ import org.eclipse.uml2.uml.UMLPackage;
*
* @author Camille Letavernier
*/
-public class OwnerObservableValue extends AbstractObservableValue implements IChangeListener, AggregatedObservable, CommandBasedObservableValue, IObserving {
+public class OwnerObservableValue extends ReferenceCountedObservable.Value implements IChangeListener, AggregatedObservable, CommandBasedObservableValue, IObserving {
private Property memberEnd;
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/PapyrusObservableValue.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/PapyrusObservableValue.java
index 6ae25d3cfbf..99ef34d192d 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/PapyrusObservableValue.java
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/databinding/PapyrusObservableValue.java
@@ -10,6 +10,7 @@
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Thibault Le Ouay t.leouay@sherpa-eng.com - Add binding implementation
* Christian W. Damus (CEA) - bug 440108
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.databinding;
@@ -33,6 +34,7 @@ import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils;
import org.eclipse.papyrus.infra.services.edit.service.IElementEditService;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.papyrus.uml.tools.Activator;
/**
@@ -42,7 +44,9 @@ import org.eclipse.papyrus.uml.tools.Activator;
* @author Camille Letavernier
*
*/
-public class PapyrusObservableValue extends EMFObservableValue implements AggregatedObservable, CommandBasedObservableValue {
+public class PapyrusObservableValue extends EMFObservableValue implements AggregatedObservable, CommandBasedObservableValue, ReferenceCountedObservable {
+
+ private final ReferenceCountedObservable.Support refCount = new ReferenceCountedObservable.Support(this);
/**
*
@@ -119,7 +123,7 @@ public class PapyrusObservableValue extends EMFObservableValue implements Aggreg
return UnexecutableCommand.INSTANCE;
}
-
+
protected IEditCommandRequest createSetRequest(TransactionalEditingDomain domain, EObject owner, EStructuralFeature feature, Object value) {
return new SetRequest(domain, owner, feature, value);
}
@@ -151,4 +155,16 @@ public class PapyrusObservableValue extends EMFObservableValue implements Aggreg
public boolean hasDifferentValues() {
return false; //The value is not aggregated yet
}
+
+ public void retain() {
+ refCount.retain();
+ }
+
+ public void release() {
+ refCount.release();
+ }
+
+ public void autorelease() {
+ refCount.autorelease();
+ }
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractEMFModelElementFactory.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractEMFModelElementFactory.java
new file mode 100644
index 00000000000..4e53be287f0
--- /dev/null
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractEMFModelElementFactory.java
@@ -0,0 +1,41 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2014 CEA LIST and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.views.properties.modelelement;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+
+/**
+ * A partial implementation of a ModelElementFactory for creating {@link EMFModelElement}s
+ *
+ * @author Camille Letavernier
+ *
+ */
+public abstract class AbstractEMFModelElementFactory<T extends EMFModelElement> extends AbstractModelElementFactory<T> {
+
+ @Override
+ protected void updateModelElement(T modelElement, Object newSourceElement) {
+ EObject eObject = EMFHelper.getEObject(newSourceElement);
+ if(eObject == null) {
+ throw new IllegalArgumentException("Cannot resolve EObject selection: " + newSourceElement);
+ }
+
+ updateEMFModelElement(modelElement, eObject);
+ }
+
+ public static void updateEMFModelElement(EMFModelElement modelElement, EObject newEObject) {
+ modelElement.source = newEObject;
+ modelElement.domain = EMFHelper.resolveEditingDomain(newEObject);
+ }
+}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElement.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElement.java
index a69f51490b8..d316c2c8678 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElement.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,15 +9,24 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Thibault Le Ouay t.leouay@sherpa-eng.com - Add binding implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
+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.validation.IValidator;
import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.papyrus.infra.tools.databinding.DelegatingObservableValue;
+import org.eclipse.papyrus.infra.tools.databinding.IDelegatingObservable;
+import org.eclipse.papyrus.infra.tools.databinding.ReferenceCountedObservable;
import org.eclipse.papyrus.infra.widgets.creation.ReferenceValueFactory;
import org.eclipse.papyrus.infra.widgets.providers.EmptyContentProvider;
import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider;
@@ -29,7 +38,7 @@ import org.eclipse.papyrus.views.properties.creation.PropertyEditorFactory;
*
* @author Camille Letavernier
*/
-public abstract class AbstractModelElement implements ModelElement {
+public abstract class AbstractModelElement implements ModelElement, IDataSourceListener {
/**
* The DataSource owning this ModelElement
@@ -38,11 +47,20 @@ public abstract class AbstractModelElement implements ModelElement {
private final Map<String, IObservable> observables = new HashMap<String, IObservable>();
-
+ private IDisposeListener observableDisposeListener;
+
+ AbstractModelElementFactory<AbstractModelElement> factory;
+
/**
* Constructor.
*/
protected AbstractModelElement() {
+ super();
+ }
+
+ @SuppressWarnings("unchecked")
+ void setFactory(AbstractModelElementFactory<? extends AbstractModelElement> factory) {
+ this.factory = (AbstractModelElementFactory<AbstractModelElement>)factory;
}
public IStaticContentProvider getContentProvider(String propertyPath) {
@@ -74,7 +92,41 @@ public abstract class AbstractModelElement implements ModelElement {
}
public void setDataSource(DataSource source) {
- this.dataSource = source;
+ if(this.dataSource != source) {
+ if(this.dataSource != null) {
+ this.dataSource.removeDataSourceListener(this);
+ }
+
+ this.dataSource = source;
+
+ if(this.dataSource != null) {
+ this.dataSource.addDataSourceListener(this);
+ }
+ }
+ }
+
+ public final void dataSourceChanged(DataSourceChangedEvent event) {
+ if(event.getDataSource() == dataSource) {
+ // The data source changed. Update for the new selection
+ IStructuredSelection selection = dataSource.getSelection();
+ if(selection.isEmpty()) {
+ factory.updateModelElement(this, null);
+ } else if(selection.size() == 1) {
+ factory.updateModelElement(this, selection.getFirstElement());
+ } else {
+ updateMultipleSelection(selection);
+ }
+
+ // Update our observables
+ for(Map.Entry<String, IObservable> next : observables.entrySet()) {
+ IDelegatingObservable wrapper = ((IDelegatingObservable)next.getValue());
+ wrapper.setDelegate(doGetObservable(next.getKey()));
+ }
+ }
+ }
+
+ void updateMultipleSelection(IStructuredSelection selection) {
+ throw new IllegalArgumentException("multiple selection"); //$NON-NLS-1$
}
/**
@@ -100,6 +152,10 @@ public abstract class AbstractModelElement implements ModelElement {
if(!observables.containsKey(propertyPath)) {
IObservable observable = doGetObservable(propertyPath);
if(observable != null) {
+ // Wrap it so that we may replace the delegate as needed
+ observable = DelegatingObservableValue.wrap(observable);
+ observable.addDisposeListener(getObservableDisposeListener());
+ ReferenceCountedObservable.Util.retain(observable);
observables.put(propertyPath, observable);
}
}
@@ -118,15 +174,39 @@ public abstract class AbstractModelElement implements ModelElement {
public void dispose() {
for(IObservable observable : observables.values()) {
- observable.dispose();
+ if(observableDisposeListener != null) {
+ // Don't let the listener concurrently modify the map in case releasing triggers dispose
+ observable.removeDisposeListener(observableDisposeListener);
+ }
+
+ ReferenceCountedObservable.Util.release(observable);
}
+
observables.clear();
+ observableDisposeListener = null;
}
-
- public IValidator getValidator(String propertyPath){
+
+ public IValidator getValidator(String propertyPath) {
return null;
}
-
-
+ private IDisposeListener getObservableDisposeListener() {
+ if(observableDisposeListener == null) {
+ observableDisposeListener = new IDisposeListener() {
+
+ public void handleDispose(DisposeEvent event) {
+ // Remove this property
+ for(Iterator<Map.Entry<String, IObservable>> entries = observables.entrySet().iterator(); entries.hasNext();) {
+ if(entries.next().getValue() == event.getObservable()) {
+ entries.remove();
+ break;
+ }
+ }
+ }
+ };
+ }
+
+ return observableDisposeListener;
+ }
+
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElementFactory.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElementFactory.java
new file mode 100644
index 00000000000..b9517cfee18
--- /dev/null
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AbstractModelElementFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.views.properties.modelelement;
+
+import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
+
+
+/**
+ * This is the AbstractModelElementFactory type. Enjoy.
+ */
+public abstract class AbstractModelElementFactory<T extends AbstractModelElement> implements ModelElementFactory {
+
+ protected AbstractModelElementFactory() {
+ super();
+ }
+
+ public final ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ AbstractModelElement result = doCreateFromSource(sourceElement, context);
+
+ if(result != null) {
+ result.setFactory(this);
+ }
+
+ return result;
+ }
+
+ protected abstract T doCreateFromSource(Object sourceElement, DataContextElement context);
+
+ protected abstract void updateModelElement(T modelElement, Object newSourceElement);
+}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AnnotationModelElementFactory.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AnnotationModelElementFactory.java
index 3dd13364b61..8d84047ace7 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AnnotationModelElementFactory.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/AnnotationModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011 CEA LIST.
+ * Copyright (c) 2011, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
@@ -23,9 +25,10 @@ import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
*
* @author Camille Letavernier
*/
-public class AnnotationModelElementFactory implements ModelElementFactory {
+public class AnnotationModelElementFactory extends AbstractModelElementFactory<AnnotationModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected AnnotationModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
@@ -40,4 +43,13 @@ public class AnnotationModelElementFactory implements ModelElementFactory {
return new AnnotationModelElement((EModelElement)source, domain, context.getName());
}
+ @Override
+ protected void updateModelElement(AnnotationModelElement modelElement, Object newSourceElement) {
+ EObject eObject = EMFHelper.getEObject(newSourceElement);
+ if(!(eObject instanceof EModelElement)) {
+ throw new IllegalArgumentException("Cannot resolve EModelElement selection: " + newSourceElement);
+ }
+ modelElement.source = (EModelElement)eObject;
+ modelElement.domain = EMFHelper.resolveEditingDomain(eObject);
+ }
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/CompositeModelElement.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/CompositeModelElement.java
index 0b4573a085f..73e046102df 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/CompositeModelElement.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/CompositeModelElement.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,15 +8,20 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.papyrus.infra.tools.databinding.AggregatedObservable;
import org.eclipse.papyrus.infra.tools.databinding.MultipleObservableValue;
import org.eclipse.papyrus.infra.widgets.providers.EmptyContentProvider;
@@ -34,6 +39,15 @@ import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider;
*/
public class CompositeModelElement extends AbstractModelElement {
+ private final BoundModelElementFactory subModelElementFactory;
+
+
+ public CompositeModelElement(BoundModelElementFactory subModelElementFactory) {
+ super();
+
+ this.subModelElementFactory = subModelElementFactory;
+ }
+
@Override
public IObservable doGetObservable(String propertyPath) {
@@ -69,6 +83,47 @@ public class CompositeModelElement extends AbstractModelElement {
return observableComposite;
}
+ @Override
+ void updateMultipleSelection(IStructuredSelection selection) {
+ ListIterator<ModelElement> subElements = elements.listIterator();
+ Iterator<?> newSourceElements = selection.iterator();
+
+ // Re-use existing sub-elements, just updating them
+ while(newSourceElements.hasNext() && subElements.hasNext()) {
+ ModelElement nextSubElement = subElements.next();
+ if(nextSubElement instanceof AbstractModelElement) {
+ // Can reuse it
+ AbstractModelElement reusable = (AbstractModelElement)nextSubElement;
+ reusable.factory.updateModelElement(reusable, newSourceElements.next());
+ } else {
+ // Replace it
+ nextSubElement.dispose();
+
+ ModelElement newSubElement = subModelElementFactory.createModelElement(newSourceElements.next());
+ if(newSubElement != null) {
+ subElements.set(newSubElement);
+ } else {
+ // TODO: Report a warning?
+ subElements.remove();
+ }
+ }
+ }
+
+ // And create new ones if necessary
+ while(newSourceElements.hasNext()) {
+ ModelElement newSubElement = subModelElementFactory.createModelElement(newSourceElements.next());
+ if(newSubElement != null) {
+ subElements.add(newSubElement);
+ } // TODO: Else report a warning?
+ }
+
+ // And destroy any unneeded sub-elements
+ while(subElements.hasNext()) {
+ subElements.next().dispose();
+ subElements.remove();
+ }
+ }
+
/**
* Adds a sub-model element to this CompositeModelElement
*
@@ -185,4 +240,17 @@ public class CompositeModelElement extends AbstractModelElement {
public List<ModelElement> getSubElements() {
return elements;
}
+
+ //
+ // Nested types
+ //
+
+ /**
+ * Protocol for a factory that a {@link CompositeModelElement} uses to create sub-elements for a multiple
+ * selection. It binds all of the necessary context so that the only input is a selected source element.
+ */
+ public interface BoundModelElementFactory {
+
+ ModelElement createModelElement(Object sourceElement);
+ }
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSource.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSource.java
index 362835a029d..23368e23579 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSource.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSource.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,18 +9,19 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Thibault Le Ouay t.leouay@sherpa-eng.com - Add binding implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-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.validation.IValidator;
+import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.papyrus.infra.widgets.creation.ReferenceValueFactory;
@@ -50,8 +51,10 @@ import org.eclipse.papyrus.views.properties.contexts.View;
*/
public class DataSource implements IChangeListener {
- private Set<IChangeListener> changeListeners = new HashSet<IChangeListener>();
+ private final ListenerList changeListeners = new ListenerList(ListenerList.IDENTITY);
+ private final ListenerList dataSourceListeners = new ListenerList(ListenerList.IDENTITY);
+
private View view;
private IStructuredSelection selection;
@@ -61,6 +64,7 @@ public class DataSource implements IChangeListener {
/**
* Constructs a new DataSource from the given view and selection
*
+ * @param realm
* @param view
* @param selection
*
@@ -189,13 +193,37 @@ public class DataSource implements IChangeListener {
public void removeChangeListener(IChangeListener listener) {
changeListeners.remove(listener);
}
+
+ public void addDataSourceListener(IDataSourceListener listener) {
+ dataSourceListeners.add(listener);
+ }
+
+ public void removeDataSourceListener(IDataSourceListener listener) {
+ dataSourceListeners.remove(listener);
+ }
public void handleChange(ChangeEvent event) {
- //The set of listeners may change during the update.
- Set<IChangeListener> listeners = new HashSet<IChangeListener>(changeListeners);
-
- for(IChangeListener listener : listeners) {
- listener.handleChange(event);
+ Object[] listeners = changeListeners.getListeners();
+ for(int i = 0; i < listeners.length; i++) {
+ try {
+ ((IChangeListener)listeners[i]).handleChange(event);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in observable change listener.", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ protected void fireDataSourceChanged() {
+ Object[] listeners = dataSourceListeners.getListeners();
+ if(listeners.length > 0) {
+ DataSourceChangedEvent event = new DataSourceChangedEvent(this);
+ for(int i = 0; i < listeners.length; i++) {
+ try {
+ ((IDataSourceListener)listeners[i]).dataSourceChanged(event);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in data-source listener.", e); //$NON-NLS-1$
+ }
+ }
}
}
@@ -212,6 +240,17 @@ public class DataSource implements IChangeListener {
public IStructuredSelection getSelection() {
return selection;
}
+
+ /**
+ * @param selection the selection to set
+ */
+ public void setSelection(IStructuredSelection selection) {
+ if(!selection.equals(this.selection)) {
+ this.selection = selection;
+
+ fireDataSourceChanged();
+ }
+ }
/**
* @param propertyPath
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceChangedEvent.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceChangedEvent.java
new file mode 100644
index 00000000000..f102926cb9a
--- /dev/null
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceChangedEvent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.views.properties.modelelement;
+
+import java.util.EventObject;
+
+
+/**
+ * This is the DataSourceChangedEvent type. Enjoy.
+ */
+public class DataSourceChangedEvent extends EventObject {
+
+ private static final long serialVersionUID = 1L;
+
+ DataSourceChangedEvent(DataSource source) {
+ super(source);
+ }
+
+ public DataSource getDataSource() {
+ return (DataSource)getSource();
+ }
+}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceFactory.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceFactory.java
index 0a36400fdf8..9dc30680fcd 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceFactory.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/DataSourceFactory.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 435103
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
@@ -107,12 +108,18 @@ public class DataSourceFactory {
* @return The model element corresponding to the given contextElement and
* selection
*/
- private ModelElement createModelElement(DataContextElement contextElement, IStructuredSelection selection) {
+ private ModelElement createModelElement(final DataContextElement contextElement, IStructuredSelection selection) {
if(selection.size() == 1) { // Single Selection
ModelElement modelElement = createFromSource(selection.getFirstElement(), contextElement);
return modelElement;
} else { // MultiSelection
- CompositeModelElement composite = new CompositeModelElement();
+ // Bind the context element in a factory for the composite to create sub-elements
+ CompositeModelElement composite = new CompositeModelElement(new CompositeModelElement.BoundModelElementFactory() {
+
+ public ModelElement createModelElement(Object sourceElement) {
+ return createFromSource(sourceElement, contextElement);
+ }
+ });
Iterator<?> it = selection.iterator();
while(it.hasNext()) {
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/EMFModelElementFactory.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/EMFModelElementFactory.java
index 8877aca5120..6559d7eff55 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/EMFModelElementFactory.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/EMFModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
@@ -23,9 +25,10 @@ import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
* @author Camille Letavernier
*
*/
-public class EMFModelElementFactory implements ModelElementFactory {
+public class EMFModelElementFactory extends AbstractEMFModelElementFactory<EMFModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected EMFModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
EObject source = EMFHelper.getEObject(sourceElement);
if(source == null) {
Activator.log.warn("Unable to resolve the selected element to an EObject"); //$NON-NLS-1$
@@ -35,5 +38,4 @@ public class EMFModelElementFactory implements ModelElementFactory {
EditingDomain domain = EMFHelper.resolveEditingDomain(source);
return new EMFModelElement(source, domain);
}
-
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/IDataSourceListener.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/IDataSourceListener.java
new file mode 100644
index 00000000000..9e5b9a4b6d2
--- /dev/null
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/IDataSourceListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.views.properties.modelelement;
+
+import java.util.EventListener;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+
+/**
+ * A listener protocol for changes to a {@link DataSource} (especially its selection). Data sources are not {@linkplain IObservable observables}
+ * because that would confuse the XWT bindings framework.
+ *
+ * @see DataSource
+ * @see DataSource#addDataSourceListener(IDataSourceListener)
+ */
+public interface IDataSourceListener extends EventListener {
+
+ void dataSourceChanged(DataSourceChangedEvent event);
+}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/PreferencesModelElementFactory.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/PreferencesModelElementFactory.java
index 0aa2db099db..e771d4e3141 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/PreferencesModelElementFactory.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/modelelement/PreferencesModelElementFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2012 CEA LIST.
+ * Copyright (c) 2012, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,16 +8,31 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.modelelement;
+import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.papyrus.views.properties.contexts.DataContextElement;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
-public class PreferencesModelElementFactory implements ModelElementFactory {
+public class PreferencesModelElementFactory extends AbstractModelElementFactory<PreferencesModelElement> {
- public ModelElement createFromSource(Object sourceElement, DataContextElement context) {
+ @Override
+ protected PreferencesModelElement doCreateFromSource(Object sourceElement, DataContextElement context) {
return new PreferencesModelElement(context);
}
+ @Override
+ protected void updateModelElement(PreferencesModelElement modelElement, Object newSourceElement) {
+ if(!(newSourceElement instanceof DataContextElement)) {
+ throw new IllegalArgumentException("Cannot resolve DataContextElement selection: " + newSourceElement);
+ }
+
+ DataContextElement context = (DataContextElement)newSourceElement;
+ modelElement.context = context;
+ modelElement.store = new ScopedPreferenceStore(InstanceScope.INSTANCE, context.getName());
+ }
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/DefaultDisplayEngine.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/DefaultDisplayEngine.java
index dc5360b25b5..a55521e39fb 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/DefaultDisplayEngine.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/DefaultDisplayEngine.java
@@ -15,10 +15,10 @@
package org.eclipse.papyrus.views.properties.runtime;
import java.net.URL;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -40,6 +40,8 @@ import org.eclipse.papyrus.xwt.ILoadingContext;
import org.eclipse.papyrus.xwt.IXWTLoader;
import org.eclipse.papyrus.xwt.XWT;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
@@ -55,12 +57,14 @@ public class DefaultDisplayEngine implements DisplayEngine {
private ILoadingContext loadingContext = new DefaultLoadingContext(getClass().getClassLoader());
- private Set<String> displayedSections = new HashSet<String>();
+ private Map<String, XWTTabDescriptor> currentTabs = new HashMap<String, XWTTabDescriptor>();
- private Set<Control> controls = new HashSet<Control>();
+ private TabModel<DataSource> displayedSections = new TabModel<DataSource>();
+
+ private TabModel<Control> controls = new TabModel<Control>();
private boolean allowDuplicate;
-
+
private Object xmlCache;
/**
@@ -84,7 +88,6 @@ public class DefaultDisplayEngine implements DisplayEngine {
}
public List<ITabDescriptor> getTabDescriptors(Set<View> views) {
-
Map<String, XWTTabDescriptor> result = new LinkedHashMap<String, XWTTabDescriptor>();
Set<String> selectedSections = new HashSet<String>();
@@ -116,20 +119,37 @@ public class DefaultDisplayEngine implements DisplayEngine {
}
}
- disposeControls();
- return new LinkedList<ITabDescriptor>(result.values());
+ for(Map.Entry<String, XWTTabDescriptor> next : result.entrySet()) {
+ XWTTabDescriptor existing = currentTabs.get(next.getKey());
+ if((existing != null) && !existing.equals(next.getValue())) {
+ // Will have to rebuild this tab
+ disposeControls(next.getKey());
+ }
+ }
+
+ currentTabs = result;
+
+ return new ArrayList<ITabDescriptor>(result.values());
}
/**
- * Disposes the controls created by this DisplayEngine
+ * Disposes the controls created by this DisplayEngine for the specified tab ID.
* This should not dispose the engine itself, which can be reused.
*/
- protected void disposeControls() {
- for(Control control : controls) {
+ protected void disposeControls(String tabID) {
+ for(Control control : this.controls.remove(tabID)) {
control.dispose();
}
- displayedSections.clear();
- controls.clear();
+
+ for(DataSource dataSource : displayedSections.remove(tabID)) {
+ dataSource.dispose();
+ }
+ }
+
+ protected void disposeControls() {
+ for(String next : new ArrayList<String>(controls.tabIDs())) {
+ disposeControls(next);
+ }
}
/**
@@ -138,7 +158,7 @@ public class DefaultDisplayEngine implements DisplayEngine {
public void dispose() {
disposeControls();
}
-
+
/**
* Invalidates any caches that I may have because the displayed property UI contexts, constraints, or views have
* changed in some way.
@@ -153,21 +173,54 @@ public class DefaultDisplayEngine implements DisplayEngine {
return null;
}
- if(!allowDuplicate && displayedSections.contains(section.getName())) {
+ DataSource existing = getDataSource(section);
+ if(!allowDuplicate && (existing != null)) {
+ // Update the data source and fire the bindings
+ existing.setSelection(source.getSelection());
+
return null;
}
Control control = createSection(parent, section, loadXWTFile(section), source);
- displayedSections.add(section.getName());
+ addDataSource(section, source);
if(control != null) {
- controls.add(control);
+ addControl(section, control);
}
return control;
}
+ protected DataSource getDataSource(Section section) {
+ return displayedSections.get(section.getTab().getId(), section.getName());
+ }
+
+ /**
+ * Adds a new {@code dataSource} for a property {@code section}.
+ *
+ * @return the previously-recorded data source, if any, for this {@code section} which has now been displaced
+ */
+ protected DataSource addDataSource(Section section, DataSource dataSource) {
+ return displayedSections.put(section.getTab().getId(), section.getName(), dataSource);
+ }
+
+ protected void addControl(Section section, Control control) {
+ final String tabID = section.getTab().getId();
+ final String sectionID = section.getName();
+
+ controls.put(tabID, sectionID, control);
+
+ control.addDisposeListener(new DisposeListener() {
+
+ public void widgetDisposed(DisposeEvent e) {
+ // Perhaps the tabbed properties view is disposing a tab that is not shown by the new selection
+ displayedSections.remove(tabID, sectionID);
+ controls.remove(tabID, sectionID);
+ }
+ });
+ }
+
public void refreshSection(Composite parent, Section section, DataSource source) {
for(Control control : parent.getChildren()) {
control.dispose();
@@ -175,10 +228,10 @@ public class DefaultDisplayEngine implements DisplayEngine {
Control control = createSection(parent, section, loadXWTFile(section), source);
- displayedSections.add(section.getName());
+ addDataSource(section, source);
if(control != null) {
- controls.add(control);
+ addControl(section, control);
}
}
@@ -198,7 +251,7 @@ public class DefaultDisplayEngine implements DisplayEngine {
try {
ResourceSet rset = section.eResource().getResourceSet();
URL url = new URL(null, sectionFile.toString(), new EMFURLStreamHandler(rset.getURIConverter()));
-
+
Map<String, Object> options = new HashMap<String, Object>();
options.put(IXWTLoader.CONTAINER_PROPERTY, parent);
options.put(IXWTLoader.DATACONTEXT_PROPERTY, source);
@@ -208,11 +261,11 @@ public class DefaultDisplayEngine implements DisplayEngine {
if(control != null) {
control.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- controls.add(control);
+ addControl(section, control);
}
} catch (Exception ex) {
Activator.log.error("Error while loading " + section.getSectionFile(), ex); //$NON-NLS-1$
- disposeControls();
+ disposeControls(section.getTab().getId());
Label label = new Label(parent, SWT.NONE);
label.setText("An error occured in the property view. The file " + section.getSectionFile() + " could not be loaded"); //$NON-NLS-1$ //$NON-NLS-2$
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/TabModel.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/TabModel.java
new file mode 100644
index 00000000000..da005579afb
--- /dev/null
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/runtime/TabModel.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.views.properties.runtime;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * An encapsulation of the hierarchical tab structure of the property sheets managed by a {@link DisplayEngine}.
+ */
+class TabModel<V> {
+
+ private final Map<Path, V> model = new HashMap<Path, V>();
+
+ TabModel() {
+ super();
+ }
+
+ public Set<String> tabIDs() {
+ Set<String> result = new HashSet<String>();
+
+ for(Path next : model.keySet()) {
+ result.add(next.tabID);
+ }
+
+ return Collections.unmodifiableSet(result);
+ }
+
+ public V get(String tabID, String sectionID) {
+ return model.get(new Path(tabID, sectionID));
+ }
+
+ public Collection<V> get(String tabID) {
+ List<V> result = new ArrayList<V>(4);
+
+ Path key = new Path(tabID);
+ for(Map.Entry<Path, V> next : model.entrySet()) {
+ if(next.getKey().equals(key)) {
+ result.add(next.getValue());
+ }
+ }
+
+ return Collections.unmodifiableList(result);
+ }
+
+ public V put(String tabID, String sectionID, V value) {
+ return model.put(new Path(tabID, sectionID), value);
+ }
+
+ public V remove(String tabID, String sectionID) {
+ return model.remove(new Path(tabID, sectionID));
+ }
+
+ public Collection<V> remove(String tabID) {
+ List<V> result = new ArrayList<V>(4);
+
+ Path key = new Path(tabID);
+ for(V next = model.remove(key); next != null; next = model.remove(key)) {
+ result.add(next);
+ }
+
+ return Collections.unmodifiableList(result);
+ }
+
+ public Collection<V> clear() {
+ List<V> result = new ArrayList<V>(model.values());
+
+ model.clear();
+
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("TabModel%s", model.toString()); //$NON-NLS-1$
+ }
+
+ //
+ // Nested types
+ //
+
+ final static class Path {
+
+ // Do not intern this string! Deliberately constructing a new String instance
+ private static final String WILDCARD = new String("*"); //$NON-NLS-1$
+
+ final String tabID;
+
+ final String sectionID;
+
+ Path(String tabID, String sectionID) {
+ checkWildcard(tabID);
+ checkWildcard(sectionID);
+
+ this.tabID = tabID;
+ this.sectionID = sectionID;
+ }
+
+ /**
+ * Create a wildcard path for all sections in a tab.
+ */
+ Path(String tabID) {
+ checkWildcard(tabID);
+
+ this.tabID = tabID;
+ this.sectionID = WILDCARD;
+ }
+
+ static void checkWildcard(String id) {
+ // Deliberately testing for identity of non-interned string
+ if((id == null) || (id == WILDCARD)) {
+ throw new IllegalArgumentException("Attempt to create a wildcard path explicitly"); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("(%s, %s)", tabID, sectionID); //$NON-NLS-1$
+ }
+
+ @Override
+ public int hashCode() {
+ // This isn't great for hash-map performance, but wildcards have to have the same hash as the keys they
+ // match, otherwise using them to access the map won't work
+ return tabID.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(!(obj instanceof Path)) {
+ return false;
+ }
+
+ Path other = (Path)obj;
+ return equals(tabID, other.tabID) && equals(sectionID, other.sectionID);
+ }
+
+ private static boolean equals(String anID, String anotherID) {
+ // Deliberately testing for identity of non-interned string
+ return (anID == WILDCARD) || (anotherID == WILDCARD) || anID.equals(anotherID);
+ }
+
+ }
+
+}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/widgets/AbstractPropertyEditor.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/widgets/AbstractPropertyEditor.java
index 6c452ce7b4f..7e9fe1961cf 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/widgets/AbstractPropertyEditor.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/widgets/AbstractPropertyEditor.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,6 +9,8 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Thibault Le Ouay t.leouay@sherpa-eng.com - Add binding implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.widgets;
@@ -27,6 +29,8 @@ import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.Context;
import org.eclipse.papyrus.views.properties.contexts.Property;
import org.eclipse.papyrus.views.properties.modelelement.DataSource;
+import org.eclipse.papyrus.views.properties.modelelement.DataSourceChangedEvent;
+import org.eclipse.papyrus.views.properties.modelelement.IDataSourceListener;
import org.eclipse.papyrus.views.properties.runtime.ConfigurationManager;
import org.eclipse.papyrus.views.properties.util.PropertiesUtil;
import org.eclipse.swt.events.DisposeEvent;
@@ -52,6 +56,8 @@ public abstract class AbstractPropertyEditor implements IChangeListener, Customi
* The DataSource representing the semantic objects
*/
protected DataSource input;
+
+ private IDataSourceListener dataSourceListener;
protected boolean readOnly = false;
@@ -83,6 +89,7 @@ public abstract class AbstractPropertyEditor implements IChangeListener, Customi
protected IValidator modelValidator;
protected IConverter targetToModelConverter;
+
/**
* Indicates if the editor's label should be displayed
*/
@@ -299,8 +306,22 @@ public abstract class AbstractPropertyEditor implements IChangeListener, Customi
* @param input
*/
public void setInput(DataSource input) {
- this.input = input;
- checkInput();
+ final DataSource oldInput = this.input;
+ if(input != oldInput) {
+ if(oldInput != null) {
+ oldInput.removeDataSourceListener(getDataSourceListener());
+ }
+
+ this.input = input;
+
+ if(input != null) {
+ input.addDataSourceListener(getDataSourceListener());
+ }
+
+ // Only do this after attaching our listener so that it will be ahead of
+ // any ModelElements created for properties
+ checkInput();
+ }
}
/**
@@ -565,4 +586,36 @@ public abstract class AbstractPropertyEditor implements IChangeListener, Customi
return modelValidator;
}
+ private IDataSourceListener getDataSourceListener() {
+ if(dataSourceListener == null) {
+ dataSourceListener = new IDataSourceListener() {
+
+ public void dataSourceChanged(DataSourceChangedEvent event) {
+ // The data source's selection changed. Update my validator or clear it if now there is none
+ IObservableValue observable = AbstractPropertyEditor.this.observableValue;
+
+ if((observable != null) && (modelValidator != null) && (valueEditor != null) && !valueEditor.isDisposed()) {
+ modelValidator = null;
+
+ // First, clear the validator to disable validation
+ valueEditor.setStrategies();
+ valueEditor.setModelValidator(null);
+
+ // Then re-enable to later when ready for user input
+ observable.getRealm().asyncExec(new Runnable() {
+
+ public void run() {
+ if((valueEditor != null) && !valueEditor.isDisposed()) {
+ valueEditor.setStrategies();
+ valueEditor.setModelValidator(getValidator());
+ }
+ }
+ });
+ }
+ }
+ };
+ }
+
+ return dataSourceListener;
+ }
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSection.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSection.java
index 074caeaf9e0..c282fcc67aa 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSection.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSection.java
@@ -9,6 +9,7 @@
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 435420
+ * Christian W. Damus (CEA) - bug 417409
*
*****************************************************************************/
package org.eclipse.papyrus.views.properties.xwt;
@@ -116,15 +117,18 @@ public class XWTSection extends AbstractPropertySection implements IChangeListen
}
private void setSource(DataSource source) {
- if(this.source != null) {
- this.source.removeChangeListener(this);
- this.source.dispose();
- }
+ final DataSource oldSource = this.source;
+
+ if(oldSource != source) {
+ if(oldSource != null) {
+ oldSource.removeChangeListener(this);
+ }
- this.source = source;
+ this.source = source;
- if(section.getConstraints().size() > 0) {
- source.addChangeListener(this);
+ if(section.getConstraints().size() > 0) {
+ source.addChangeListener(this);
+ }
}
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSectionDescriptor.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSectionDescriptor.java
index 17161e129b9..1907ddffdb4 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSectionDescriptor.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTSectionDescriptor.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,6 +8,8 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.xwt;
@@ -73,5 +75,35 @@ public class XWTSectionDescriptor extends AbstractSectionDescriptor {
return true;
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + System.identityHashCode(display);
+ result = prime * result + System.identityHashCode(section);
+ result = prime * result + System.identityHashCode(view);
+ return result;
+ }
+
+ /**
+ * XWT section descriptors are equal if they have the same (identical) references to the section and view from the property-sheet model
+ * and are associated with the same display engine.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ boolean result;
+
+ if(this == obj) {
+ result = true;
+ } else if((obj == null) || (obj.getClass() != this.getClass())) {
+ result = false;
+ } else {
+ XWTSectionDescriptor other = (XWTSectionDescriptor)obj;
+
+ result = (other.section == this.section) && (other.view == this.view) && (other.display == this.display);
+ }
+
+ return result;
+ }
}
diff --git a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTTabDescriptor.java b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTTabDescriptor.java
index d4a4b84f240..0c7ed473d80 100644
--- a/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTTabDescriptor.java
+++ b/plugins/views/properties/org.eclipse.papyrus.views.properties/src/org/eclipse/papyrus/views/properties/xwt/XWTTabDescriptor.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 CEA LIST.
+ * Copyright (c) 2010, 2014 CEA LIST and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,9 +8,13 @@
*
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 417409
+ *
*****************************************************************************/
package org.eclipse.papyrus.views.properties.xwt;
+import java.util.List;
+
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.contexts.Section;
import org.eclipse.papyrus.views.properties.contexts.Tab;
@@ -104,4 +108,37 @@ public class XWTTabDescriptor extends AbstractTabDescriptor {
public int getPriority() {
return tab.getPriority();
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ List<?> sectionDescriptors = getSectionDescriptors();
+ result = prime * result + ((tab == null) ? 0 : tab.hashCode());
+ result = prime * result + ((sectionDescriptors == null) ? 0 : sectionDescriptors.hashCode());
+ return result;
+ }
+
+ /**
+ * XWT tab descriptors are equal if they have the same ID and an equal list (in order) of section descriptors.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ boolean result;
+
+ if(this == obj) {
+ result = true;
+ } else if((obj == null) || (obj.getClass() != this.getClass())) {
+ result = false;
+ } else {
+ XWTTabDescriptor other = (XWTTabDescriptor)obj;
+
+ result = (other.getId() == null) ? this.getId() == null : (other.getId().equals(this.getId()));
+ if(result) {
+ result = (other.getSectionDescriptors() == null) ? this.getSectionDescriptors() == null : other.getSectionDescriptors().equals(this.getSectionDescriptors());
+ }
+ }
+
+ return result;
+ }
}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF
index 77189134d18..d1822070f3f 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/META-INF/MANIFEST.MF
@@ -9,6 +9,11 @@ Require-Bundle: org.junit;bundle-version="4.10.0",
org.eclipse.papyrus.junit.framework;bundle-version="1.0.0",
org.eclipse.papyrus.infra.tools;bundle-version="1.0.0",
org.eclipse.swt;bundle-version="3.100.0",
- org.eclipse.core.runtime;bundle-version="3.10.0"
-Export-Package: org.eclipse.papyrus.infra.tools.tests,
+ org.eclipse.core.runtime;bundle-version="3.10.0",
+ org.eclipse.core.databinding;bundle-version="1.4.0",
+ org.eclipse.core.databinding.beans;bundle-version="1.2.0",
+ org.eclipse.core.databinding.property;bundle-version="1.4.0",
+ com.google.guava;bundle-version="11.0.0"
+Export-Package: org.eclipse.papyrus.infra.tools.databinding,
+ org.eclipse.papyrus.infra.tools.tests,
org.eclipse.papyrus.infra.tools.util
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java
new file mode 100644
index 00000000000..a5ca68c11cb
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/AllDataBindingTests.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+
+/**
+ * The test suite for data-bindings API.
+ */
+@RunWith(Suite.class)
+@SuiteClasses({ DelegatingObservableValueTest.class, DelegatingObservableSetTest.class, DelegatingObservableListTest.class })
+public class AllDataBindingTests {
+
+ private AllDataBindingTests() {
+ super();
+ }
+
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollectionTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollectionTest.java
new file mode 100644
index 00000000000..54dee6a6997
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollectionTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+
+/**
+ * Abstract test framework for {@link DelegatingObservableCollection}s of various kinds.
+ */
+public abstract class DelegatingObservableCollectionTest<T extends IObservableCollection> extends DelegatingObservableTest<T> {
+
+ Collection<?> valuesToAdd;
+
+ Collection<?> valuesToRemove;
+
+ @TrackedGetterTest
+ @Test
+ public void testSize() {
+ assertThat(fixture.size(), is(delegate.size()));
+ addToDelegate();
+ assertThat(fixture.size(), is(delegate.size()));
+ removeFromDelegate();
+ assertThat(fixture.size(), is(delegate.size()));
+ delegate.clear();
+ assertThat(fixture.size(), is(0));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.size(), is(0));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testIsEmpty() {
+ assertThat(fixture.isEmpty(), is(true));
+ addToDelegate();
+ assertThat(fixture.isEmpty(), is(false));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.isEmpty(), is(true));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testContains() {
+ final Object o = aValueToAdd();
+
+ assertThat(fixture.contains(o), is(false));
+ addToDelegate();
+ assertThat(fixture.contains(o), is(true));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.contains(o), is(false));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testIterator() {
+ assertThat(fixture.iterator().hasNext(), is(false));
+ addToDelegate();
+ assertThat(ImmutableList.copyOf(iterator(fixture)), is(ImmutableList.copyOf(iterator(delegate))));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.iterator().hasNext(), is(false));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testToArray() {
+ assertThat(fixture.toArray().length, is(0));
+ addToDelegate();
+ assertThat(fixture.toArray(), isArray(delegate.toArray()));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.toArray().length, is(0));
+ }
+
+ @TrackedGetterTest
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testToArray_Array() {
+ assertThat(fixture.toArray(newArray()).length, is(0));
+
+ addToDelegate();
+ Object[] actual = fixture.toArray(newArray());
+ assertThat(actual.length, is(valuesToAdd.size()));
+ assertThat(actual, isArray(delegate.toArray(newArray())));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.toArray(newArray()).length, is(0));
+ }
+
+ @Test
+ public void testAdd() {
+ final Object o = aValueToAdd();
+
+ add(o);
+ assertThat(delegateCollection(), hasItem(o));
+ }
+
+ @Test
+ public void testRemove() {
+ final Object o = aValueToAdd();
+
+ addToDelegate();
+
+ remove(o);
+ assertThat(delegateCollection(), not(hasItem(o)));
+ }
+
+ @SuppressWarnings("unchecked")
+ @TrackedGetterTest
+ @Test
+ public void testContainsAll() {
+ addToDelegate();
+
+ assertThat(fixture.containsAll(valuesToAdd), is(true));
+ }
+
+ @Test
+ public void testAddAll() {
+ addAll();
+
+ assertThat(delegateCollection(), hasItems(valuesToAdd.toArray()));
+ }
+
+ @Test
+ public void testRemoveAll() {
+ addToDelegate();
+
+ assertThat(delegateCollection(), hasItems(valuesToRemove.toArray()));
+
+ removeAll();
+ assertThat(delegateCollection(), not(hasItem(in(valuesToRemove))));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testRetainAll() {
+ addToDelegate();
+
+ fixture.retainAll(valuesToRemove);
+
+ assertThat(delegateCollection(), hasItems(valuesToRemove.toArray()));
+ assertThat(delegate.size(), is(valuesToRemove.size())); // Nothing else
+ }
+
+ @Test
+ public void testClear() {
+ addAll();
+
+ assertThat(delegate.isEmpty(), is(false));
+
+ fixture.clear();
+
+ assertThat(delegate.isEmpty(), is(true));
+ }
+
+ /**
+ * Test method for {@link org.eclipse.papyrus.infra.tools.databinding.DelegatingObservableCollection#getElementType()}.
+ */
+ @Test
+ public void testGetElementType() {
+ assertThat(fixture.getElementType(), is((Object)getDelegateElementType()));
+
+ // Edge case
+ delegator().setDelegate(null);
+ assertThat(fixture.getElementType(), nullValue());
+ }
+
+ // This is a tracked getter for collections
+ @TrackedGetterTest
+ @Test
+ public void testEquals() {
+ super.testEquals();
+ }
+
+ // This is a tracked getter for collections
+ @TrackedGetterTest
+ @Test
+ public void testHashCode() {
+ super.testHashCode();
+ }
+
+ //
+ // Test framework
+ //
+
+ protected abstract Class<?> getDelegateElementType();
+
+ Object[] newArray() {
+ return (Object[])Array.newInstance(getDelegateElementType(), 0);
+ }
+
+ Object aValueToAdd() {
+ // Don't just get the first one, because that's an edge
+ return Iterables.get(valuesToAdd, 1, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ void addToDelegate() {
+ delegate.addAll(valuesToAdd);
+ }
+
+ @SuppressWarnings("unchecked")
+ void add(Object o) {
+ fixture.add(o);
+ }
+
+ @SuppressWarnings("unchecked")
+ void addAll() {
+ fixture.addAll(valuesToAdd);
+ }
+
+ @SuppressWarnings("unchecked")
+ void removeFromDelegate() {
+ delegate.removeAll(valuesToRemove);
+ }
+
+ void remove(Object o) {
+ fixture.remove(o);
+ }
+
+ @SuppressWarnings("unchecked")
+ void removeAll() {
+ fixture.removeAll(valuesToRemove);
+ }
+
+ @SuppressWarnings("unchecked")
+ <E> Collection<E> delegateCollection() {
+ return (Collection<E>)delegate;
+ }
+
+ Iterator<?> iterator(IObservableCollection c) {
+ return c.iterator();
+ }
+
+ Matcher<Object[]> isArray(final Object[] array) {
+ return new BaseMatcher<Object[]>() {
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("equal to array ").appendValueList("[", ", ", "]", array);
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ return (item instanceof Object[]) && Arrays.equals((Object[])item, array);
+ }
+ };
+ }
+
+ Matcher<Object> in(final Collection<?> extent) {
+ return new BaseMatcher<Object>() {
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("contained in ").appendValueList("[", ", ", "]", extent);
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ return extent.contains(item);
+ }
+ };
+ }
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableListTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableListTest.java
new file mode 100644
index 00000000000..0259388e8df
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableListTest.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+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.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+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.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+
+/**
+ * Test suite for both the simple and the detail variants of the {@link DelegatingObservableList} class.
+ */
+@RunWith(Enclosed.class)
+public abstract class DelegatingObservableListTest extends DelegatingObservableCollectionTest<IObservableList> {
+
+ @Test
+ public void testAddListChangeListener() {
+ class ListListener implements IListChangeListener {
+
+ ListDiff diff;
+
+ List<Object> additions() {
+ List<Object> result = Lists.newArrayList();
+
+ for(ListDiffEntry next : diff.getDifferences()) {
+ if(next.isAddition()) {
+ result.add(next.getElement());
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public void handleListChange(ListChangeEvent event) {
+ assertThat(event.getObservable(), sameInstance((IObservable)fixture));
+
+ diff = event.diff;
+ }
+ }
+
+ ListListener l = new ListListener();
+
+ fixture.addListChangeListener(l);
+
+ doChange();
+
+ assertThat(l.diff, notNullValue());
+ assertThat(l.additions(), hasItems(valuesToAdd.toArray()));
+
+ l.diff = null;
+
+ fixture.removeListChangeListener(l);
+
+ delegate.clear();
+
+ assertThat(l.diff, nullValue()); // We weren't listening
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testAdd_index() {
+ addToDelegate();
+
+ fixture.add(1, "Edith");
+
+ List<Object> expected = Lists.newArrayList(valuesToAdd);
+ expected.add(1, "Edith");
+
+ assertThat(delegateList(), is(expected));
+ }
+
+ @Test
+ public void testAddAll_index() {
+ addToDelegate();
+
+ List<String> newNames = ImmutableList.of("Edit", "Frank");
+ fixture.addAll(1, newNames);
+
+ List<Object> expected = Lists.newArrayList(valuesToAdd);
+ expected.addAll(1, newNames);
+
+ assertThat(delegateList(), is(expected));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testGet_index() {
+ addToDelegate();
+
+ assertThat(fixture.get(2), is((Object)"Cathy"));
+ }
+
+ @Test
+ public void testSet_index() {
+ addToDelegate();
+
+ assertThat(fixture.set(2, "Colleen"), is((Object)"Cathy"));
+ assertThat(delegate.get(2), is((Object)"Colleen"));
+ }
+
+ @Test
+ public void testMove() {
+ addToDelegate();
+
+ fixture.move(2, 1);
+ assertThat(delegateList(), is((List<Object>)ImmutableList.<Object> of("Alice", "Cathy", "Bert", "Dave")));
+ }
+
+ @Test
+ public void testRemove_int() {
+ addToDelegate();
+
+ fixture.remove(2);
+
+ List<Object> expected = Lists.newArrayList(valuesToAdd);
+ expected.remove(2);
+
+ assertThat(delegateList(), is(expected));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testIndexOf() {
+ addToDelegate();
+
+ assertThat(fixture.indexOf("Cathy"), is(2));
+ }
+
+ @SuppressWarnings("unchecked")
+ @TrackedGetterTest
+ @Test
+ public void testLastIndexOf() {
+ addToDelegate();
+
+ delegate.add(3, "Bert"); // Already have a Bert at index 1
+
+ assertThat(fixture.lastIndexOf("Bert"), is(3));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testListIterator() {
+ addToDelegate();
+
+ @SuppressWarnings("unchecked")
+ ListIterator<Object> iter = fixture.listIterator();
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is((Object)"Alice"));
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is((Object)"Bert"));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testListIterator_index() {
+ addToDelegate();
+
+ @SuppressWarnings("unchecked")
+ ListIterator<Object> iter = fixture.listIterator(delegate.size());
+ assertThat(iter.hasPrevious(), is(true));
+ assertThat(iter.previous(), is((Object)"Dave"));
+ assertThat(iter.hasPrevious(), is(true));
+ assertThat(iter.previous(), is((Object)"Cathy"));
+ }
+
+ @TrackedGetterTest
+ @Test
+ public void testSubList() {
+ addToDelegate();
+
+ @SuppressWarnings("unchecked")
+ List<Object> subList = fixture.subList(1, 3);
+
+ assertThat(subList, is((List<Object>)ImmutableList.<Object> of("Bert", "Cathy")));
+ }
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected Class<?> getDelegateElementType() {
+ return String.class;
+ }
+
+ @Override
+ protected void doChange() {
+ addAll();
+ }
+
+ List<Object> delegateList() {
+ return (List<Object>)delegateCollection();
+ }
+
+ //
+ // Test suites
+ //
+
+ @RunWith(RealmRunner.class)
+ public static class ListTest extends DelegatingObservableListTest {
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected Class<? extends IObservableList> getDelegateType() {
+ return WritableList.class;
+ }
+
+ @Override
+ protected void preCreateFixture() {
+ houseKeeper.setField("valuesToAdd", ImmutableList.of("Alice", "Bert", "Cathy", "Dave"));
+ houseKeeper.setField("valuesToRemove", ImmutableList.of("Bert", "Cathy"));
+ }
+
+ @Override
+ protected IObservableList createDelegate() {
+ return new WritableList(realm, Lists.newArrayList(), String.class);
+ }
+ }
+
+ @RunWith(RealmRunner.class)
+ public static class ObservingTest extends DelegatingObservableListTest {
+
+ private ListOfNames list;
+
+ private IObservableValue master;
+
+ @Test
+ public void testGetObserved() {
+ assertThat(((IObserving)fixture).getObserved(), is((Object)list));
+ }
+
+ //
+ // Test framework
+ //
+
+ @SuppressWarnings("restriction")
+ @Override
+ protected Class<? extends IObservableList> getDelegateType() {
+ return org.eclipse.core.internal.databinding.beans.BeanObservableListDecorator.class;
+ }
+
+ @Override
+ protected void preCreateFixture() {
+ houseKeeper.setField("list", new ListOfNames());
+ houseKeeper.setField("master", houseKeeper.cleanUpLater(new WritableValue(realm, list, ListOfNames.class)));
+
+ houseKeeper.setField("valuesToAdd", ImmutableList.of("Alice", "Bert", "Cathy", "Dave"));
+ houseKeeper.setField("valuesToRemove", ImmutableList.of("Bert", "Cathy"));
+ }
+
+ @Override
+ protected IObservableList createDelegate() {
+ return BeansObservables.observeDetailList(master, "names", String.class);
+ }
+
+ @Override
+ protected IObservableList createNonObservingDelegate() {
+ return new WritableList(realm, Lists.newArrayList(), String.class);
+ }
+
+ public static class ListOfNames {
+
+ private final PropertyChangeSupport bean = new PropertyChangeSupport(this);
+
+ private List<String> names;
+
+ ListOfNames(String... initial) {
+ names = Lists.newArrayList(initial);
+ }
+
+ public String[] getNames() {
+ return names.toArray(new String[names.size()]);
+ }
+
+ public void setNames(String... name) {
+ final String[] oldValue = getNames();
+ this.names = Lists.newArrayList(name);
+ bean.firePropertyChange("names", oldValue, getNames());
+ }
+
+ public String getName(int index) {
+ return names.get(index);
+ }
+
+ public String setName(int index, String name) {
+ final String oldName = getName(index);
+ String result = names.set(index, name);
+ bean.fireIndexedPropertyChange("names", index, oldName, name);
+ return result;
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(listener);
+ }
+
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(propertyName, listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(propertyName, listener);
+ }
+ }
+ }
+
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSetTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSetTest.java
new file mode 100644
index 00000000000..39b5a73ee52
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSetTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObserving;
+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.set.WritableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+
+
+/**
+ * Test suite for both the simple and the detail variants of the {@link DelegatingObservableSet} class.
+ */
+@RunWith(Enclosed.class)
+public abstract class DelegatingObservableSetTest extends DelegatingObservableCollectionTest<IObservableSet> {
+
+ @Test
+ public void testAddRemoveSetChangeListener() {
+ class SetListener implements ISetChangeListener {
+
+ SetDiff diff;
+
+ @SuppressWarnings("unchecked")
+ Set<Object> additions() {
+ return (Set<Object>)diff.getAdditions();
+ }
+
+ @Override
+ public void handleSetChange(SetChangeEvent event) {
+ assertThat(event.getObservable(), sameInstance((IObservable)fixture));
+
+ diff = event.diff;
+ }
+ }
+
+ SetListener l = new SetListener();
+
+ fixture.addSetChangeListener(l);
+
+ doChange();
+
+ assertThat(l.diff, notNullValue());
+ assertThat(l.additions(), hasItems(valuesToAdd.toArray()));
+
+ l.diff = null;
+
+ fixture.removeSetChangeListener(l);
+
+ delegate.clear();
+
+ assertThat(l.diff, nullValue()); // We weren't listening
+ }
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected Class<?> getDelegateElementType() {
+ return String.class;
+ }
+
+ @Override
+ protected void doChange() {
+ addAll();
+ }
+
+ //
+ // Test suites
+ //
+
+ @RunWith(RealmRunner.class)
+ public static class SetTest extends DelegatingObservableSetTest {
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected Class<? extends IObservableSet> getDelegateType() {
+ return WritableSet.class;
+ }
+
+ @Override
+ protected void preCreateFixture() {
+ houseKeeper.setField("valuesToAdd", ImmutableList.of("Alice", "Bert", "Cathy", "Dave"));
+ houseKeeper.setField("valuesToRemove", ImmutableList.of("Bert", "Cathy"));
+ }
+
+ @Override
+ protected IObservableSet createDelegate() {
+ return new WritableSet(realm, Collections.EMPTY_SET, String.class);
+ }
+ }
+
+ @RunWith(RealmRunner.class)
+ public static class ObservingTest extends DelegatingObservableSetTest {
+
+ private SetOfStrings set;
+
+ private IObservableValue master;
+
+ @Test
+ public void testGetObserved() {
+ assertThat(((IObserving)fixture).getObserved(), is((Object)set));
+ }
+
+ //
+ // Test framework
+ //
+
+ @SuppressWarnings("restriction")
+ @Override
+ protected Class<? extends IObservableSet> getDelegateType() {
+ return org.eclipse.core.internal.databinding.beans.BeanObservableSetDecorator.class;
+ }
+
+ @Override
+ protected void preCreateFixture() {
+ houseKeeper.setField("set", new SetOfStrings());
+ houseKeeper.setField("master", houseKeeper.cleanUpLater(new WritableValue(realm, set, SetOfStrings.class)));
+
+ houseKeeper.setField("valuesToAdd", ImmutableList.of("Alice", "Bert", "Cathy", "Dave"));
+ houseKeeper.setField("valuesToRemove", ImmutableList.of("Bert", "Cathy"));
+ }
+
+ @Override
+ protected IObservableSet createDelegate() {
+ return BeansObservables.observeDetailSet(master, "strings", String.class);
+ }
+
+ @Override
+ protected IObservableSet createNonObservingDelegate() {
+ return new WritableSet(realm, Collections.EMPTY_SET, String.class);
+ }
+
+ public static class SetOfStrings {
+
+ private final PropertyChangeSupport bean = new PropertyChangeSupport(this);
+
+ private Set<String> strings;
+
+ SetOfStrings(String... initial) {
+ strings = Sets.newHashSet(initial);
+ }
+
+ public Set<String> getStrings() {
+ return strings;
+ }
+
+ public void setStrings(Set<String> strings) {
+ // Don't have to copy the set because we replace it
+ final Set<String> oldStrings = Collections.unmodifiableSet(getStrings());
+ this.strings = strings;
+ bean.firePropertyChange("strings", oldStrings, Collections.unmodifiableSet(getStrings()));
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(listener);
+ }
+
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(propertyName, listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(propertyName, listener);
+ }
+ }
+ }
+
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableTest.java
new file mode 100644
index 00000000000..ffd6bac8d38
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+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.IObserving;
+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.IObservableList;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.papyrus.junit.utils.Duck;
+import org.eclipse.papyrus.junit.utils.rules.Condition;
+import org.eclipse.papyrus.junit.utils.rules.Conditional;
+import org.eclipse.papyrus.junit.utils.rules.HouseKeeper;
+import org.eclipse.papyrus.junit.utils.tests.AbstractPapyrusTest;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+
+/**
+ * Abstract test framework for {@link DelegatingObservable}s of various kinds.
+ */
+public abstract class DelegatingObservableTest<T extends IObservable> extends AbstractPapyrusTest {
+
+ @Rule
+ public final HouseKeeper houseKeeper = new HouseKeeper();
+
+ protected static Realm realm;
+
+ @ObservableFixture
+ public T fixture;
+
+ protected T delegate;
+
+ @Test
+ public void testWrap() {
+ assertThat(fixture, instanceOf(getFixtureType()));
+ }
+
+ @Test
+ public void testCreate() {
+ IObservable observable = houseKeeper.cleanUpLater(DelegatingObservable.create(realm, getInterface(getDelegateType())));
+ assertThat(observable, instanceOf(getFixtureType()));
+ }
+
+ @Test
+ public void testCreate_withObserving() {
+ IObservable observable = houseKeeper.cleanUpLater(DelegatingObservable.create(realm, getInterface(getDelegateType())));
+ assertThat(observable, instanceOf(getFixtureType()));
+
+ observable = houseKeeper.cleanUpLater(DelegatingObservable.create(realm, getInterface(getDelegateType()), loader(), IObserving.class));
+ assertThat(observable, instanceOf(getFixtureType()));
+ assertThat(observable, instanceOf(IObserving.class));
+ }
+
+ @Test
+ public void testSetDelegate() {
+ ChangeCounter counter = new ChangeCounter(fixture);
+
+ delegator().setDelegate(createObservable());
+
+ assertThat(counter.count(), is(1));
+ }
+
+ @Conditional(key = "isObserving")
+ @Test
+ public void testGetObserved() {
+ IObserving observing = (IObserving)fixture;
+ assertThat(observing.getObserved(), notNullValue());
+ assertThat(observing.getObserved(), is(((IObserving)delegate).getObserved()));
+ }
+
+ @Test
+ public void testAddRemoveChangeListener() {
+ ChangeCounter counter = new ChangeCounter(fixture);
+
+ doChange();
+
+ assertThat(counter.count(), is(1));
+
+ fixture.removeChangeListener(counter);
+
+ doChange();
+
+ assertThat(counter.count(), is(1)); // Wasn't listening for second change
+ }
+
+ @Conditional(key = "supportsStale")
+ @TrackedGetterTest
+ @Test
+ public void testIsStale() {
+ assertThat(fixture.isStale(), is(false));
+
+ new Duck(delegate).quack("setStale", true);
+
+ assertThat(fixture.isStale(), is(true));
+ }
+
+ @Conditional(key = "supportsStale")
+ @Test
+ public void testAddRemoveStaleListener() {
+ class StaleListener implements IStaleListener {
+
+ int count;
+
+ @Override
+ public void handleStale(StaleEvent staleEvent) {
+ count++;
+ assertThat(staleEvent.getObservable(), sameInstance((IObservable)fixture));
+ }
+ }
+
+ StaleListener l = new StaleListener();
+
+ fixture.addStaleListener(l);
+ new Duck(delegate).quack("setStale", true);
+
+ assertThat(l.count, is(1));
+ }
+
+ @Test
+ public void testAddDisposeListener() {
+ DisposeCounter counter = new DisposeCounter(fixture);
+
+ fixture.dispose();
+ fixture.dispose(); // Repeat
+
+ assertThat(counter.count, is(1)); // Dispose is idempotent
+ }
+
+ @Test
+ public void testDispose() {
+ fixture.dispose();
+
+ assertThat(delegator().getDelegate(), nullValue());
+
+ // Disposing the delegator does not dispose the delegate
+ assertThat(delegate.isDisposed(), is(false));
+ }
+
+ @Test
+ public void testAutoreleaseDelegate() {
+ DisposeCounter counter = new DisposeCounter(delegator().getDelegate());
+
+ delegator().setDelegate(null);
+
+ // Forcible drain the realm's auto-release pool. If this results in the
+ // delegate being disposed, then we'll know that it's because the delegator
+ // auto-released it and we just kicked its retain count to zero
+ ReferenceCountedObservable.AutoReleasePool.get(realm).release();
+
+ // The delegate's retain count should go to zero, resulting in disposal
+ assertThat(counter.count(), is(1));
+ }
+
+ @Test
+ public void testIsDisposed() {
+ assertThat(fixture.isDisposed(), is(false));
+
+ fixture.dispose();
+
+ assertThat(fixture.isDisposed(), is(true));
+ }
+
+ @Test
+ public void testGetRealm() {
+ assertThat(fixture.getRealm(), is(realm));
+ }
+
+ @Test
+ public void testEquals() {
+ assertThat(fixture, equalTo(delegate));
+ }
+
+ @Test
+ public void testHashCode() {
+ assertThat(fixture.hashCode(), is(delegate.hashCode()));
+ }
+
+ /**
+ * Tests the case where we create an observable that delegates to an observable that is, itself, a delegating observable.
+ */
+ @Test
+ public void testDelegatingToDelegator() {
+ IObservable redelegator = DelegatingObservable.wrap(fixture);
+ assertThat(redelegator, notNullValue());
+
+ // The wrapping delegator trivially is of the same class as its delegate
+ assertThat((Object)redelegator.getClass(), sameInstance((Object)fixture.getClass()));
+
+ // But, of course, it is a different instance
+ assertThat(redelegator, not(sameInstance((IObservable)fixture)));
+
+ // And it properly delegates
+ assertThat(((IDelegatingObservable)redelegator).getDelegate(), sameInstance((IObservable)fixture));
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createFixture() {
+ preCreateFixture();
+
+ houseKeeper.setField("delegate", createDelegate());
+ houseKeeper.setField("fixture", DelegatingObservable.wrap(delegate));
+ }
+
+ protected void preCreateFixture() {
+ // Pass
+ }
+
+ protected abstract Class<? extends T> getDelegateType();
+
+ protected abstract void doChange();
+
+ ClassLoader loader() {
+ return getClass().getClassLoader();
+ }
+
+ protected IDelegatingObservable delegator() {
+ return (IDelegatingObservable)fixture;
+ }
+
+ protected T createObservable() {
+ return houseKeeper.cleanUpLater(createDelegate());
+ }
+
+ protected T createDelegate() {
+ try {
+ return getDelegateType().getConstructor(Realm.class).newInstance(realm);
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Failed to create test delegate: " + e.getLocalizedMessage());
+ return null; // Unreachable
+ }
+ }
+
+ protected T createNonObservingDelegate() {
+ return createDelegate();
+ }
+
+ @Condition
+ public boolean isObserving() {
+ return IObserving.class.isAssignableFrom(getDelegateType());
+ }
+
+ @Condition
+ public boolean supportsStale() {
+ try {
+ return getDelegateType().getMethod("setStale", boolean.class) != null;
+ } catch (Exception e) {
+ // Clearly not supported
+ return false;
+ }
+ }
+
+ protected final Class<? extends IObservable> getFixtureType() {
+ Class<?> delegateType = getDelegateType();
+
+ if(IObservableSet.class.isAssignableFrom(delegateType)) {
+ return IObservableSet.class;
+ } else if(IObservableList.class.isAssignableFrom(delegateType)) {
+ return IObservableList.class;
+ } else if(IObservableValue.class.isAssignableFrom(delegateType)) {
+ return IObservableValue.class;
+ } else {
+ fail("Invalid delegate type: " + delegateType);
+ return null; // Unreachable
+ }
+ }
+
+ private static Class<? extends IObservable> getInterface(Class<? extends IObservable> instanceClass) {
+ Class<? extends IObservable> result = IObservable.class;
+
+ if(IObservableSet.class.isAssignableFrom(instanceClass)) {
+ result = IObservableSet.class;
+ } else if(IObservableList.class.isAssignableFrom(instanceClass)) {
+ result = IObservableList.class;
+ } else if(IObservableValue.class.isAssignableFrom(instanceClass)) {
+ result = IObservableValue.class;
+ }
+
+ return result;
+ }
+
+ protected class ChangeCounter implements IChangeListener {
+
+ private final IObservable expectedSource;
+
+ protected ChangeCounter(IObservable observable) {
+ expectedSource = observable;
+ observable.addChangeListener(this);
+ }
+
+ private int count;
+
+ int count() {
+ return count;
+ }
+
+ @Override
+ public void handleChange(ChangeEvent event) {
+ count++;
+ assertThat(event.getObservable(), sameInstance(expectedSource));
+ }
+ }
+
+ protected class DisposeCounter implements IDisposeListener {
+
+ private final IObservable expectedSource;
+
+ protected DisposeCounter(IObservable observable) {
+ expectedSource = observable;
+ observable.addDisposeListener(this);
+ }
+
+ private int count;
+
+ int count() {
+ return count;
+ }
+
+ @Override
+ public void handleDispose(DisposeEvent event) {
+ count++;
+ assertThat(event.getObservable(), sameInstance(expectedSource));
+ }
+ }
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValueTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValueTest.java
new file mode 100644
index 00000000000..41207fdb7e3
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValueTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObserving;
+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.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Test suite for both the simple and the detail variants of the {@link DelegatingObservableValue} class.
+ */
+@RunWith(Enclosed.class)
+public abstract class DelegatingObservableValueTest extends DelegatingObservableTest<IObservableValue> {
+
+ Bean bean;
+
+ Object expectedValue;
+
+ Object valueToSet;
+
+ @Test
+ public void testAddRemoveValueChangeListener() {
+ class TestListener implements IValueChangeListener {
+
+ int count;
+
+ @Override
+ public void handleValueChange(ValueChangeEvent event) {
+ assertThat(event.getObservable(), sameInstance((IObservable)fixture));
+
+ count++;
+ }
+ };
+
+ final TestListener l = new TestListener();
+
+ fixture.addValueChangeListener(l);
+ doChange();
+ assertThat(l.count, is(1));
+ fixture.removeValueChangeListener(l);
+ doChange();
+ assertThat(l.count, is(1)); // Not notified this time
+ }
+
+ @Test
+ public void testGetValueType() {
+ assertThat(fixture.getValueType(), is((Object)expectedValue.getClass()));
+ }
+
+ @Test
+ public void testGetValue() {
+ assertExpectedValue();
+ }
+
+ @Test
+ public void testSetValue() {
+ doChange();
+ assertExpectedValue();
+ }
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected void doChange() {
+ fixture.setValue(valueToSet);
+
+ Object swap = valueToSet;
+ valueToSet = expectedValue;
+ expectedValue = swap;
+ }
+
+ void assertExpectedValue() {
+ assertThat(fixture.getValue(), is(expectedValue));
+ }
+
+ //
+ // Test suites
+ //
+
+ @RunWith(RealmRunner.class)
+ public static class ValueTest extends DelegatingObservableValueTest {
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected Class<? extends IObservableValue> getDelegateType() {
+ return WritableValue.class;
+ }
+
+ @Override
+ protected void preCreateFixture() {
+ houseKeeper.setField("bean", new Bean("Alice"));
+
+ houseKeeper.setField("expectedValue", bean);
+ houseKeeper.setField("valueToSet", new Bean("Bert"));
+ }
+
+ @Override
+ protected IObservableValue createDelegate() {
+ return new WritableValue(realm, bean, Bean.class);
+ }
+ }
+
+ @RunWith(RealmRunner.class)
+ public static class ObservingTest extends DelegatingObservableValueTest {
+
+ private IObservableValue master;
+
+ @Test
+ public void testGetObserved() {
+ assertThat(((IObserving)fixture).getObserved(), is((Object)bean));
+ }
+
+ //
+ // Test framework
+ //
+
+ @SuppressWarnings("restriction")
+ @Override
+ protected Class<? extends IObservableValue> getDelegateType() {
+ return org.eclipse.core.internal.databinding.beans.BeanObservableValueDecorator.class;
+ }
+
+ @Override
+ protected void preCreateFixture() {
+ houseKeeper.setField("bean", new Bean("Alice"));
+ houseKeeper.setField("master", houseKeeper.cleanUpLater(new WritableValue(realm, bean, Bean.class)));
+
+ houseKeeper.setField("expectedValue", "Alice");
+ houseKeeper.setField("valueToSet", "Bert");
+ }
+
+ @Override
+ protected IObservableValue createDelegate() {
+ return BeansObservables.observeDetailValue(master, "name", String.class);
+ }
+
+ @Override
+ protected IObservableValue createNonObservingDelegate() {
+ return new WritableValue(realm, bean, Bean.class);
+ }
+ }
+}
+
+
+class Bean {
+
+ private final PropertyChangeSupport bean = new PropertyChangeSupport(this);
+
+ private String name;
+
+ Bean(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ final String oldName = getName();
+ this.name = name;
+ bean.firePropertyChange("name", oldName, getName());
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(listener);
+ }
+
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(propertyName, listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(propertyName, listener);
+ }
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/ObservableFixture.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/ObservableFixture.java
new file mode 100644
index 00000000000..b7be55c0dff
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/ObservableFixture.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotates the observable fixture field that is covered by a test method testing a {@linkplain TrackedGetterTest tracked getter}.
+ *
+ * @see TrackedGetterTest
+ * @see RealmRunner
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ObservableFixture {
+ // Empty annotation
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java
new file mode 100644
index 00000000000..4d92a3d69e6
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/RealmRunner.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.ObservableTracker;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.papyrus.junit.utils.classification.ClassificationRunner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+
+/**
+ * A custom test runner that runs tests in the thread (which is not the SWT UI thread!) of a data-bindings realm specially
+ * designed for the purpose of JUnit testing.
+ */
+public class RealmRunner extends ClassificationRunner {
+
+ public RealmRunner(Class<?> klass) throws InitializationError {
+ super(klass);
+ }
+
+ @Override
+ protected Statement classBlock(RunNotifier notifier) {
+ final Statement base = super.classBlock(notifier);
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ DelegatingObservableTest.realm = new TestRealm();
+ try {
+ base.evaluate();
+ } finally {
+ ((TestRealm)DelegatingObservableTest.realm).dispose();
+ DelegatingObservableTest.realm = null;
+ }
+ }
+ };
+ }
+
+ void basicRunChild(FrameworkMethod method, RunNotifier notifier) {
+ super.runChild(method, notifier);
+ }
+
+ /**
+ * Schedules a test to run asynchronously on the realm's thread, waiting for it to finish there.
+ */
+ @Override
+ protected void runChild(final FrameworkMethod method, final RunNotifier notifier) {
+ AsyncRunnable run = new AsyncRunnable() {
+
+ @Override
+ protected void doRun() {
+ basicRunChild(method, notifier);
+ }
+ };
+
+ DelegatingObservableTest.realm.exec(run);
+ run.await();
+ }
+
+ @Override
+ protected Statement methodInvoker(FrameworkMethod method, final Object test) {
+ final Statement base = super.methodInvoker(method, test);
+ Statement result = base;
+
+ if(method.getAnnotation(TrackedGetterTest.class) != null) {
+ result = new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ AsyncRunnable run = new AsyncRunnable() {
+
+ @Override
+ protected void doRun() throws Throwable {
+ base.evaluate();
+ }
+ };
+
+ // Don't actually need to listen to the tracked observables, just to collect them
+ IObservable[] tracked = ObservableTracker.runAndMonitor(run, null, null);
+
+ // This was not actually run asynchronously
+ run.throwCaughtThrowable();
+
+ IObservable observable = getObservableFixture(test);
+
+ boolean found = false;
+ for(int i = 0; !found && (i < tracked.length); i++) {
+ found = tracked[i] == observable;
+ }
+ assertThat("Observable fixture did not invoke ObservableTracker::getterCalled()", found, is(true));
+ }
+ };
+ }
+
+ return result;
+ }
+
+ IObservable getObservableFixture(Object test) {
+ List<IObservable> list = getTestClass().getAnnotatedFieldValues(test, ObservableFixture.class, IObservable.class);
+
+ assertThat("No @ObservableFixture field found in test class.", list.isEmpty(), is(false));
+
+ return list.get(0);
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class TestRealm extends Realm {
+
+ private ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+
+ @Override
+ public Thread newThread(Runnable r) {
+ realmThread = new Thread(r, "Test Realm");
+ return realmThread;
+ }
+ });
+
+ private volatile Thread realmThread;
+
+ @Override
+ public boolean isCurrent() {
+ return Thread.currentThread() == realmThread;
+ }
+
+ @Override
+ public void asyncExec(Runnable runnable) {
+ executor.execute(runnable);
+ }
+
+ void dispose() {
+ if(executor != null) {
+ executor.shutdown();
+ executor = null;
+ realmThread = null;
+ }
+ }
+ }
+
+ static abstract class AsyncRunnable implements Runnable {
+
+ private final CountDownLatch latch = new CountDownLatch(1);
+
+ private Throwable thrown;
+
+ @Override
+ public void run() {
+ try {
+ doRun();
+ } catch (Throwable e) {
+ thrown = e;
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ protected abstract void doRun() throws Throwable;
+
+ public void await() {
+ try {
+ latch.await();
+ } catch (Exception e) {
+ fail(e.getLocalizedMessage());
+ }
+
+ if(thrown instanceof Error) {
+ throw (Error)thrown;
+ } else if(thrown instanceof RuntimeException) {
+ throw (RuntimeException)thrown;
+ } else if(thrown instanceof Exception) {
+ throw new RuntimeException(thrown);
+ }
+ }
+
+ void throwCaughtThrowable() throws Throwable {
+ if(thrown != null) {
+ throw thrown;
+ }
+ }
+ };
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/TrackedGetterTest.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/TrackedGetterTest.java
new file mode 100644
index 00000000000..4a4a1bb9b65
--- /dev/null
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/databinding/TrackedGetterTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 CEA and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.eclipse.core.databinding.observable.ObservableTracker;
+
+
+/**
+ * Annotates a test as testing a tracked getter, so the {@code RealmRunner} will check that the test's observable fixture
+ * (as indicated by the {@link &#x40;ObservableFixture ObservableFixture} annotation) was reported to the {@link ObservableTracker}.
+ *
+ * @see ObservableFixture
+ * @see RealmRunner
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface TrackedGetterTest {
+ // Empty annotation
+}
diff --git a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/tests/AllTests.java b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/tests/AllTests.java
index c85893c795e..9768a84aa0d 100644
--- a/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/tests/AllTests.java
+++ b/tests/junit/plugins/infra/org.eclipse.papyrus.infra.tools.tests/src/org/eclipse/papyrus/infra/tools/tests/AllTests.java
@@ -12,6 +12,7 @@
*/
package org.eclipse.papyrus.infra.tools.tests;
+import org.eclipse.papyrus.infra.tools.databinding.AllDataBindingTests;
import org.eclipse.papyrus.infra.tools.util.UIUtilTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -22,7 +23,7 @@ import org.junit.runners.Suite.SuiteClasses;
* The master test suite for the plug-in.
*/
@RunWith(Suite.class)
-@SuiteClasses({ UIUtilTest.class })
+@SuiteClasses({ UIUtilTest.class, AllDataBindingTests.class })
public class AllTests {
public AllTests() {
diff --git a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/META-INF/MANIFEST.MF b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/META-INF/MANIFEST.MF
index 610a2475076..c37559cb966 100644
--- a/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/META-INF/MANIFEST.MF
+++ b/tests/junit/plugins/uml/diagram/org.eclipse.papyrus.uml.diagram.sequence.tests/META-INF/MANIFEST.MF
@@ -20,7 +20,8 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.papyrus.uml.appearance;bundle-version="1.0.0",
org.eclipse.papyrus.infra.viewpoints.policy;bundle-version="1.0.0",
org.eclipse.papyrus.junit.framework;bundle-version="1.0.0",
- org.eclipse.papyrus.junit.utils;bundle-version="1.0.0"
+ org.eclipse.papyrus.junit.utils;bundle-version="1.0.0",
+ org.eclipse.papyrus.views.properties;bundle-version="1.0.0"
Export-Package: org.eclipse.papyrus.uml.diagram.sequence.tests
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy

Back to the top