Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2016-02-11 02:48:20 +0000
committerGerrit Code Review @ Eclipse.org2016-02-12 15:31:41 +0000
commitecd4928b327f5561364c5068c9ff5f1668e92d13 (patch)
tree7c34f46cf82a1d65ac753fa92c2a5d55371b8dba /plugins/infra/core
parent751a204d74e15eb2db6b41c937691fc56dcc1252 (diff)
downloadorg.eclipse.papyrus-ecd4928b327f5561364c5068c9ff5f1668e92d13.tar.gz
org.eclipse.papyrus-ecd4928b327f5561364c5068c9ff5f1668e92d13.tar.xz
org.eclipse.papyrus-ecd4928b327f5561364c5068c9ff5f1668e92d13.zip
Bug 485220: [Architecture] Provide a more modular architecture
https://bugs.eclipse.org/bugs/show_bug.cgi?id=485220 Factor UI dependencies out of the UML Element Types bundle. This includes moving some advices that interact with the user into a new org.eclipse.papyrus.uml.service.types.ui bundle. Pull up the PasteCommandService and IPasteCommandProvider API into the Infra Diagram layer where the extension point is defined. Deprecate the old API in the UML layer. Introduce a service for participation of languages in CSS styling: * styling reset actions in the Reset Style command * access to semantic model classes and properties to make available to CSS Factor PapyrusObservableValue and cohorts out of the UML Tools bundle into the Infra Layer for more general reuse and to relieve the Diagram Infrastructure layer of UML dependencies. The old API remains as deprecated. Remove the Infra Diagram Layer dependency on UML Layer for property testers governing deletion in the diagram. Includes introduction of a new IGraphicalDeletionHelper OSGi service for delegation of the determination of whether an element can be deleted from the diagram and replacement of the XML expression properties * org.eclipse.papyrus.uml.diagram.common.isSemanticDeletion * org.eclipse.papyrus.uml.diagram.common.isReadOnly by * org.eclipse.papyrus.infra.gmfdiag.common.isSemanticDeletion * org.eclipse.papyrus.infra.gmfdiag.common.canDelete (where the latter is the negation of the property that it supersedes) Extract UML dependencies from the Diagram Outline and CSS Editor bundles. Remove unused MDTUtil APIs that referenced a UML-specific annotation. Move the Diagram Infrastructure CSS Palette bundle into the UML layer because it serves to provide extensions on the Palette Service, which is an overtly UML-specific capability. All client APIs for the Properties View are moved from org.eclipse.papyrus.views.properties bundle to a new org.eclipse.papyrus.infra.properties.ui bundle. This includes renaming of: * extension points * label-provider contexts * XWT namespaces Add an "all UI tests" suite. Define a componentized hierarchical build layout of the main plug-ins Change-Id: I43f8f3644857a18b69715f5a2f1da9b1cf286d67
Diffstat (limited to 'plugins/infra/core')
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.log/pom.xml6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.pluginexplorer/pom.xml6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor.di/pom.xml6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/pom.xml6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core.sashwindows.di/pom.xml6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml239
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/schema/model.exsd7
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/AbstractModelWithSharedResource.java510
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/EMFLogicalModel.java20
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/IEMFModel.java144
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java27
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelsReader.java56
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/sasheditor/SashModel.java752
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ExtensionServicesRegistry.java31
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java381
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusCallable.java49
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusRunnable.java49
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IServiceRegistryProvider.java30
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/ServiceUtils.java114
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java982
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/.classpath7
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/.project28
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.core.prefs291
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.ui.prefs68
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF22
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/OSGI-INF/l10n/bundle.properties3
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/about.html28
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/build.properties7
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/pom.xml12
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/Activator.java93
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/DelegatingUIExecutorService.java235
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/comparator/CompositeComparator.java63
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/AggregatedObservable.java47
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservable.java27
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservableValue.java19
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java174
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java347
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java131
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java205
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java128
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java140
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java52
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IMultipleObservableValue.java37
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java181
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java381
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java73
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java288
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/IContext.java65
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotification.java32
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotificationBuilder.java132
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/LogNotification.java56
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationBuilder.java388
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationRunnable.java36
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/Type.java29
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/IExecutorServiceFactory.java25
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/INotificationBuilderFactory.java26
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/BooleanHelper.java38
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ClassLoaderHelper.java152
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CompositeServiceTracker.java92
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CoreExecutors.java43
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/FileUtils.java129
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IExecutorService.java107
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressCallable.java68
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressRunnable.java61
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IntegerAndSpreadsheetNumberConverter.java90
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterables2.java66
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterators2.java59
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ListHelper.java81
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/PlatformHelper.java141
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReferenceCounted.java175
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReflectHelper.java55
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/StringHelper.java174
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Suppliers2.java132
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypeUtils.java185
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypesConstants.java38
-rw-r--r--plugins/infra/core/pom.xml23
77 files changed, 7696 insertions, 1516 deletions
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.log/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core.log/pom.xml
index b5d2bdfd90f..11b1b68b60c 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.log/pom.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.log/pom.xml
@@ -2,13 +2,11 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>org.eclipse.papyrus.releng</artifactId>
<groupId>org.eclipse.papyrus</groupId>
- <version>1.2.0-SNAPSHOT</version>
- <relativePath>../../../../releng/main</relativePath>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.core.log</artifactId>
- <groupId>org.eclipse.papyrus</groupId>
<version>1.2.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.pluginexplorer/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core.pluginexplorer/pom.xml
index 0b735aae0d9..b21f8997b31 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.pluginexplorer/pom.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.pluginexplorer/pom.xml
@@ -2,13 +2,11 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>org.eclipse.papyrus.releng</artifactId>
<groupId>org.eclipse.papyrus</groupId>
- <version>1.2.0-SNAPSHOT</version>
- <relativePath>../../../../releng/main</relativePath>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.core.pluginexplorer</artifactId>
- <groupId>org.eclipse.papyrus</groupId>
<version>1.2.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor.di/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor.di/pom.xml
index 3f885ce4530..1daf5ae6e56 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor.di/pom.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor.di/pom.xml
@@ -2,13 +2,11 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>org.eclipse.papyrus.releng</artifactId>
<groupId>org.eclipse.papyrus</groupId>
- <version>1.2.0-SNAPSHOT</version>
- <relativePath>../../../../releng/main</relativePath>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.core.sasheditor.di</artifactId>
- <groupId>org.eclipse.papyrus</groupId>
<version>1.2.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/pom.xml
index e9f1d27360c..57ef0408764 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/pom.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.sasheditor/pom.xml
@@ -2,13 +2,11 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>org.eclipse.papyrus.releng</artifactId>
<groupId>org.eclipse.papyrus</groupId>
- <version>1.2.0-SNAPSHOT</version>
- <relativePath>../../../../releng/main</relativePath>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.core.sasheditor</artifactId>
- <groupId>org.eclipse.papyrus</groupId>
<version>1.2.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core.sashwindows.di/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core.sashwindows.di/pom.xml
index 8e67e56cd32..f4d64d319a9 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core.sashwindows.di/pom.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core.sashwindows.di/pom.xml
@@ -2,13 +2,11 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>org.eclipse.papyrus.releng</artifactId>
<groupId>org.eclipse.papyrus</groupId>
- <version>1.2.0-SNAPSHOT</version>
- <relativePath>../../../../releng/main</relativePath>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.core.sashwindows.di</artifactId>
- <groupId>org.eclipse.papyrus</groupId>
<version>1.2.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml
index a7a4780781c..cb8e66e2229 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml
@@ -1,119 +1,120 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.2"?>
-<plugin>
- <extension-point id="modelListener" name="ModelListener" schema="schema/modelListener.exsd"/>
- <extension-point id="service" name="Service" schema="schema/service.exsd"/>
- <extension-point id="model" name="plugin.xml.ModelName" schema="schema/model.exsd"/>
- <extension-point id="transactionalEditingDomainProvider" name="transactionalEditingDomainProvider" schema="schema/transactionalEditingDomainProvider.exsd"/>
- <extension-point id="sashModelProvider" name="Sash Model Providers" schema="schema/sashModelProvider.exsd"/>
- <extension-point id="language" name="Modeling Language" schema="schema/language.exsd"/>
-
- <extension
- point="org.eclipse.papyrus.infra.core.model">
- <model
- classname="org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel"
- description="Model for sash system"
- fileExtension="sash">
- </model>
- <modelSetSnippet
- classname="org.eclipse.papyrus.infra.core.modelsetquery.impl.ModelSetQueryInitializer"
- description="Adapter attached to the ModelSet (aka ResourceSet)">
- </modelSetSnippet>
- <model
- classname="org.eclipse.papyrus.infra.core.resource.sasheditor.DiModel"
- description="Main Papyrus IModel"
- fileExtension="di">
- </model>
- </extension>
- <extension
- point="org.eclipse.papyrus.infra.core.service">
- <serviceFactory
- classname="org.eclipse.papyrus.infra.core.editor.ModelSetServiceFactory"
- description="The service loading Models"
- id="org.eclipse.papyrus.infra.core.resource.ModelSet"
- priority="1"
- startKind="startup">
- </serviceFactory>
- <serviceFactory
- classname="org.eclipse.papyrus.infra.core.editor.DiResourceSetServiceFactory"
- description="An alias to ModelSet"
- id="org.eclipse.papyrus.infra.core.utils.DiResourceSet"
- priority="1"
- startKind="startup">
- <dependsOn
- serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
- </dependsOn>
- </serviceFactory>
- <serviceFactory
- classname="org.eclipse.papyrus.infra.core.resource.EditingDomainServiceFactory"
- description="TransactionalEditing Domain"
- id="org.eclipse.emf.transaction.TransactionalEditingDomain"
- priority="1"
- startKind="startup">
- <dependsOn
- serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
- </dependsOn>
- </serviceFactory>
- <serviceFactory
- classname="org.eclipse.papyrus.infra.core.resource.EditingDomainServiceFactory"
- description="Editing Domain"
- id="org.eclipse.emf.edit.domain.EditingDomain"
- priority="1"
- startKind="startup">
- <dependsOn
- serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
- </dependsOn>
- </serviceFactory>
- </extension>
- <extension
- id="org.eclipse.papyrus.modelmarker"
- name="model marker"
- point="org.eclipse.core.resources.markers">
- <attribute
- name="uri">
- </attribute>
- <persistent
- value="true">
- </persistent>
- </extension>
- <extension
- point="org.eclipse.core.runtime.adapters">
- <factory
- adaptableType="org.eclipse.papyrus.infra.core.resource.ModelSet"
- class="org.eclipse.papyrus.infra.core.services.ModelSetServiceAdapter$Factory">
- <adapter
- type="org.eclipse.papyrus.infra.core.services.IService">
- </adapter>
- </factory>
- </extension>
- <extension
- point="org.eclipse.papyrus.infra.core.service">
- <service
- classname="org.eclipse.papyrus.infra.core.internal.language.LanguageService"
- id="org.eclipse.papyrus.infra.core.language.ILanguageService"
- priority="10"
- startKind="startup">
- </service>
- <serviceFactory
- classname="org.eclipse.papyrus.infra.core.internal.sashmodel.BasicPageManagerServiceFactory"
- description="Default IPageManager service for headless operation."
- id="org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager"
- priority="1"
- startKind="startup">
- <dependsOn
- serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
- </dependsOn>
- </serviceFactory>
- </extension>
-
- <extension
- point="org.eclipse.core.expressions.propertyTesters">
- <propertyTester
- id="org.eclipse.papyrus.infra.core.serviceRegistryProperties"
- type="org.eclipse.papyrus.infra.core.services.ServicesRegistry"
- namespace="org.eclipse.papyrus.infra.core"
- properties="hasSemanticModel"
- class="org.eclipse.papyrus.infra.core.internal.expressions.ServiceRegistryPropertyTester">
- </propertyTester>
- </extension>
-</plugin>
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension-point id="modelListener" name="ModelListener" schema="schema/modelListener.exsd"/>
+ <extension-point id="service" name="Service" schema="schema/service.exsd"/>
+ <extension-point id="model" name="plugin.xml.ModelName" schema="schema/model.exsd"/>
+ <extension-point id="transactionalEditingDomainProvider" name="transactionalEditingDomainProvider" schema="schema/transactionalEditingDomainProvider.exsd"/>
+ <extension-point id="sashModelProvider" name="Sash Model Providers" schema="schema/sashModelProvider.exsd"/>
+ <extension-point id="language" name="Modeling Language" schema="schema/language.exsd"/>
+
+ <extension
+ point="org.eclipse.papyrus.infra.core.model">
+ <model
+ classname="org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel"
+ description="Model for sash system"
+ fileExtension="sash">
+ </model>
+ <modelSetSnippet
+ classname="org.eclipse.papyrus.infra.core.modelsetquery.impl.ModelSetQueryInitializer"
+ description="Adapter attached to the ModelSet (aka ResourceSet)">
+ </modelSetSnippet>
+ <model
+ classname="org.eclipse.papyrus.infra.core.resource.sasheditor.DiModel"
+ description="Main Papyrus IModel"
+ fileExtension="di"
+ required="true">
+ </model>
+ </extension>
+ <extension
+ point="org.eclipse.papyrus.infra.core.service">
+ <serviceFactory
+ classname="org.eclipse.papyrus.infra.core.editor.ModelSetServiceFactory"
+ description="The service loading Models"
+ id="org.eclipse.papyrus.infra.core.resource.ModelSet"
+ priority="1"
+ startKind="startup">
+ </serviceFactory>
+ <serviceFactory
+ classname="org.eclipse.papyrus.infra.core.editor.DiResourceSetServiceFactory"
+ description="An alias to ModelSet"
+ id="org.eclipse.papyrus.infra.core.utils.DiResourceSet"
+ priority="1"
+ startKind="startup">
+ <dependsOn
+ serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
+ </dependsOn>
+ </serviceFactory>
+ <serviceFactory
+ classname="org.eclipse.papyrus.infra.core.resource.EditingDomainServiceFactory"
+ description="TransactionalEditing Domain"
+ id="org.eclipse.emf.transaction.TransactionalEditingDomain"
+ priority="1"
+ startKind="startup">
+ <dependsOn
+ serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
+ </dependsOn>
+ </serviceFactory>
+ <serviceFactory
+ classname="org.eclipse.papyrus.infra.core.resource.EditingDomainServiceFactory"
+ description="Editing Domain"
+ id="org.eclipse.emf.edit.domain.EditingDomain"
+ priority="1"
+ startKind="startup">
+ <dependsOn
+ serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
+ </dependsOn>
+ </serviceFactory>
+ </extension>
+ <extension
+ id="org.eclipse.papyrus.modelmarker"
+ name="model marker"
+ point="org.eclipse.core.resources.markers">
+ <attribute
+ name="uri">
+ </attribute>
+ <persistent
+ value="true">
+ </persistent>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.adapters">
+ <factory
+ adaptableType="org.eclipse.papyrus.infra.core.resource.ModelSet"
+ class="org.eclipse.papyrus.infra.core.services.ModelSetServiceAdapter$Factory">
+ <adapter
+ type="org.eclipse.papyrus.infra.core.services.IService">
+ </adapter>
+ </factory>
+ </extension>
+ <extension
+ point="org.eclipse.papyrus.infra.core.service">
+ <service
+ classname="org.eclipse.papyrus.infra.core.internal.language.LanguageService"
+ id="org.eclipse.papyrus.infra.core.language.ILanguageService"
+ priority="10"
+ startKind="startup">
+ </service>
+ <serviceFactory
+ classname="org.eclipse.papyrus.infra.core.internal.sashmodel.BasicPageManagerServiceFactory"
+ description="Default IPageManager service for headless operation."
+ id="org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager"
+ priority="1"
+ startKind="startup">
+ <dependsOn
+ serviceKeyRef="org.eclipse.papyrus.infra.core.resource.ModelSet">
+ </dependsOn>
+ </serviceFactory>
+ </extension>
+
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ id="org.eclipse.papyrus.infra.core.serviceRegistryProperties"
+ type="org.eclipse.papyrus.infra.core.services.ServicesRegistry"
+ namespace="org.eclipse.papyrus.infra.core"
+ properties="hasSemanticModel"
+ class="org.eclipse.papyrus.infra.core.internal.expressions.ServiceRegistryPropertyTester">
+ </propertyTester>
+ </extension>
+</plugin>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml
index 372299c142d..1ac9fb4d645 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml
@@ -2,13 +2,11 @@
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
- <artifactId>org.eclipse.papyrus.releng</artifactId>
<groupId>org.eclipse.papyrus</groupId>
- <version>1.2.0-SNAPSHOT</version>
- <relativePath>../../../../releng/main</relativePath>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.papyrus.infra.core</artifactId>
- <groupId>org.eclipse.papyrus</groupId>
<version>1.2.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/model.exsd b/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/model.exsd
index d9644724ac7..e044620c098 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/model.exsd
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/model.exsd
@@ -88,6 +88,13 @@ The class will be registered using the key set in the Model.
</documentation>
</annotation>
</attribute>
+ <attribute name="required" type="boolean">
+ <annotation>
+ <documentation>
+ Indication of whether the model&apos;s resources are required to be present and loaded in the resource set. The default if not specified is &lt;tt&gt;false&lt;/tt&gt;.
+ </documentation>
+ </annotation>
+ </attribute>
</complexType>
</element>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/AbstractModelWithSharedResource.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/AbstractModelWithSharedResource.java
index ff8fc7986a3..748ab35ed26 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/AbstractModelWithSharedResource.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/AbstractModelWithSharedResource.java
@@ -1,253 +1,257 @@
-/*****************************************************************************
- * Copyright (c) 2011, 2016 LIFL, CEA LIST, Christian W. Damus, and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * LIFL - Initial API and implementation
- * Christian W. Damus - bug 485220
- *
- *****************************************************************************/
-package org.eclipse.papyrus.infra.core.resource;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.papyrus.infra.core.Activator;
-
-/**
- * Base class for models sharing a common {@link Resource}. To share a common {@link Resource}, one of the model should be Master, while the other are
- * slaves. The Master is the one performing the save operation. All the model
- * should use the same file extension. So, {@link #getModelFileExtension()} should return the same value for all models.
- *
- * @author cedric dumoulin
- *
- * @param T
- * Type of the roots of the model.
- */
-public abstract class AbstractModelWithSharedResource<T extends EObject> extends EMFLogicalModel {
-
- /**
- * Possible type for this model: master or slave
- */
- public enum ModelKind {
- master, slave
- }
-
- /**
- * Model kind.
- */
- private ModelKind modelKind;
-
- /**
- *
- * Constructor.
- *
- * @param modelKind
- */
- public AbstractModelWithSharedResource(ModelKind modelKind) {
- this.modelKind = modelKind;
- }
-
- /**
- * By default, we are a slave. Constructor.
- *
- * @param modelKind
- */
- public AbstractModelWithSharedResource() {
- this.modelKind = ModelKind.slave;
- }
-
- /**
- * Attach the model to its resource if this is not already done.
- */
- @Override
- public void loadModel(URI uriWithoutExtension) {
- try {
- // Look for the resource
- lookupResource(uriWithoutExtension);
-
- // Check if model is loaded.
- if (resourceIsSet()) {
- configureResource(resource);
- return;
- }
- // model is not loaded, do it.
- super.loadModel(uriWithoutExtension);
- } catch (Exception ex) {
- if (modelKind == ModelKind.master) {
- Activator.log.error(ex);
- }
- }
- }
-
- /**
- * Create the model if this is not already done.
- */
- @Override
- public void createModel(URI uri) {
- try {
- // Look for the resource
- lookupResource(uri);
-
- // Check if model is loaded.
- if (resourceIsSet()) {
- configureResource(resource);
- return;
- }
- super.createModel(uri);
- } catch (Exception ex) {
- if (modelKind == ModelKind.master) {
- Activator.log.error(ex);
- }
- }
- }
-
- /**
- * Lookup for the resource in the resourceSet.
- *
- * @param uri
- * the URI (without extension) of the resource to look for
- */
- private void lookupResource(URI uriWithoutExtension) {
-
- // Compute model URI
- resourceURI = uriWithoutExtension.appendFileExtension(getModelFileExtension());
-
- resource = getResourceSet().getResource(resourceURI, false);
-
- }
-
- /**
- * Do nothing as we are slave. The Resource is save by the master model.
- *
- * @see org.eclipse.papyrus.infra.core.resource.AbstractBaseModel#saveModel()
- *
- * @throws IOException
- */
- @Override
- public void saveModel() throws IOException {
-
- // Do nothing if we are a slave
- if (modelKind == ModelKind.slave) {
- return;
- }
-
- // Do the save
- super.saveModel();
- }
-
- @Override
- public void saveCopy(IPath targetPathWithoutExtension, Map<Object, Object> targetMap) {
- // Do nothing if we are a slave
- if (modelKind == ModelKind.slave) {
- return;
- }
-
- // Do the save
- super.saveCopy(targetPathWithoutExtension, targetMap);
- }
-
-
- /**
- * Get the root of this model. Lookup in the associated {@link Resource} for
- * the root.
- *
- * @return The root of the model, or null if no root exist.
- */
- @SuppressWarnings("unchecked")
- public T getModelRoot() {
- Resource resource = getResource();
- if (resource == null) {
- return null;
- }
-
- for (EObject object : resource.getContents()) {
-
- if (isModelRoot(object)) {
- return (T) object;
- }
- }
-
- // Not found
- return null;
- }
-
- /**
- * Get the roots of this model. Lookup in the associated {@link Resource} for the roots.
- *
- * @return A list containing the roots of the model. The list is empty if
- * there is no root.
- */
- @SuppressWarnings("unchecked")
- public List<T> getModelRoots() {
-
- List<T> roots = new ArrayList<T>();
-
- for (EObject object : getResource().getContents()) {
- if (isModelRoot(object)) {
- roots.add((T) object);
- }
- }
-
- return roots;
- }
-
- @Override
- public Iterable<? extends EObject> getRootElements() {
- return getModelRoots();
- }
-
- /**
- * @see org.eclipse.papyrus.infra.core.resource.EMFLogicalModel#isModelFor(java.lang.Object)
- *
- * @param element
- * @return
- */
- @Override
- public boolean isModelFor(Object element) {
- if (modelKind == ModelKind.slave) {
- // I'm not the main model for this resource/object
- return false;
- }
- return super.isModelFor(element);
- }
-
- /**
- * Return true if the provided object is a root of the model, false
- * otherwise. This method should be implemented by subclasses.
- *
- * @param object
- * @return
- */
- protected abstract boolean isModelRoot(EObject object);
-
- /**
- * Add a root to this model.
- *
- * @param root
- */
- public void addModelRoot(T root) {
- getResource().getContents().add(root);
- }
-
- @Override
- protected Map<Object, Object> getSaveOptions() {
- if (modelKind == ModelKind.master) {
- return super.getSaveOptions();
- } else {
- return Collections.emptyMap();
- }
- }
-
-}
+/*****************************************************************************
+ * Copyright (c) 2011, 2016 LIFL, CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * LIFL - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.core.resource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.papyrus.infra.core.Activator;
+
+/**
+ * Base class for models sharing a common {@link Resource}. To share a common {@link Resource}, one of the model should be Master, while the other are
+ * slaves. The Master is the one performing the save operation. All the model
+ * should use the same file extension. So, {@link #getModelFileExtension()} should return the same value for all models.
+ *
+ * @author cedric dumoulin
+ *
+ * @param T
+ * Type of the roots of the model.
+ */
+public abstract class AbstractModelWithSharedResource<T extends EObject> extends EMFLogicalModel {
+
+ /**
+ * Possible type for this model: master or slave
+ */
+ public enum ModelKind {
+ master, slave
+ }
+
+ /**
+ * Model kind.
+ */
+ private ModelKind modelKind;
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param modelKind
+ */
+ public AbstractModelWithSharedResource(ModelKind modelKind) {
+ this.modelKind = modelKind;
+ }
+
+ /**
+ * By default, we are a slave. Constructor.
+ *
+ * @param modelKind
+ */
+ public AbstractModelWithSharedResource() {
+ this.modelKind = ModelKind.slave;
+ }
+
+ /**
+ * Attach the model to its resource if this is not already done.
+ */
+ @Override
+ public void loadModel(URI uriWithoutExtension) {
+ try {
+ // Look for the resource
+ lookupResource(uriWithoutExtension);
+
+ // Check if model is loaded.
+ if (resourceIsSet()) {
+ configureResource(resource);
+ return;
+ }
+ // model is not loaded, do it.
+ super.loadModel(uriWithoutExtension);
+ } catch (Exception ex) {
+ if (modelKind == ModelKind.master) {
+ Activator.log.error(ex);
+ }
+ }
+ }
+
+ /**
+ * Create the model if this is not already done.
+ */
+ @Override
+ public void createModel(URI uri) {
+ try {
+ // Look for the resource
+ lookupResource(uri);
+
+ // Check if model is loaded.
+ if (resourceIsSet()) {
+ configureResource(resource);
+ return;
+ }
+ super.createModel(uri);
+ } catch (Exception ex) {
+ if (modelKind == ModelKind.master) {
+ Activator.log.error(ex);
+ }
+ }
+ }
+
+ /**
+ * Lookup for the resource in the resourceSet.
+ *
+ * @param uri
+ * the URI (without extension) of the resource to look for
+ */
+ private void lookupResource(URI uriWithoutExtension) {
+
+ // Compute model URI
+ resourceURI = uriWithoutExtension.appendFileExtension(getModelFileExtension());
+
+ resource = getResourceSet().getResource(resourceURI, false);
+
+ }
+
+ /**
+ * Do nothing as we are slave. The Resource is save by the master model.
+ *
+ * @see org.eclipse.papyrus.infra.core.resource.AbstractBaseModel#saveModel()
+ *
+ * @throws IOException
+ */
+ @Override
+ public void saveModel() throws IOException {
+
+ // Do nothing if we are a slave
+ if (modelKind == ModelKind.slave) {
+ return;
+ }
+
+ // Do the save
+ super.saveModel();
+ }
+
+ @Override
+ public void saveCopy(IPath targetPathWithoutExtension, Map<Object, Object> targetMap) {
+ // Do nothing if we are a slave
+ if (modelKind == ModelKind.slave) {
+ return;
+ }
+
+ // Do the save
+ super.saveCopy(targetPathWithoutExtension, targetMap);
+ }
+
+
+ /**
+ * Get the root of this model. Lookup in the associated {@link Resource} for
+ * the root.
+ *
+ * @return The root of the model, or null if no root exist.
+ */
+ @SuppressWarnings("unchecked")
+ public T getModelRoot() {
+ Resource resource = getResource();
+ if (resource == null) {
+ return null;
+ }
+
+ for (EObject object : resource.getContents()) {
+
+ if (isModelRoot(object)) {
+ return (T) object;
+ }
+ }
+
+ // Not found
+ return null;
+ }
+
+ /**
+ * Get the roots of this model. Lookup in the associated {@link Resource} for the roots.
+ *
+ * @return A list containing the roots of the model. The list is empty if
+ * there is no root.
+ */
+ @SuppressWarnings("unchecked")
+ public List<T> getModelRoots() {
+
+ List<T> roots = new ArrayList<T>();
+
+ for (EObject object : getResource().getContents()) {
+ if (isModelRoot(object)) {
+ roots.add((T) object);
+ }
+ }
+
+ return roots;
+ }
+
+ @Override
+ public Iterable<? extends EObject> getRootElements() {
+ return getModelRoots();
+ }
+
+ /**
+ * @see org.eclipse.papyrus.infra.core.resource.EMFLogicalModel#isModelFor(java.lang.Object)
+ *
+ * @param element
+ * @return
+ */
+ @Override
+ public boolean isModelFor(Object element) {
+ if (modelKind == ModelKind.slave) {
+ // I'm not the main model for this resource/object
+ return false;
+ }
+ return super.isModelFor(element);
+ }
+
+ /**
+ * Return true if the provided object is a root of the model, false
+ * otherwise. This method should be implemented by subclasses.
+ *
+ * @param object
+ * @return
+ */
+ protected abstract boolean isModelRoot(EObject object);
+
+ /**
+ * Add a root to this model.
+ *
+ * @param root
+ */
+ public void addModelRoot(T root) {
+ getResource().getContents().add(root);
+ }
+
+ @Override
+ protected Map<Object, Object> getSaveOptions() {
+ if (modelKind == ModelKind.master) {
+ return super.getSaveOptions();
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+
+ @Override
+ protected boolean isSupportedRoot(EObject object) {
+ return isModelRoot(object);
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/EMFLogicalModel.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/EMFLogicalModel.java
index 39c41452ec7..b665e30baef 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/EMFLogicalModel.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/EMFLogicalModel.java
@@ -177,4 +177,24 @@ public abstract class EMFLogicalModel extends AbstractBaseModel implements IEMFM
.filter(this::isRootElement)
.iterator();
}
+
+ /**
+ * The very basic requirement is that I have a {@link #getResource() resource} in which
+ * to persist the {@code object}.
+ */
+ @Override
+ public boolean canPersist(EObject object) {
+ return (getResource() != null) && isSupportedRoot(object);
+ }
+
+ protected abstract boolean isSupportedRoot(EObject object);
+
+ @Override
+ public void persist(EObject object) {
+ if (!canPersist(object)) {
+ throw new IllegalArgumentException("cannot persist " + object); //$NON-NLS-1$
+ }
+
+ getResource().getContents().add(object);
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/IEMFModel.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/IEMFModel.java
index 542c8bd2143..63c5c0324fc 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/IEMFModel.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/IEMFModel.java
@@ -1,59 +1,85 @@
-/*****************************************************************************
- * Copyright (c) 2013, 2016 CEA LIST, Christian W. Damus, and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms 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 - bug 485220
- *
- *****************************************************************************/
-package org.eclipse.papyrus.infra.core.resource;
-
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
-
-/**
- * A IModel which handles EMF Resources
- *
- * @author Camille Letavernier
- *
- */
-public interface IEMFModel extends IModel {
-
- /**
- * Returns the main resource associated to this model
- *
- * @return
- */
- public Resource getResource();
-
- /**
- * Obtains the top-level model elements in the main resource associated with this model.
- *
- * @return the model roots, or an empty iterable if either there definitively are none
- * or they cannot be determined (for example, because the main resource is a controlled
- * unit and the parent unit is not available)
- */
- public Iterable<? extends EObject> getRootElements();
-
- /**
- * Called when a resource is loaded. Implement this method to configure
- * the resource or load related resources
- *
- * @param resource
- */
- public void handle(Resource resource);
-
- /**
- * Returns true if the resource is a controlled resource
- *
- * @param resource
- * @return
- */
- public boolean isControlled(Resource resource);
-
-}
+/*****************************************************************************
+ * Copyright (c) 2013, 2016 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.core.resource;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+
+/**
+ * A IModel which handles EMF Resources
+ *
+ * @author Camille Letavernier
+ *
+ */
+public interface IEMFModel extends IModel {
+
+ /**
+ * Returns the main resource associated to this model
+ *
+ * @return
+ */
+ public Resource getResource();
+
+ /**
+ * Obtains the top-level model elements in the main resource associated with this model.
+ *
+ * @return the model roots, or an empty iterable if either there definitively are none
+ * or they cannot be determined (for example, because the main resource is a controlled
+ * unit and the parent unit is not available)
+ */
+ public Iterable<? extends EObject> getRootElements();
+
+ /**
+ * Called when a resource is loaded. Implement this method to configure
+ * the resource or load related resources
+ *
+ * @param resource
+ */
+ public void handle(Resource resource);
+
+ /**
+ * Returns true if the resource is a controlled resource
+ *
+ * @param resource
+ * @return
+ */
+ public boolean isControlled(Resource resource);
+
+ /**
+ * Queries whether I am the primary model in which the specified {@code object}
+ * should be stored as a root of one of my resources.
+ *
+ * @param object
+ * an object that needs to be {@link #persist(EObject) persisted}
+ *
+ * @return whether I should persist the {@code object}
+ *
+ * @see #persist(EObject)
+ */
+ public boolean canPersist(EObject object);
+
+ /**
+ * Persists an {@code object} in my most appriopriate resource, if
+ * I {@link #canPersist(EObject) can persist} it
+ *
+ * @param object
+ * an object
+ *
+ * @throws IllegalArgumentException
+ * if I cannot persist the {@code object}
+ *
+ * @see #canPersist(EObject)
+ */
+ public void persist(EObject object);
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java
index 50d98d220ea..ea5967b3683 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java
@@ -1,6 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2008, 2015 CEA LIST, Christian W. Damus, and others.
- *
+ * Copyright (c) 2008, 2016 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -14,13 +13,10 @@
* Christian W. Damus (CEA) - Support read-only state at object level (CDO)
* Christian W. Damus (CEA) - Refactoring of Create Model Wizard (CDO)
* Christian W. Damus (CEA LIST) - Controlled resources in CDO repositories
- * Christian W. Damus (CEA) - bug 429826
- * Christian W. Damus (CEA) - bug 432813
- * Christian W. Damus (CEA) - bug 437052
+ * Christian W. Damus (CEA) - bugs 429826, 432813, 437052
* Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Bug 436952
* Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Bug 436998
- * Christian W. Damus - bug 436998
- * Christian W. Damus - bug 468030
+ * Christian W. Damus - bugs 436998, 468030, 485220
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.resource;
@@ -1296,4 +1292,21 @@ public class ModelSet extends ResourceSetImpl {
languages.clear();
}
}
+
+ /**
+ * Obtains the model that should persist an {@code object}.
+ *
+ * @param object
+ * an object to be persisted as a new root of a managed EMF {@code Resource}
+ *
+ * @return the model that is best suited to persist the {@code object}, or {@code null} if none
+ */
+ public IEMFModel getModelToPersist(EObject object) {
+ return models.values().stream()
+ .filter(IEMFModel.class::isInstance)
+ .map(IEMFModel.class::cast)
+ .filter(m -> m.canPersist(object))
+ .findFirst()
+ .orElse(null);
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelsReader.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelsReader.java
index 1a39d42b88e..ae3edded14d 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelsReader.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelsReader.java
@@ -19,7 +19,11 @@ import static org.eclipse.papyrus.infra.core.Activator.log;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
@@ -71,9 +75,12 @@ public class ModelsReader extends ExtensionUtils {
/** name of the attribute "identifier" */
public static final String IDENTIFIER_ATTRIBUTE_NAME = "identifier";
- /** Name of the extension indicating the model's canonical file extension. */
+ /** Name of the attribute indicating the model's canonical file extension. */
private static final String EXTENSION_ATTRIBUTE = "fileExtension"; //$NON-NLS-1$
+ /** Name of the attribute indicating whether the model is required to be available. */
+ private static final String REQUIRED_ATTRIBUTE = "required"; //$NON-NLS-1$
+
/** Namespace where to look for the extension points. */
protected String extensionPointNamespace;
@@ -343,4 +350,51 @@ public class ModelsReader extends ExtensionUtils {
model.setAfterLoadModelDependencies(afterLoadModelIdentifiers);
model.setBeforeUnloadDependencies(unloadBeforeModelIdentifiers);
}
+
+ /**
+ * Queries the models that are required in their model-set.
+ *
+ * @param modelSet
+ * a model-set
+ * @return ones that are required
+ */
+ public Set<IModel> getRequiredModels(ModelSet modelSet) {
+ return getRequiredModels(modelSet, IModel.class);
+ }
+
+ /**
+ * Queries the models that are required in their model-set.
+ *
+ * @param modelSet
+ * a model-set
+ * @param modelType
+ * the specific type of models to request
+ * @return ones that are required
+ */
+ public <M extends IModel> Set<M> getRequiredModels(ModelSet modelSet, Class<M> modelType) {
+ Set<String> requiredModelClasses = Stream.of(getExtensions())
+ .filter(c -> Boolean.parseBoolean(c.getAttribute(REQUIRED_ATTRIBUTE)))
+ .map(c -> c.getAttribute(CLASSNAME_ATTRIBUTE))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ return modelSet.models.values().stream()
+ .filter(modelType::isInstance)
+ .map(modelType::cast)
+ .filter(instanceOfAny(requiredModelClasses))
+ .collect(Collectors.toSet());
+ }
+
+ private static Predicate<IModel> instanceOfAny(Set<String> classNames) {
+ return model -> {
+ boolean result = false;
+
+ // We don't have to worry about interfaces because the extension point
+ // identifies instantiable classes only
+ for (Class<?> type = model.getClass(); !result && (type != null); type = type.getSuperclass()) {
+ result = classNames.contains(type.getName());
+ }
+
+ return result;
+ };
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/sasheditor/SashModel.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/sasheditor/SashModel.java
index 94a90564047..c853d4b32b7 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/sasheditor/SashModel.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/sasheditor/SashModel.java
@@ -1,373 +1,379 @@
-/*****************************************************************************
- * Copyright (c) 2009, 2016 CEA LIST, Christian W. Damus, and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
- * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Rewrite the sash model - store in the plugin's PreferenceStore (Bug 429239)
- * Christian W. Damus (CEA) - bugs 429242, 436468
- * Christian W. Damus - bugs 434983, 469188, 485220
- *
- *****************************************************************************/
-package org.eclipse.papyrus.infra.core.resource.sasheditor;
-
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-import java.util.Collections;
-import java.util.Map;
-
-import org.eclipse.emf.common.notify.Adapter;
-import org.eclipse.emf.common.notify.Notification;
-import org.eclipse.emf.common.notify.impl.AdapterImpl;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.URIConverter;
-import org.eclipse.emf.ecore.xmi.XMIResource;
-import org.eclipse.emf.ecore.xmi.XMLResource;
-import org.eclipse.papyrus.infra.core.Activator;
-import org.eclipse.papyrus.infra.core.resource.EMFLogicalModel;
-import org.eclipse.papyrus.infra.core.resource.IModel;
-import org.eclipse.papyrus.infra.core.resource.ModelSet;
-import org.eclipse.papyrus.infra.core.sashwindows.di.SashWindowsMngr;
-import org.eclipse.papyrus.infra.core.sashwindows.di.util.DiUtils;
-
-import com.google.common.base.Objects;
-
-/**
- * <p>
- * Model for the sash system.
- * </p>
- * <p>
- * It may be stored in the *.di file (Legacy mode) or in a *.sash file in the user
- * preference space (~workspace/.metadata/.plugins/org.eclipse.papyrus.infra.core/)
- * </p>
- * <p>
- * The following properties are observable via Java Beans {@linkplain #addPropertyChangeListener(String, PropertyChangeListener) listeners}:
- * </p>
- * <ul>
- * <li>{@link #isLegacyMode() legacyMode}</li>
- * <li>{@link #getPrivateResourceURI() privateResourceURI}</li>
- * <li>{@link #getSharedResourceURI() sharedResourceURI}</li>
- * </ul>
- *
- * @author Cedric Dumoulin
- * @author Camille Letavernier
- *
- */
-public class SashModel extends EMFLogicalModel implements IModel {
-
- public static final String PROPERTY_PRIVATE_RESOURCE_URI = "privateResourceURI"; //$NON-NLS-1$
- public static final String PROPERTY_SHARED_RESOURCE_URI = "sharedResourceURI"; //$NON-NLS-1$
- public static final String PROPERTY_LEGACY_MODE = "legacyMode"; //$NON-NLS-1$
-
- private final PropertyChangeSupport bean = new PropertyChangeSupport(this);
-
- private SashModelProviderManager providerManager;
-
- private Adapter sashModelStorageAdapter;
-
- private volatile Boolean legacyMode;
-
- /**
- * File extension.
- *
- * @deprecated Use {@link DiModel#MODEL_FILE_EXTENSION} instead. The SashModel has been moved to a separate file
- */
- @Deprecated
- public static final String MODEL_FILE_EXTENSION = "di"; //$NON-NLS-1$
-
- /**
- * File extension for the Sash model
- */
- public static final String SASH_MODEL_FILE_EXTENSION = "sash"; //$NON-NLS-1$
-
- /**
- * Model ID.
- */
- public static final String MODEL_ID = "org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel"; //$NON-NLS-1$
-
- /**
- *
- * Constructor.
- *
- */
- public SashModel() {
- super();
-
- sashModelStorageAdapter = new AdapterImpl() {
- @Override
- public void notifyChanged(Notification msg) {
- if (getResources().contains(msg.getNotifier())) {
- switch (msg.getFeatureID(Resource.class)) {
- case Resource.RESOURCE__CONTENTS:
- invalidateLegacyMode();
- break;
- }
- }
- }
- };
- }
-
- /**
- * Get the file extension used for this model.
- *
- * @see org.eclipse.papyrus.infra.core.resource.AbstractBaseModel#getModelFileExtension()
- *
- * @return
- */
- @Override
- protected String getModelFileExtension() {
- if (isLegacy((resourceURI == null) ? resourceURI : resourceURI.trimFileExtension())) {
- return DiModel.MODEL_FILE_EXTENSION;
- } else {
- return SASH_MODEL_FILE_EXTENSION;
- }
- }
-
- @Override
- public void init(ModelSet modelSet) {
- super.init(modelSet);
-
- this.providerManager = new SashModelProviderManager(modelSet);
- }
-
- @Override
- public void unload() {
- if (providerManager != null) {
- providerManager.dispose();
- providerManager = null;
- }
-
- getResources().forEach(res -> res.eAdapters().remove(sashModelStorageAdapter));
-
- super.unload();
- }
-
- @Override
- protected boolean isRelatedResource(Resource resource) {
- boolean result = false;
-
- if (resource != null) {
- // We only handle the main Sash resource. Imported *.sash are not relevant
- if (resource == getResource()) {
- result = true;
- } else {
- // We can only calculate these related URIs if the ModelSet is initialized
- result = resource.getURI().equals(getPrivateResourceURI()) || resource.getURI().equals(getSharedResourceURI());
- }
- }
-
- return result;
- }
-
- @Override
- protected void configureResource(Resource resourceToConfigure) {
- super.configureResource(resourceToConfigure);
-
- if (resourceToConfigure != null) {
- resourceToConfigure.eAdapters().add(sashModelStorageAdapter);
- }
- }
-
- /**
- * Get the identifier used to register this model.
- *
- * @see org.eclipse.papyrus.infra.core.resource.AbstractBaseModel#getIdentifier()
- *
- * @return
- */
- @Override
- public String getIdentifier() {
- return MODEL_ID;
- }
-
- @Override
- public void loadModel(URI uriWithoutExtension) {
-
- URI sashModelURI = getSashModelURI(uriWithoutExtension);
-
- this.resourceURI = sashModelURI;
-
- try {
- super.loadModel(sashModelURI.trimFileExtension());
- } catch (Exception ex) {
- createModel(sashModelURI.trimFileExtension());
- }
-
- if (resource == null) {
- createModel(sashModelURI.trimFileExtension());
- }
- }
-
- @Override
- public void createModel(URI uriWithoutExtension) {
- if (isLegacy(uriWithoutExtension)) {
- super.createModel(getSashModelStoreURI(uriWithoutExtension).trimFileExtension());
- } else {
- super.createModel(uriWithoutExtension);
- }
- }
-
- @Override
- public void setModelURI(URI uriWithoutExtension) {
- URI oldPrivateURI = getPrivateResourceURI();
- URI oldSharedURI = getSharedResourceURI();
-
- URI newURI;
- if ((resourceURI != null) && isLegacy(resourceURI.trimFileExtension())) {
- newURI = getLegacyURI(uriWithoutExtension);
- } else {
- newURI = getSashModelStoreURI(uriWithoutExtension);
- }
-
- super.setModelURI(newURI.trimFileExtension());
-
- bean.firePropertyChange(PROPERTY_PRIVATE_RESOURCE_URI, oldPrivateURI, getPrivateResourceURI());
- bean.firePropertyChange(PROPERTY_SHARED_RESOURCE_URI, oldSharedURI, getSharedResourceURI());
- }
-
- protected boolean isLegacy(URI uriWithoutExtension) {
- if (uriWithoutExtension == null) {
- return false;
- }
- return Objects.equal(uriWithoutExtension, getModelManager().getURIWithoutExtension());
- }
-
- /**
- * Returns the sash model URI (With file extension)
- *
- * It may be either the Legacy URI (platform:/resource/model/model.di)
- * or the 1.0.0 URI (file:/~workspace/.metadata/.plugins/org.eclipse.papyrus.infra.core/model/model.sash)
- *
- * @param uriWithoutExtension
- * @return
- *
- */
- protected URI getSashModelURI(URI uriWithoutExtension) {
- URIConverter converter = getModelManager().getURIConverter();
- URI legacyURI = getLegacyURI(uriWithoutExtension);
-
- // If the DI file exists and contains a SashWindowsMngr, this is a legacy model
- if (converter.exists(legacyURI, Collections.emptyMap())) {
- try {
- Resource diResource = getModelManager().getResource(legacyURI, true);
- if (DiUtils.lookupSashWindowsMngr(diResource) != null) {
- return legacyURI;
- }
- } catch (Exception ex) {
- // Temporary workaround: the DI file may exist and be empty
- // (DiModel is currently disabled and doesn't properly init the di file)
- // Log the error and continue
- Activator.log.error(ex);
- }
- }
-
- URI preferenceStoreURI = getSashModelStoreURI(uriWithoutExtension);
-
- return preferenceStoreURI;
- }
-
- protected URI getLegacyURI(URI uriWithoutExtension) {
- return uriWithoutExtension.appendFileExtension(DiModel.MODEL_FILE_EXTENSION);
- }
-
- protected URI getSashModelStoreURI(URI uriWithoutExtension) {
- URI fullURI = uriWithoutExtension.appendFileExtension(SASH_MODEL_FILE_EXTENSION);
- return providerManager.getSashModelProvider(fullURI).getSashModelURI(fullURI);
- }
-
- @Override
- protected Map<Object, Object> getSaveOptions() {
- Map<Object, Object> saveOptions = super.getSaveOptions();
-
- saveOptions.put(XMIResource.OPTION_USE_XMI_TYPE, Boolean.FALSE);
- saveOptions.put(XMLResource.OPTION_SAVE_TYPE_INFORMATION, Boolean.FALSE);
- saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED, Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
-
- return saveOptions;
- }
-
- public boolean isLegacyMode() {
- if (legacyMode == null) {
- legacyMode = false; // Assume not
-
- // Does the shared DI resource contain the sash layout?
- URI sharedURI = getSharedResourceURI();
- if (sharedURI != null) {
- for (Resource next : getResources()) {
- if (sharedURI.equals(next.getURI())) {
- legacyMode = DiUtils.lookupSashWindowsMngr(next) != null;
- break;
- }
- }
- }
- }
-
- return legacyMode;
- }
-
- void invalidateLegacyMode() {
- boolean oldValue = isLegacyMode();
-
- legacyMode = null;
-
- boolean newValue = isLegacyMode();
-
- if (oldValue != newValue) {
- bean.firePropertyChange(PROPERTY_LEGACY_MODE, oldValue, newValue);
- }
- }
-
- /**
- * Gets the URI of the sash-model resource in the user private area, irrespective
- * of whether that actually is the resource that currently stores the sash model.
- *
- * @return the private sash-model resource URI
- */
- public URI getPrivateResourceURI() {
- URI modelURI = (getModelManager() == null) ? null : getModelManager().getURIWithoutExtension();
- return (modelURI == null) ? null : getSashModelStoreURI(modelURI);
- }
-
- /**
- * Gets the URI of the sash-model resource in the shared (collocated with the user model)
- * area, irrespective of whether that actually is the resource that currently stores the
- * sash model.
- *
- * @return the shared sash-model resource URI
- */
- public URI getSharedResourceURI() {
- URI modelURI = (getModelManager() == null) ? null : getModelManager().getURIWithoutExtension();
- return (modelURI == null) ? null : modelURI.appendFileExtension(DiModel.MODEL_FILE_EXTENSION);
- }
-
- @Override
- protected boolean isRootElement(EObject object) {
- return super.isRootElement(object) && (object instanceof SashWindowsMngr);
- }
-
- //
- // Bean API
- //
-
- public void addPropertyChangeListener(PropertyChangeListener listener) {
- bean.addPropertyChangeListener(listener);
- }
-
- public void removePropertyChangeListener(PropertyChangeListener listener) {
- bean.removePropertyChangeListener(listener);
- }
-
- public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
- bean.addPropertyChangeListener(propertyName, listener);
- }
-
- public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
- bean.removePropertyChangeListener(propertyName, listener);
- }
-}
+/*****************************************************************************
+ * Copyright (c) 2009, 2016 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
+ * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Rewrite the sash model - store in the plugin's PreferenceStore (Bug 429239)
+ * Christian W. Damus (CEA) - bugs 429242, 436468
+ * Christian W. Damus - bugs 434983, 469188, 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.core.resource.sasheditor;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notification;
+import org.eclipse.emf.common.notify.impl.AdapterImpl;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.URIConverter;
+import org.eclipse.emf.ecore.xmi.XMIResource;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.papyrus.infra.core.Activator;
+import org.eclipse.papyrus.infra.core.resource.EMFLogicalModel;
+import org.eclipse.papyrus.infra.core.resource.IModel;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.sashwindows.di.DiPackage;
+import org.eclipse.papyrus.infra.core.sashwindows.di.SashWindowsMngr;
+import org.eclipse.papyrus.infra.core.sashwindows.di.util.DiUtils;
+
+import com.google.common.base.Objects;
+
+/**
+ * <p>
+ * Model for the sash system.
+ * </p>
+ * <p>
+ * It may be stored in the *.di file (Legacy mode) or in a *.sash file in the user
+ * preference space (~workspace/.metadata/.plugins/org.eclipse.papyrus.infra.core/)
+ * </p>
+ * <p>
+ * The following properties are observable via Java Beans {@linkplain #addPropertyChangeListener(String, PropertyChangeListener) listeners}:
+ * </p>
+ * <ul>
+ * <li>{@link #isLegacyMode() legacyMode}</li>
+ * <li>{@link #getPrivateResourceURI() privateResourceURI}</li>
+ * <li>{@link #getSharedResourceURI() sharedResourceURI}</li>
+ * </ul>
+ *
+ * @author Cedric Dumoulin
+ * @author Camille Letavernier
+ *
+ */
+public class SashModel extends EMFLogicalModel implements IModel {
+
+ public static final String PROPERTY_PRIVATE_RESOURCE_URI = "privateResourceURI"; //$NON-NLS-1$
+ public static final String PROPERTY_SHARED_RESOURCE_URI = "sharedResourceURI"; //$NON-NLS-1$
+ public static final String PROPERTY_LEGACY_MODE = "legacyMode"; //$NON-NLS-1$
+
+ private final PropertyChangeSupport bean = new PropertyChangeSupport(this);
+
+ private SashModelProviderManager providerManager;
+
+ private Adapter sashModelStorageAdapter;
+
+ private volatile Boolean legacyMode;
+
+ /**
+ * File extension.
+ *
+ * @deprecated Use {@link DiModel#MODEL_FILE_EXTENSION} instead. The SashModel has been moved to a separate file
+ */
+ @Deprecated
+ public static final String MODEL_FILE_EXTENSION = "di"; //$NON-NLS-1$
+
+ /**
+ * File extension for the Sash model
+ */
+ public static final String SASH_MODEL_FILE_EXTENSION = "sash"; //$NON-NLS-1$
+
+ /**
+ * Model ID.
+ */
+ public static final String MODEL_ID = "org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel"; //$NON-NLS-1$
+
+ /**
+ *
+ * Constructor.
+ *
+ */
+ public SashModel() {
+ super();
+
+ sashModelStorageAdapter = new AdapterImpl() {
+ @Override
+ public void notifyChanged(Notification msg) {
+ if (getResources().contains(msg.getNotifier())) {
+ switch (msg.getFeatureID(Resource.class)) {
+ case Resource.RESOURCE__CONTENTS:
+ invalidateLegacyMode();
+ break;
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Get the file extension used for this model.
+ *
+ * @see org.eclipse.papyrus.infra.core.resource.AbstractBaseModel#getModelFileExtension()
+ *
+ * @return
+ */
+ @Override
+ protected String getModelFileExtension() {
+ if (isLegacy((resourceURI == null) ? resourceURI : resourceURI.trimFileExtension())) {
+ return DiModel.MODEL_FILE_EXTENSION;
+ } else {
+ return SASH_MODEL_FILE_EXTENSION;
+ }
+ }
+
+ @Override
+ public void init(ModelSet modelSet) {
+ super.init(modelSet);
+
+ this.providerManager = new SashModelProviderManager(modelSet);
+ }
+
+ @Override
+ public void unload() {
+ if (providerManager != null) {
+ providerManager.dispose();
+ providerManager = null;
+ }
+
+ getResources().forEach(res -> res.eAdapters().remove(sashModelStorageAdapter));
+
+ super.unload();
+ }
+
+ @Override
+ protected boolean isRelatedResource(Resource resource) {
+ boolean result = false;
+
+ if (resource != null) {
+ // We only handle the main Sash resource. Imported *.sash are not relevant
+ if (resource == getResource()) {
+ result = true;
+ } else {
+ // We can only calculate these related URIs if the ModelSet is initialized
+ result = resource.getURI().equals(getPrivateResourceURI()) || resource.getURI().equals(getSharedResourceURI());
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void configureResource(Resource resourceToConfigure) {
+ super.configureResource(resourceToConfigure);
+
+ if (resourceToConfigure != null) {
+ resourceToConfigure.eAdapters().add(sashModelStorageAdapter);
+ }
+ }
+
+ /**
+ * Get the identifier used to register this model.
+ *
+ * @see org.eclipse.papyrus.infra.core.resource.AbstractBaseModel#getIdentifier()
+ *
+ * @return
+ */
+ @Override
+ public String getIdentifier() {
+ return MODEL_ID;
+ }
+
+ @Override
+ public void loadModel(URI uriWithoutExtension) {
+
+ URI sashModelURI = getSashModelURI(uriWithoutExtension);
+
+ this.resourceURI = sashModelURI;
+
+ try {
+ super.loadModel(sashModelURI.trimFileExtension());
+ } catch (Exception ex) {
+ createModel(sashModelURI.trimFileExtension());
+ }
+
+ if (resource == null) {
+ createModel(sashModelURI.trimFileExtension());
+ }
+ }
+
+ @Override
+ public void createModel(URI uriWithoutExtension) {
+ if (isLegacy(uriWithoutExtension)) {
+ super.createModel(getSashModelStoreURI(uriWithoutExtension).trimFileExtension());
+ } else {
+ super.createModel(uriWithoutExtension);
+ }
+ }
+
+ @Override
+ public void setModelURI(URI uriWithoutExtension) {
+ URI oldPrivateURI = getPrivateResourceURI();
+ URI oldSharedURI = getSharedResourceURI();
+
+ URI newURI;
+ if ((resourceURI != null) && isLegacy(resourceURI.trimFileExtension())) {
+ newURI = getLegacyURI(uriWithoutExtension);
+ } else {
+ newURI = getSashModelStoreURI(uriWithoutExtension);
+ }
+
+ super.setModelURI(newURI.trimFileExtension());
+
+ bean.firePropertyChange(PROPERTY_PRIVATE_RESOURCE_URI, oldPrivateURI, getPrivateResourceURI());
+ bean.firePropertyChange(PROPERTY_SHARED_RESOURCE_URI, oldSharedURI, getSharedResourceURI());
+ }
+
+ protected boolean isLegacy(URI uriWithoutExtension) {
+ if (uriWithoutExtension == null) {
+ return false;
+ }
+ return Objects.equal(uriWithoutExtension, getModelManager().getURIWithoutExtension());
+ }
+
+ /**
+ * Returns the sash model URI (With file extension)
+ *
+ * It may be either the Legacy URI (platform:/resource/model/model.di)
+ * or the 1.0.0 URI (file:/~workspace/.metadata/.plugins/org.eclipse.papyrus.infra.core/model/model.sash)
+ *
+ * @param uriWithoutExtension
+ * @return
+ *
+ */
+ protected URI getSashModelURI(URI uriWithoutExtension) {
+ URIConverter converter = getModelManager().getURIConverter();
+ URI legacyURI = getLegacyURI(uriWithoutExtension);
+
+ // If the DI file exists and contains a SashWindowsMngr, this is a legacy model
+ if (converter.exists(legacyURI, Collections.emptyMap())) {
+ try {
+ Resource diResource = getModelManager().getResource(legacyURI, true);
+ if (DiUtils.lookupSashWindowsMngr(diResource) != null) {
+ return legacyURI;
+ }
+ } catch (Exception ex) {
+ // Temporary workaround: the DI file may exist and be empty
+ // (DiModel is currently disabled and doesn't properly init the di file)
+ // Log the error and continue
+ Activator.log.error(ex);
+ }
+ }
+
+ URI preferenceStoreURI = getSashModelStoreURI(uriWithoutExtension);
+
+ return preferenceStoreURI;
+ }
+
+ protected URI getLegacyURI(URI uriWithoutExtension) {
+ return uriWithoutExtension.appendFileExtension(DiModel.MODEL_FILE_EXTENSION);
+ }
+
+ protected URI getSashModelStoreURI(URI uriWithoutExtension) {
+ URI fullURI = uriWithoutExtension.appendFileExtension(SASH_MODEL_FILE_EXTENSION);
+ return providerManager.getSashModelProvider(fullURI).getSashModelURI(fullURI);
+ }
+
+ @Override
+ protected Map<Object, Object> getSaveOptions() {
+ Map<Object, Object> saveOptions = super.getSaveOptions();
+
+ saveOptions.put(XMIResource.OPTION_USE_XMI_TYPE, Boolean.FALSE);
+ saveOptions.put(XMLResource.OPTION_SAVE_TYPE_INFORMATION, Boolean.FALSE);
+ saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED, Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
+
+ return saveOptions;
+ }
+
+ public boolean isLegacyMode() {
+ if (legacyMode == null) {
+ legacyMode = false; // Assume not
+
+ // Does the shared DI resource contain the sash layout?
+ URI sharedURI = getSharedResourceURI();
+ if (sharedURI != null) {
+ for (Resource next : getResources()) {
+ if (sharedURI.equals(next.getURI())) {
+ legacyMode = DiUtils.lookupSashWindowsMngr(next) != null;
+ break;
+ }
+ }
+ }
+ }
+
+ return legacyMode;
+ }
+
+ void invalidateLegacyMode() {
+ boolean oldValue = isLegacyMode();
+
+ legacyMode = null;
+
+ boolean newValue = isLegacyMode();
+
+ if (oldValue != newValue) {
+ bean.firePropertyChange(PROPERTY_LEGACY_MODE, oldValue, newValue);
+ }
+ }
+
+ /**
+ * Gets the URI of the sash-model resource in the user private area, irrespective
+ * of whether that actually is the resource that currently stores the sash model.
+ *
+ * @return the private sash-model resource URI
+ */
+ public URI getPrivateResourceURI() {
+ URI modelURI = (getModelManager() == null) ? null : getModelManager().getURIWithoutExtension();
+ return (modelURI == null) ? null : getSashModelStoreURI(modelURI);
+ }
+
+ /**
+ * Gets the URI of the sash-model resource in the shared (collocated with the user model)
+ * area, irrespective of whether that actually is the resource that currently stores the
+ * sash model.
+ *
+ * @return the shared sash-model resource URI
+ */
+ public URI getSharedResourceURI() {
+ URI modelURI = (getModelManager() == null) ? null : getModelManager().getURIWithoutExtension();
+ return (modelURI == null) ? null : modelURI.appendFileExtension(DiModel.MODEL_FILE_EXTENSION);
+ }
+
+ @Override
+ protected boolean isRootElement(EObject object) {
+ return super.isRootElement(object) && (object instanceof SashWindowsMngr);
+ }
+
+ @Override
+ protected boolean isSupportedRoot(EObject object) {
+ return DiPackage.Literals.SASH_WINDOWS_MNGR.isInstance(object);
+ }
+
+ //
+ // Bean API
+ //
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(listener);
+ }
+
+ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.addPropertyChangeListener(propertyName, listener);
+ }
+
+ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ bean.removePropertyChangeListener(propertyName, listener);
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ExtensionServicesRegistry.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ExtensionServicesRegistry.java
index 100819ee1ec..a7ce2a07d80 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ExtensionServicesRegistry.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ExtensionServicesRegistry.java
@@ -1,6 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011, 2014 LIFL and others.
- *
+ * Copyright (c) 2011, 2016 LIFL, Christian W. Damus, 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:
* LIFL - Initial API and implementation
+ * Christian W. Damus - bug 485220
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services;
@@ -19,6 +19,7 @@ import java.util.List;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.papyrus.infra.core.Activator;
import org.eclipse.papyrus.infra.core.services.ServiceDescriptor.ServiceTypeKind;
/**
@@ -55,9 +56,26 @@ public class ExtensionServicesRegistry extends ServicesRegistry {
private static final String DEPENDSON_KEY_ATTRIBUTE_NAME = "serviceKeyRef";
/**
- * Constructor.
- *
- * @throws Exception
+ * Initializes me with the default extension-point namespace from which to load
+ * registered services.
+ *
+ * @throws ServiceException
+ * on any problem in the loading of service registrations
+ */
+ public ExtensionServicesRegistry() throws ServiceException {
+ this(Activator.PLUGIN_ID);
+ }
+
+ /**
+ * Initializes me with the specified extension-point namespace from which to load
+ * registered services.
+ *
+ * @param extensionPointNamespace
+ * the extension point namespace from which to
+ * load service registration extensions
+ *
+ * @throws ServiceException
+ * on any problem in the loading of service registrations
*/
public ExtensionServicesRegistry(String extensionPointNamespace) throws ServiceException {
this.extensionPointNamespace = extensionPointNamespace;
@@ -119,8 +137,7 @@ public class ExtensionServicesRegistry extends ServicesRegistry {
if (exceptions != null) {
if (exceptions.size() == 1) {
throw exceptions.get(0);
- }
- else {
+ } else {
throw new ServiceException("Somme services are not started (first is shown)", exceptions.get(0)); //$NON-NLS-1$
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java
index d52f56b2859..d04e02fcd7f 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java
@@ -1,156 +1,225 @@
-/*****************************************************************************
- * Copyright (c) 2010, 2016 LIFL, CEA LIST, Christian W. Damus, and others.
- *
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
- * Christian W. Damus - bug 468030
- * Christian W. Damus - bug 485220
- *
- *****************************************************************************/
-
-package org.eclipse.papyrus.infra.core.utils;
-
-import org.eclipse.emf.transaction.TransactionalEditingDomain;
-import org.eclipse.papyrus.infra.core.Activator;
-import org.eclipse.papyrus.infra.core.resource.ModelSet;
-import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager;
-import org.eclipse.papyrus.infra.core.services.ServiceException;
-import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
-import org.eclipse.papyrus.infra.core.services.spi.IContextualServiceRegistryTracker;
-
-/**
- * Set of utility methods for accessing core Services. This class provide
- * methods to access the Papyrus well known services.
- *
- * <br>
- * This is the base class for concrete classes providing these utility methods.
- * Concrete class need to provide method {@link #getServiceRegistry(Object)} implementing how the ServiceRegistry is retrieved from the provided
- * object.
- * Subclasses can also provide a Singleton pattern (getInstance()) in order to
- * allow access to the utility methods in a static way. <br>
- *
- * @author cedri dumoulin
- *
- */
-public abstract class AbstractServiceUtils<T> {
-
- /**
- * Get the service registry from the specified parameter.
- *
- * @param from
- * @return
- */
- abstract public ServicesRegistry getServiceRegistry(T from) throws ServiceException;
-
- /**
- * Obtains the service registry determined automatically from the context of which
- * Papyrus editor or view is active (implying the model that the user is currently editing).
- *
- * @return the contextual service registry, or {@code null} if none can be determined
- */
- protected ServicesRegistry getContextualServiceRegistry() {
- IContextualServiceRegistryTracker tracker = Activator.getDefault().getContextualServiceRegistryTracker();
- return (tracker == null) ? null : tracker.getServiceRegistry();
- }
-
- /**
- * Gets the {@link TransactionalEditingDomain} registered in the {@link ServicesRegistry}.
- *
- * @param from
- * @return
- * @throws ServiceException
- * If an error occurs while getting the requested service.
- */
- public TransactionalEditingDomain getTransactionalEditingDomain(T from) throws ServiceException {
- return getServiceRegistry(from).getService(TransactionalEditingDomain.class);
- }
-
- /**
- * Gets the {@link IPageManager} registered in the {@link ServicesRegistry}.
- *
- * @param from
- * @return
- * @throws ServiceException
- * If an error occurs while getting the requested service.
- */
- public IPageManager getIPageManager(T from) throws ServiceException {
- return getServiceRegistry(from).getService(IPageManager.class);
- }
-
- /**
- * Gets the {@link IPageMngr} registered in the {@link ServicesRegistry}.
- *
- * @param from
- * @return
- * @throws ServiceException
- * If an error occurs while getting the requested service.
- */
- public ModelSet getModelSet(T from) throws ServiceException {
- return getServiceRegistry(from).getService(ModelSet.class);
- }
-
- /**
- * Returns an implementation of the requested service, from the specified context
- *
- * @param service
- * The service for which an implementation is requested
- * @param from
- * The context from which the service should be retrieved
- * @return
- * The implementation of the requested service
- * @throws ServiceException
- * If an error occurs (e.g. cannot find the ServicesRegistry or the Service)
- *
- */
- public <S> S getService(Class<S> service, T from) throws ServiceException {
- return getServiceRegistry(from).getService(service);
- }
-
- /**
- * Returns an implementation of the requested service, from the specified context
- *
- * @param service
- * The service for which an implementation is requested
- * @param from
- * The context from which the service should be retrieved
- * @return
- * The implementation of the requested service
- * @throws ServiceException
- * If an error occurs (e.g. cannot find the ServicesRegistry or the Service)
- *
- */
- public Object getService(Object service, T from) throws ServiceException {
- return getServiceRegistry(from).getService(service);
- }
-
- /**
- * Returns an implementation of the requested <em>optional</em> service, from the specified context, if it is available.
- *
- * @param service
- * The service for which an implementation is requested
- * @param from
- * The context from which the service should be retrieved
- * @param defaultImpl
- * A default implementation of the requested service API to return if none is available in the registry
- * or if the registered implementation could not be properly initialized. May be {@code null} if the
- * service is <em>optional</em>
- *
- * @return
- * The implementation of the requested service, or the {@code defaultImpl}
- */
- public <S> S getService(Class<S> service, T from, S defaultImpl) {
- try {
- // Don't even attempt to get a registry from a null context
- return (from == null) ? defaultImpl : getServiceRegistry(from).getService(service);
- } catch (ServiceException e) {
- // That's OK. It's optional and we have a default
- return defaultImpl;
- }
- }
-}
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 LIFL, CEA LIST, Christian W. Damus, and others.
+ *
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
+ * Christian W. Damus - bug 468030
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.papyrus.infra.core.Activator;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.core.services.spi.IContextualServiceRegistryTracker;
+import org.eclipse.papyrus.infra.tools.util.IProgressCallable;
+import org.eclipse.papyrus.infra.tools.util.IProgressRunnable;
+
+/**
+ * Set of utility methods for accessing core Services. This class provide
+ * methods to access the Papyrus well known services.
+ *
+ * <br>
+ * This is the base class for concrete classes providing these utility methods.
+ * Concrete class need to provide method {@link #getServiceRegistry(Object)} implementing how the ServiceRegistry is retrieved from the provided
+ * object.
+ * Subclasses can also provide a Singleton pattern (getInstance()) in order to
+ * allow access to the utility methods in a static way. <br>
+ *
+ * @author cedri dumoulin
+ *
+ */
+public abstract class AbstractServiceUtils<T> {
+
+ /**
+ * Get the service registry from the specified parameter.
+ *
+ * @param from
+ * @return
+ */
+ abstract public ServicesRegistry getServiceRegistry(T from) throws ServiceException;
+
+ /**
+ * Obtains the service registry determined automatically from the context of which
+ * Papyrus editor or view is active (implying the model that the user is currently editing).
+ *
+ * @return the contextual service registry, or {@code null} if none can be determined
+ */
+ protected ServicesRegistry getContextualServiceRegistry() {
+ IContextualServiceRegistryTracker tracker = Activator.getDefault().getContextualServiceRegistryTracker();
+ return (tracker == null) ? null : tracker.getServiceRegistry();
+ }
+
+ /**
+ * Gets the {@link TransactionalEditingDomain} registered in the {@link ServicesRegistry}.
+ *
+ * @param from
+ * @return
+ * @throws ServiceException
+ * If an error occurs while getting the requested service.
+ */
+ public TransactionalEditingDomain getTransactionalEditingDomain(T from) throws ServiceException {
+ return getServiceRegistry(from).getService(TransactionalEditingDomain.class);
+ }
+
+ /**
+ * Gets the {@link IPageManager} registered in the {@link ServicesRegistry}.
+ *
+ * @param from
+ * @return
+ * @throws ServiceException
+ * If an error occurs while getting the requested service.
+ */
+ public IPageManager getIPageManager(T from) throws ServiceException {
+ return getServiceRegistry(from).getService(IPageManager.class);
+ }
+
+ /**
+ * Gets the {@link IPageMngr} registered in the {@link ServicesRegistry}.
+ *
+ * @param from
+ * @return
+ * @throws ServiceException
+ * If an error occurs while getting the requested service.
+ */
+ public ModelSet getModelSet(T from) throws ServiceException {
+ return getServiceRegistry(from).getService(ModelSet.class);
+ }
+
+ /**
+ * Returns an implementation of the requested service, from the specified context
+ *
+ * @param service
+ * The service for which an implementation is requested
+ * @param from
+ * The context from which the service should be retrieved
+ * @return
+ * The implementation of the requested service
+ * @throws ServiceException
+ * If an error occurs (e.g. cannot find the ServicesRegistry or the Service)
+ *
+ */
+ public <S> S getService(Class<S> service, T from) throws ServiceException {
+ return getServiceRegistry(from).getService(service);
+ }
+
+ /**
+ * Returns an implementation of the requested service, from the specified context
+ *
+ * @param service
+ * The service for which an implementation is requested
+ * @param from
+ * The context from which the service should be retrieved
+ * @return
+ * The implementation of the requested service
+ * @throws ServiceException
+ * If an error occurs (e.g. cannot find the ServicesRegistry or the Service)
+ *
+ */
+ public Object getService(Object service, T from) throws ServiceException {
+ return getServiceRegistry(from).getService(service);
+ }
+
+ /**
+ * Returns an implementation of the requested <em>optional</em> service, from the specified context, if it is available.
+ *
+ * @param service
+ * The service for which an implementation is requested
+ * @param from
+ * The context from which the service should be retrieved
+ * @param defaultImpl
+ * A default implementation of the requested service API to return if none is available in the registry
+ * or if the registered implementation could not be properly initialized. May be {@code null} if the
+ * service is <em>optional</em>
+ *
+ * @return
+ * The implementation of the requested service, or the {@code defaultImpl}
+ */
+ public <S> S getService(Class<S> service, T from, S defaultImpl) {
+ try {
+ // Don't even attempt to get a registry from a null context
+ return (from == null) ? defaultImpl : getServiceRegistry(from).getService(service);
+ } catch (ServiceException e) {
+ // That's OK. It's optional and we have a default
+ return defaultImpl;
+ }
+ }
+
+ /**
+ * Obtains a Papyrus callable from a plain {@code callable} with the registry
+ * context derived {@code from} the given context object.
+ *
+ * @param callable
+ * a callable to encapsulate
+ * @param from
+ * the Papyrus context from which to derive the registry
+ *
+ * @return the Papyrus callable
+ */
+ public <V> IPapyrusCallable<V> callable(IProgressCallable<V> callable, T from) {
+ return new IPapyrusCallable<V>() {
+ @Override
+ public V call(IProgressMonitor monitor) throws Exception {
+ return callable.call(monitor);
+ }
+
+ @Override
+ public ServicesRegistry getServiceRegistry() {
+ ServicesRegistry result = null;
+
+ try {
+ result = AbstractServiceUtils.this.getServiceRegistry(from);
+ } catch (ServiceException e) {
+ Activator.log.error(e);
+ }
+
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Obtains a Papyrus runnable from a plain {@code runnable} with the registry
+ * context derived {@code from} the given context object.
+ *
+ * @param runnable
+ * a runnable to encapsulate
+ * @param from
+ * the Papyrus context from which to derive the registry
+ *
+ * @return the Papyrus runnable
+ */
+ public IPapyrusRunnable runnable(IProgressRunnable runnable, T from) {
+ return new IPapyrusRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) {
+ runnable.run(monitor);
+ }
+
+ @Override
+ public ServicesRegistry getServiceRegistry() {
+ ServicesRegistry result = null;
+
+ try {
+ result = AbstractServiceUtils.this.getServiceRegistry(from);
+ } catch (ServiceException e) {
+ Activator.log.error(e);
+ }
+
+ return result;
+ }
+ };
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusCallable.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusCallable.java
new file mode 100644
index 00000000000..5d7b6edd304
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusCallable.java
@@ -0,0 +1,49 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.tools.util.IProgressCallable;
+
+/**
+ * Protocol for a callable in the Papyrus context.
+ */
+@FunctionalInterface
+public interface IPapyrusCallable<V> extends IProgressCallable<V>, IServiceRegistryProvider {
+
+ @Override
+ default ServicesRegistry getServiceRegistry() {
+ try {
+ return ServiceUtils.getInstance().getServiceRegistry(null);
+ } catch (ServiceException e) {
+ return null;
+ }
+ }
+
+ static <V> IPapyrusCallable<V> inContext(ServicesRegistry registry, IPapyrusCallable<V> callable) {
+ return new IPapyrusCallable<V>() {
+ @Override
+ public V call(IProgressMonitor monitor) throws Exception {
+ return callable.call(monitor);
+ }
+
+ @Override
+ public ServicesRegistry getServiceRegistry() {
+ return registry;
+ }
+ };
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusRunnable.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusRunnable.java
new file mode 100644
index 00000000000..4c8d2f1d008
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IPapyrusRunnable.java
@@ -0,0 +1,49 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.tools.util.IProgressRunnable;
+
+/**
+ * Protocol for a runnable in the Papyrus context.
+ */
+@FunctionalInterface
+public interface IPapyrusRunnable extends IProgressRunnable, IServiceRegistryProvider {
+
+ @Override
+ default ServicesRegistry getServiceRegistry() {
+ try {
+ return ServiceUtils.getInstance().getServiceRegistry(null);
+ } catch (ServiceException e) {
+ return null;
+ }
+ }
+
+ static IPapyrusRunnable inContext(ServicesRegistry registry, IPapyrusRunnable runnable) {
+ return new IPapyrusRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) {
+ runnable.run(monitor);
+ }
+
+ @Override
+ public ServicesRegistry getServiceRegistry() {
+ return registry;
+ }
+ };
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IServiceRegistryProvider.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IServiceRegistryProvider.java
new file mode 100644
index 00000000000..36c50087af5
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/IServiceRegistryProvider.java
@@ -0,0 +1,30 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+
+/**
+ * A protocol for any object that can provide the context of a Papyrus
+ * {@link ServicesRegistry} in which it is exists.
+ */
+@FunctionalInterface
+public interface IServiceRegistryProvider {
+ /**
+ * Obtains my contextual service registry.
+ *
+ * @return my service registry
+ */
+ ServicesRegistry getServiceRegistry();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/ServiceUtils.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/ServiceUtils.java
index 4ee47baabbd..9712f639a46 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/ServiceUtils.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/ServiceUtils.java
@@ -1,57 +1,57 @@
-/*****************************************************************************
- * Copyright (c) 2010, 2016 LIFL, CEA LIST, Christian W. Damus, and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
- * Christian W. Damus - bug 485220
- *
- *****************************************************************************/
-
-package org.eclipse.papyrus.infra.core.utils;
-
-import org.eclipse.papyrus.infra.core.services.ServiceException;
-import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
-
-/**
- * @author cedric dumoulin
- *
- */
-public class ServiceUtils extends AbstractServiceUtils<ServicesRegistry> {
-
- private final static ServiceUtils instance = new ServiceUtils();
-
- /**
- * Get the singleton instance of the class.
- *
- * @return
- */
- public static final ServiceUtils getInstance() {
- return instance;
- }
-
- /**
- * @see org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils#getServiceRegistry(java.lang.Object)
- *
- * @param from
- * the service registry, or {@code null} to try to get the contextual default service registry
- * @return
- * @throws ServiceException
- */
- @Override
- public ServicesRegistry getServiceRegistry(ServicesRegistry from) throws ServiceException {
- if (from != null) {
- return from;
- }
- ServicesRegistry contextual = getContextualServiceRegistry();
- if (contextual == null) {
- throw new ServiceException("The Service Registry could not be found");
- }
- return contextual;
- }
-
-}
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 LIFL, CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.utils;
+
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+
+/**
+ * @author cedric dumoulin
+ *
+ */
+public class ServiceUtils extends AbstractServiceUtils<ServicesRegistry> {
+
+ private final static ServiceUtils instance = new ServiceUtils();
+
+ /**
+ * Get the singleton instance of the class.
+ *
+ * @return
+ */
+ public static final ServiceUtils getInstance() {
+ return instance;
+ }
+
+ /**
+ * @see org.eclipse.papyrus.infra.core.utils.AbstractServiceUtils#getServiceRegistry(java.lang.Object)
+ *
+ * @param from
+ * the service registry, or {@code null} to try to get the contextual default service registry
+ * @return
+ * @throws ServiceException
+ */
+ @Override
+ public ServicesRegistry getServiceRegistry(ServicesRegistry from) throws ServiceException {
+ if (from != null) {
+ return from;
+ }
+ ServicesRegistry contextual = getContextualServiceRegistry();
+ if (contextual == null) {
+ throw new ServiceException("The Service Registry could not be found");
+ }
+ return contextual;
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
index add383bc3fe..29fcefbed91 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/TransactionHelper.java
@@ -1,460 +1,522 @@
-/*****************************************************************************
- * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms 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) - bugs 429826, 408491, 433320
- * Christian W. Damus - bugs 451557, 457560, 461629, 463564, 466997, 465416, 485220
- *
- *****************************************************************************/
-package org.eclipse.papyrus.infra.core.utils;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executor;
-
-import org.eclipse.emf.edit.domain.EditingDomain;
-import org.eclipse.emf.transaction.RecordingCommand;
-import org.eclipse.emf.transaction.RollbackException;
-import org.eclipse.emf.transaction.Transaction;
-import org.eclipse.emf.transaction.TransactionalEditingDomain;
-import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
-import org.eclipse.emf.transaction.impl.TransactionImpl;
-import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis;
-
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-
-/**
- * This helper can be used to run (safe) transactions outside the CommandStack
- *
- * @author Camille Letavernier
- *
- */
-public class TransactionHelper {
-
- // Refactoring needed. The sasheditor contentprovider should have dependencies to infra.EMF...
-
- public static final String TRANSACTION_OPTION_READ_ONLY_AXIS = "papyrus.read_only_axis"; //$NON-NLS-1$
-
- /**
- * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
- */
- @Deprecated
- public static final String TRANSACTION_OPTION_NO_READ_ONLY_CACHE = "papyrus.no_read_only_cache"; //$NON-NLS-1$
-
- public static final String TRANSACTION_OPTION_INTERACTIVE = "papyrus.interactive"; //$NON-NLS-1$
-
- public static final String TRANSACTION_OPTION_MERGE_NESTED_READ = "papyrus.merge_nested_read"; //$NON-NLS-1$
-
- public static void run(EditingDomain domain, Runnable writeOperation) throws InterruptedException, RollbackException {
- if (domain instanceof TransactionalEditingDomain) {
- run((TransactionalEditingDomain) domain, writeOperation);
- } else {
- writeOperation.run();
- }
- }
-
- public static void run(TransactionalEditingDomain domain, final Runnable writeOperation) throws InterruptedException, RollbackException {
- if (domain instanceof InternalTransactionalEditingDomain) {
- run((InternalTransactionalEditingDomain) domain, writeOperation);
- } else {
- // Shouldn't happen, as all TransactionalEditingDomain implementations should also implement InternalTransactionalEditingDomain
- domain.getCommandStack().execute(new RecordingCommand(domain) {
-
- @Override
- protected void doExecute() {
- writeOperation.run();
- }
- });
- }
- }
-
- public static void run(InternalTransactionalEditingDomain domain, Runnable writeOperation) throws InterruptedException, RollbackException {
- Transaction transaction = domain.startTransaction(false, Collections.emptyMap());
- try {
- writeOperation.run();
- } finally {
- transaction.commit();
- }
- }
-
- /**
- * Queries whether an editing {@code domain} has been disposed.
- *
- * @param domain
- * an editing domain
- *
- * @return whether the {@code domain} is {@code null} (which presumably implies disposed) or has been disposed
- */
- public static boolean isDisposed(TransactionalEditingDomain domain) {
- // A disposed editing domain (and only a disposed editing domain) has no command stack
- return (domain == null) || (domain.getCommandStack() == null);
- }
-
- /**
- * Merges the read-only {@code axis} option into an existing map of {@code options}.
- *
- * @param options
- * an existing (non-{@code null}) options map
- * @param axis
- * the axis option to merge
- * @return the augmented {@code options}
- */
- public static Map<String, Object> mergeReadOnlyAxisOption(Map<String, Object> options, ReadOnlyAxis axis) {
- return mergeReadOnlyAxisOption(options, Collections.singleton(axis));
- }
-
- /**
- * Merges the read-only {@code axes} option into an existing map of {@code options}.
- *
- * @param options
- * an existing (non-{@code null}) options map
- * @param axes
- * the axes option to merge
- * @return the augmented {@code options}
- */
- public static Map<String, Object> mergeReadOnlyAxisOption(Map<String, Object> options, Set<ReadOnlyAxis> axes) {
- options.put(TRANSACTION_OPTION_READ_ONLY_AXIS, axes);
- return options;
- }
-
- /**
- * Adds the read-only {@code axis} option to a transaction's {@code options}.
- *
- * @param options
- * an options map, which may be {@code null} or immutable
- * @param axis
- * the axis option to add
- * @return a new map based on the {@code options} and including the {@code axis}
- */
- public static Map<String, Object> addReadOnlyAxisOption(Map<String, ?> options, ReadOnlyAxis axis) {
- return addReadOnlyAxisOption(options, Collections.singleton(axis));
- }
-
- /**
- * Adds the read-only {@code axes} option to a transaction's {@code options}.
- *
- * @param options
- * an options map, which may be {@code null} or immutable
- * @param axes
- * the axes option to add
- * @return a new map based on the {@code options} and including the {@code axes}
- */
- public static Map<String, Object> addReadOnlyAxisOption(Map<String, ?> options, Set<ReadOnlyAxis> axes) {
- Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
- result.put(TRANSACTION_OPTION_READ_ONLY_AXIS, axes);
- return result;
- }
-
- /**
- * Creates a new mutable transaction options map with a read-only {@code axis}.
- *
- * @param axis
- * the axis option
- * @return a new mutable map including the {@code axis}
- */
- public static Map<String, Object> readOnlyAxisOption(ReadOnlyAxis axis) {
- return readOnlyAxisOption(Collections.singleton(axis));
- }
-
- /**
- * Creates a new mutable transaction options map with a read-only {@code axes}.
- *
- * @param axes
- * the axes option
- * @return a new mutable map including the {@code axes}
- */
- public static Map<String, Object> readOnlyAxisOption(Set<ReadOnlyAxis> axes) {
- return addReadOnlyAxisOption(null, axes);
- }
-
- /**
- * Queries the read-only axes to be enforced by a {@code transaction}.
- *
- * @param transaction
- * a transaction
- * @return its read-only axes, which are {@linkplain ReadOnlyAxis#anyAxis() all of them} by default if the option is absent
- */
- @SuppressWarnings("unchecked")
- public static Set<ReadOnlyAxis> getReadOnlyAxisOption(Transaction transaction) {
- Set<ReadOnlyAxis> result;
-
- Object value = transaction.getOptions().get(TRANSACTION_OPTION_READ_ONLY_AXIS);
- if (value instanceof Set<?>) {
- result = (Set<ReadOnlyAxis>) value;
- } else if (value instanceof Iterable<?>) {
- result = Sets.immutableEnumSet((Iterable<ReadOnlyAxis>) value);
- } else {
- result = ReadOnlyAxis.anyAxis();
- }
-
- return result;
- }
-
- /**
- * Merges the option to disable read-only state caching into an existing map of {@code options}.
- *
- * @param options
- * an existing (non-{@code null}) options map
- * @param disableCache
- * whether to disable read-only caching
- * @return the augmented {@code options}
- * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
- */
- @Deprecated
- public static Map<String, Object> mergeDisableReadOnlyOption(Map<String, Object> options, boolean disableCache) {
- options.put(TRANSACTION_OPTION_NO_READ_ONLY_CACHE, disableCache);
- return options;
- }
-
- /**
- * Adds the option to disable read-only state caching to a transaction's {@code options}.
- *
- * @param options
- * an options map, which may be {@code null} or immutable
- * @param disableCache
- * whether to disable read-only caching
- * @return a new map based on the {@code options} and including the {@code disableCache} option
- * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
- */
- @Deprecated
- public static Map<String, Object> addDisableReadOnlyCacheOption(Map<String, ?> options, boolean disableCache) {
- Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
- result.put(TRANSACTION_OPTION_NO_READ_ONLY_CACHE, disableCache);
- return result;
- }
-
- /**
- * Creates a new mutable transaction options map with the option to disable caching of read-only state for objects and resources.
- *
- * @param disableCache
- * whether to disable read-only caching
- * @return a new mutable map including the {@code disableCache} option
- * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
- */
- @Deprecated
- public static Map<String, Object> disableReadOnlyCacheOption(boolean disableCache) {
- return addDisableReadOnlyCacheOption(null, disableCache);
- }
-
- /**
- * Queries whether a {@code transaction} is running with caching of read-only state of objects and resources disabled.
- *
- * @param transaction
- * a transaction
- * @return {@code true} if the {@code transaction} has the {@linkplain #TRANSACTION_OPTION_NO_READ_ONLY_CACHE interactive option} set {@code true}; {@code false}, otherwise (including the default case of no option set)
- * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
- */
- @Deprecated
- public static boolean isReadOnlyCacheDisabled(Transaction transaction) {
- Object value = transaction.getOptions().get(TRANSACTION_OPTION_NO_READ_ONLY_CACHE);
- return (value instanceof Boolean) ? (Boolean) value : false;
- }
-
- /**
- * Merges the option to merge nested read-only transactions with parent write transactions into an existing map of {@code options}.
- * This option on a write transaction affects this behaviour of subsequent child read-only transactions.
- *
- * @param options
- * an existing (non-{@code null}) options map
- * @param mergeReadOnly
- * whether to merge read-only transactions into parent write transactions
- * @return the augmented {@code options}
- */
- public static Map<String, Object> mergeMergeReadOnlyOption(Map<String, Object> options, boolean mergeReadOnly) {
- options.put(TRANSACTION_OPTION_MERGE_NESTED_READ, mergeReadOnly);
- return options;
- }
-
- /**
- * Adds the option to merge nested read-only transactions with parent write transactions to a transaction's {@code options}.
- * This option on a write transaction affects this behaviour of subsequent child read-only transactions.
- *
- * @param options
- * an options map, which may be {@code null} or immutable
- * @param mergeReadOnly
- * whether to merge read-only transactions into parent write transactions
- * @return a new map based on the {@code options} and including the {@code mergeReadOnly} option
- */
- public static Map<String, Object> addMergeReadOnlyOption(Map<String, ?> options, boolean mergeReadOnly) {
- Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
- result.put(TRANSACTION_OPTION_MERGE_NESTED_READ, mergeReadOnly);
- return result;
- }
-
- /**
- * Creates a new mutable transaction options map with the option to merge nested read-only transactions with parent write transactions.
- * This option on a write transaction affects this behaviour of subsequent child read-only transactions.
- *
- * @param mergeReadOnly
- * whether to merge read-only transactions into parent write transactions
- * @return a new mutable map including the {@code mergeReadOnly} option
- */
- public static Map<String, Object> mergeReadOnlyOption(boolean mergeReadOnly) {
- return addMergeReadOnlyOption(null, mergeReadOnly);
- }
-
- /**
- * Queries whether a {@code transaction} is running with merging of nested read-only transactions with parent write transactions enabled.
- *
- * @param transaction
- * a transaction
- * @return {@code true} if the {@code transaction} has the {@linkplain #TRANSACTION_OPTION_MERGE_NESTED_READ interactive option} set {@code true}; {@code false}, otherwise (including the default case of no option set)
- */
- public static boolean isMergeReadOnly(Transaction transaction) {
- Object value = transaction.getOptions().get(TRANSACTION_OPTION_MERGE_NESTED_READ);
- return (value instanceof Boolean) ? (Boolean) value : false;
- }
-
- /**
- * Merges the {@code interactive} transaction option into an existing map of {@code options}.
- *
- * @param options
- * an existing (non-{@code null}) options map
- * @param interactive
- * whether the transaction is in an user-interactive context
- * @return the augmented {@code options}
- */
- public static Map<String, Object> mergeInteractiveOption(Map<String, Object> options, boolean interactive) {
- options.put(TRANSACTION_OPTION_INTERACTIVE, interactive);
- return options;
- }
-
- /**
- * Adds the {@code interactive} option option to a transaction's {@code options}.
- *
- * @param options
- * an options map, which may be {@code null} or immutable
- * @param interactive
- * whether the transaction is in an user-interactive context
- * @return a new map based on the {@code options} and including the {@code interactive} option
- */
- public static Map<String, Object> addInteractiveOption(Map<String, ?> options, boolean interactive) {
- Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
- result.put(TRANSACTION_OPTION_INTERACTIVE, interactive);
- return result;
- }
-
- /**
- * Creates a new mutable transaction options map with an {@code interactive} option.
- *
- * @param interactive
- * whether the transaction is in an user-interactive context
- * @return a new mutable map including the {@code interactive} option
- */
- public static Map<String, Object> interactiveOption(boolean interactive) {
- return addInteractiveOption(null, interactive);
- }
-
- /**
- * Queries whether a {@code transaction} is running in a user-interactive context. In practice, this means that it would be appropriate to
- * prompt the user to make resources/objects writable if necessary.
- *
- * @param transaction
- * a transaction
- * @return {@code false} if the {@code transaction} has the {@linkplain #TRANSACTION_OPTION_INTERACTIVE interactive option} set {@code false}; {@code true}, otherwise (including the default case of no option set)
- */
- public static boolean isInteractive(Transaction transaction) {
- Object value = transaction.getOptions().get(TRANSACTION_OPTION_INTERACTIVE);
- return (value instanceof Boolean) ? (Boolean) value : true;
- }
-
- /**
- * Queries whether a {@code transaction} is one that is executing or that executed triggers, or is perhaps
- * the read-only post-commit transaction that tells listeners about the changes made by a trigger transaction.
- *
- * @param transaction
- * a transaction
- * @return whether it is a trigger transaction
- */
- public static boolean isTriggerTransaction(Transaction transaction) {
- Object value = transaction.getOptions().get(TransactionImpl.OPTION_IS_TRIGGER_TRANSACTION);
- return (value instanceof Boolean) ? (Boolean) value : false;
- }
-
- /**
- * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
- * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
- *
- * @param domain
- * a transactional editing domain. May not be {@code null}
- * @param fallback
- * an executor to use for scheduling tasks when the {@code domain} does not have a
- * write transaction open. May not be {@code null}
- */
- public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback) {
- return createTransactionExecutor(domain, fallback, null, null);
- }
-
- /**
- * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
- * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
- * The specified {@code policy}, if any, may determine whether for some task the {@code fallback} should be preferred
- * over the transaction executor or vice-versa (such as to handle special requirements like tasks needing to run
- * on the UI thread).
- *
- * @param domain
- * a transactional editing domain. May not be {@code null}
- * @param fallback
- * an executor to use for scheduling tasks when the {@code domain} does not have a
- * write transaction open. May not be {@code null}
- * @param policy
- * an optional executor selection policy (may be {@code null}). The policy is queried for ranking of both
- * the transaction executor and the {@code fallback}, unless there is no transaction active, which always excludes the
- * transaction executor. In the event of a tie, the transaction executor is always selected
- */
- public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback, IExecutorPolicy policy) {
- return createTransactionExecutor(domain, fallback, policy, null);
- }
-
- /**
- * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
- * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
- *
- * @param domain
- * a transactional editing domain. May not be {@code null}
- * @param fallback
- * an executor to use for scheduling tasks when the {@code domain} does not have a
- * write transaction open. May not be {@code null}
- * @param options
- * a map of options to apply to the nested transaction in which tasks are executed. May be {@code null} if not needed
- */
- public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback, Map<?, ?> options) {
- return createTransactionExecutor(domain, fallback, null, options);
- }
-
- /**
- * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
- * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
- * The specified {@code policy}, if any, may determine whether for some task the {@code fallback} should be preferred
- * over the transaction executor or vice-versa (such as to handle special requirements like tasks needing to run
- * on the UI thread).
- *
- * @param domain
- * a transactional editing domain. May not be {@code null}
- * @param fallback
- * an executor to use for scheduling tasks when the {@code domain} does not have a
- * write transaction open. May not be {@code null}
- * @param policy
- * an optional executor selection policy (may be {@code null}). The policy is queried for ranking of both
- * the transaction executor and the {@code fallback}, unless there is no transaction active, which always excludes the
- * transaction executor. In the event of a tie, the transaction executor is always selected
- * @param options
- * a map of options to apply to the nested transaction in which tasks are executed. May be {@code null} if not needed
- */
- public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback, IExecutorPolicy policy, Map<?, ?> options) {
- if ((domain == null) || (fallback == null)) {
- throw new NullPointerException();
- }
-
- return new TransactionPrecommitExecutor(domain, fallback, policy, options);
- }
-}
+/*****************************************************************************
+ * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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) - bugs 429826, 408491, 433320
+ * Christian W. Damus - bugs 451557, 457560, 461629, 463564, 466997, 465416, 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.core.utils;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.transaction.RecordingCommand;
+import org.eclipse.emf.transaction.RollbackException;
+import org.eclipse.emf.transaction.Transaction;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
+import org.eclipse.emf.transaction.impl.TransactionImpl;
+import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis;
+import org.eclipse.papyrus.infra.tools.util.IExecutorService;
+import org.eclipse.papyrus.infra.tools.util.IProgressCallable;
+import org.eclipse.papyrus.infra.tools.util.IProgressRunnable;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+
+/**
+ * This helper can be used to run (safe) transactions outside the CommandStack
+ *
+ * @author Camille Letavernier
+ *
+ */
+public class TransactionHelper {
+
+ // Refactoring needed. The sasheditor contentprovider should have dependencies to infra.EMF...
+
+ public static final String TRANSACTION_OPTION_READ_ONLY_AXIS = "papyrus.read_only_axis"; //$NON-NLS-1$
+
+ /**
+ * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
+ */
+ @Deprecated
+ public static final String TRANSACTION_OPTION_NO_READ_ONLY_CACHE = "papyrus.no_read_only_cache"; //$NON-NLS-1$
+
+ public static final String TRANSACTION_OPTION_INTERACTIVE = "papyrus.interactive"; //$NON-NLS-1$
+
+ public static final String TRANSACTION_OPTION_MERGE_NESTED_READ = "papyrus.merge_nested_read"; //$NON-NLS-1$
+
+ public static void run(EditingDomain domain, Runnable writeOperation) throws InterruptedException, RollbackException {
+ if (domain instanceof TransactionalEditingDomain) {
+ run((TransactionalEditingDomain) domain, writeOperation);
+ } else {
+ writeOperation.run();
+ }
+ }
+
+ public static void run(TransactionalEditingDomain domain, final Runnable writeOperation) throws InterruptedException, RollbackException {
+ if (domain instanceof InternalTransactionalEditingDomain) {
+ run((InternalTransactionalEditingDomain) domain, writeOperation);
+ } else {
+ // Shouldn't happen, as all TransactionalEditingDomain implementations should also implement InternalTransactionalEditingDomain
+ domain.getCommandStack().execute(new RecordingCommand(domain) {
+
+ @Override
+ protected void doExecute() {
+ writeOperation.run();
+ }
+ });
+ }
+ }
+
+ public static void run(InternalTransactionalEditingDomain domain, Runnable writeOperation) throws InterruptedException, RollbackException {
+ Transaction transaction = domain.startTransaction(false, Collections.emptyMap());
+ try {
+ writeOperation.run();
+ } finally {
+ transaction.commit();
+ }
+ }
+
+ /**
+ * Queries whether an editing {@code domain} has been disposed.
+ *
+ * @param domain
+ * an editing domain
+ *
+ * @return whether the {@code domain} is {@code null} (which presumably implies disposed) or has been disposed
+ */
+ public static boolean isDisposed(TransactionalEditingDomain domain) {
+ // A disposed editing domain (and only a disposed editing domain) has no command stack
+ return (domain == null) || (domain.getCommandStack() == null);
+ }
+
+ /**
+ * Merges the read-only {@code axis} option into an existing map of {@code options}.
+ *
+ * @param options
+ * an existing (non-{@code null}) options map
+ * @param axis
+ * the axis option to merge
+ * @return the augmented {@code options}
+ */
+ public static Map<String, Object> mergeReadOnlyAxisOption(Map<String, Object> options, ReadOnlyAxis axis) {
+ return mergeReadOnlyAxisOption(options, Collections.singleton(axis));
+ }
+
+ /**
+ * Merges the read-only {@code axes} option into an existing map of {@code options}.
+ *
+ * @param options
+ * an existing (non-{@code null}) options map
+ * @param axes
+ * the axes option to merge
+ * @return the augmented {@code options}
+ */
+ public static Map<String, Object> mergeReadOnlyAxisOption(Map<String, Object> options, Set<ReadOnlyAxis> axes) {
+ options.put(TRANSACTION_OPTION_READ_ONLY_AXIS, axes);
+ return options;
+ }
+
+ /**
+ * Adds the read-only {@code axis} option to a transaction's {@code options}.
+ *
+ * @param options
+ * an options map, which may be {@code null} or immutable
+ * @param axis
+ * the axis option to add
+ * @return a new map based on the {@code options} and including the {@code axis}
+ */
+ public static Map<String, Object> addReadOnlyAxisOption(Map<String, ?> options, ReadOnlyAxis axis) {
+ return addReadOnlyAxisOption(options, Collections.singleton(axis));
+ }
+
+ /**
+ * Adds the read-only {@code axes} option to a transaction's {@code options}.
+ *
+ * @param options
+ * an options map, which may be {@code null} or immutable
+ * @param axes
+ * the axes option to add
+ * @return a new map based on the {@code options} and including the {@code axes}
+ */
+ public static Map<String, Object> addReadOnlyAxisOption(Map<String, ?> options, Set<ReadOnlyAxis> axes) {
+ Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
+ result.put(TRANSACTION_OPTION_READ_ONLY_AXIS, axes);
+ return result;
+ }
+
+ /**
+ * Creates a new mutable transaction options map with a read-only {@code axis}.
+ *
+ * @param axis
+ * the axis option
+ * @return a new mutable map including the {@code axis}
+ */
+ public static Map<String, Object> readOnlyAxisOption(ReadOnlyAxis axis) {
+ return readOnlyAxisOption(Collections.singleton(axis));
+ }
+
+ /**
+ * Creates a new mutable transaction options map with a read-only {@code axes}.
+ *
+ * @param axes
+ * the axes option
+ * @return a new mutable map including the {@code axes}
+ */
+ public static Map<String, Object> readOnlyAxisOption(Set<ReadOnlyAxis> axes) {
+ return addReadOnlyAxisOption(null, axes);
+ }
+
+ /**
+ * Queries the read-only axes to be enforced by a {@code transaction}.
+ *
+ * @param transaction
+ * a transaction
+ * @return its read-only axes, which are {@linkplain ReadOnlyAxis#anyAxis() all of them} by default if the option is absent
+ */
+ @SuppressWarnings("unchecked")
+ public static Set<ReadOnlyAxis> getReadOnlyAxisOption(Transaction transaction) {
+ Set<ReadOnlyAxis> result;
+
+ Object value = transaction.getOptions().get(TRANSACTION_OPTION_READ_ONLY_AXIS);
+ if (value instanceof Set<?>) {
+ result = (Set<ReadOnlyAxis>) value;
+ } else if (value instanceof Iterable<?>) {
+ result = Sets.immutableEnumSet((Iterable<ReadOnlyAxis>) value);
+ } else {
+ result = ReadOnlyAxis.anyAxis();
+ }
+
+ return result;
+ }
+
+ /**
+ * Merges the option to disable read-only state caching into an existing map of {@code options}.
+ *
+ * @param options
+ * an existing (non-{@code null}) options map
+ * @param disableCache
+ * whether to disable read-only caching
+ * @return the augmented {@code options}
+ * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
+ */
+ @Deprecated
+ public static Map<String, Object> mergeDisableReadOnlyOption(Map<String, Object> options, boolean disableCache) {
+ options.put(TRANSACTION_OPTION_NO_READ_ONLY_CACHE, disableCache);
+ return options;
+ }
+
+ /**
+ * Adds the option to disable read-only state caching to a transaction's {@code options}.
+ *
+ * @param options
+ * an options map, which may be {@code null} or immutable
+ * @param disableCache
+ * whether to disable read-only caching
+ * @return a new map based on the {@code options} and including the {@code disableCache} option
+ * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
+ */
+ @Deprecated
+ public static Map<String, Object> addDisableReadOnlyCacheOption(Map<String, ?> options, boolean disableCache) {
+ Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
+ result.put(TRANSACTION_OPTION_NO_READ_ONLY_CACHE, disableCache);
+ return result;
+ }
+
+ /**
+ * Creates a new mutable transaction options map with the option to disable caching of read-only state for objects and resources.
+ *
+ * @param disableCache
+ * whether to disable read-only caching
+ * @return a new mutable map including the {@code disableCache} option
+ * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
+ */
+ @Deprecated
+ public static Map<String, Object> disableReadOnlyCacheOption(boolean disableCache) {
+ return addDisableReadOnlyCacheOption(null, disableCache);
+ }
+
+ /**
+ * Queries whether a {@code transaction} is running with caching of read-only state of objects and resources disabled.
+ *
+ * @param transaction
+ * a transaction
+ * @return {@code true} if the {@code transaction} has the {@linkplain #TRANSACTION_OPTION_NO_READ_ONLY_CACHE interactive option} set {@code true}; {@code false}, otherwise (including the default case of no option set)
+ * @deprecated Since 1.1 M7 read-only state of resources is always cached and the cache lifecycle is not tied to transactions.
+ */
+ @Deprecated
+ public static boolean isReadOnlyCacheDisabled(Transaction transaction) {
+ Object value = transaction.getOptions().get(TRANSACTION_OPTION_NO_READ_ONLY_CACHE);
+ return (value instanceof Boolean) ? (Boolean) value : false;
+ }
+
+ /**
+ * Merges the option to merge nested read-only transactions with parent write transactions into an existing map of {@code options}.
+ * This option on a write transaction affects this behaviour of subsequent child read-only transactions.
+ *
+ * @param options
+ * an existing (non-{@code null}) options map
+ * @param mergeReadOnly
+ * whether to merge read-only transactions into parent write transactions
+ * @return the augmented {@code options}
+ */
+ public static Map<String, Object> mergeMergeReadOnlyOption(Map<String, Object> options, boolean mergeReadOnly) {
+ options.put(TRANSACTION_OPTION_MERGE_NESTED_READ, mergeReadOnly);
+ return options;
+ }
+
+ /**
+ * Adds the option to merge nested read-only transactions with parent write transactions to a transaction's {@code options}.
+ * This option on a write transaction affects this behaviour of subsequent child read-only transactions.
+ *
+ * @param options
+ * an options map, which may be {@code null} or immutable
+ * @param mergeReadOnly
+ * whether to merge read-only transactions into parent write transactions
+ * @return a new map based on the {@code options} and including the {@code mergeReadOnly} option
+ */
+ public static Map<String, Object> addMergeReadOnlyOption(Map<String, ?> options, boolean mergeReadOnly) {
+ Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
+ result.put(TRANSACTION_OPTION_MERGE_NESTED_READ, mergeReadOnly);
+ return result;
+ }
+
+ /**
+ * Creates a new mutable transaction options map with the option to merge nested read-only transactions with parent write transactions.
+ * This option on a write transaction affects this behaviour of subsequent child read-only transactions.
+ *
+ * @param mergeReadOnly
+ * whether to merge read-only transactions into parent write transactions
+ * @return a new mutable map including the {@code mergeReadOnly} option
+ */
+ public static Map<String, Object> mergeReadOnlyOption(boolean mergeReadOnly) {
+ return addMergeReadOnlyOption(null, mergeReadOnly);
+ }
+
+ /**
+ * Queries whether a {@code transaction} is running with merging of nested read-only transactions with parent write transactions enabled.
+ *
+ * @param transaction
+ * a transaction
+ * @return {@code true} if the {@code transaction} has the {@linkplain #TRANSACTION_OPTION_MERGE_NESTED_READ interactive option} set {@code true}; {@code false}, otherwise (including the default case of no option set)
+ */
+ public static boolean isMergeReadOnly(Transaction transaction) {
+ Object value = transaction.getOptions().get(TRANSACTION_OPTION_MERGE_NESTED_READ);
+ return (value instanceof Boolean) ? (Boolean) value : false;
+ }
+
+ /**
+ * Merges the {@code interactive} transaction option into an existing map of {@code options}.
+ *
+ * @param options
+ * an existing (non-{@code null}) options map
+ * @param interactive
+ * whether the transaction is in an user-interactive context
+ * @return the augmented {@code options}
+ */
+ public static Map<String, Object> mergeInteractiveOption(Map<String, Object> options, boolean interactive) {
+ options.put(TRANSACTION_OPTION_INTERACTIVE, interactive);
+ return options;
+ }
+
+ /**
+ * Adds the {@code interactive} option option to a transaction's {@code options}.
+ *
+ * @param options
+ * an options map, which may be {@code null} or immutable
+ * @param interactive
+ * whether the transaction is in an user-interactive context
+ * @return a new map based on the {@code options} and including the {@code interactive} option
+ */
+ public static Map<String, Object> addInteractiveOption(Map<String, ?> options, boolean interactive) {
+ Map<String, Object> result = (options == null) ? Maps.<String, Object> newHashMap() : Maps.newHashMap(options);
+ result.put(TRANSACTION_OPTION_INTERACTIVE, interactive);
+ return result;
+ }
+
+ /**
+ * Creates a new mutable transaction options map with an {@code interactive} option.
+ *
+ * @param interactive
+ * whether the transaction is in an user-interactive context
+ * @return a new mutable map including the {@code interactive} option
+ */
+ public static Map<String, Object> interactiveOption(boolean interactive) {
+ return addInteractiveOption(null, interactive);
+ }
+
+ /**
+ * Queries whether a {@code transaction} is running in a user-interactive context. In practice, this means that it would be appropriate to
+ * prompt the user to make resources/objects writable if necessary.
+ *
+ * @param transaction
+ * a transaction
+ * @return {@code false} if the {@code transaction} has the {@linkplain #TRANSACTION_OPTION_INTERACTIVE interactive option} set {@code false}; {@code true}, otherwise (including the default case of no option set)
+ */
+ public static boolean isInteractive(Transaction transaction) {
+ Object value = transaction.getOptions().get(TRANSACTION_OPTION_INTERACTIVE);
+ return (value instanceof Boolean) ? (Boolean) value : true;
+ }
+
+ /**
+ * Queries whether a {@code transaction} is one that is executing or that executed triggers, or is perhaps
+ * the read-only post-commit transaction that tells listeners about the changes made by a trigger transaction.
+ *
+ * @param transaction
+ * a transaction
+ * @return whether it is a trigger transaction
+ */
+ public static boolean isTriggerTransaction(Transaction transaction) {
+ Object value = transaction.getOptions().get(TransactionImpl.OPTION_IS_TRIGGER_TRANSACTION);
+ return (value instanceof Boolean) ? (Boolean) value : false;
+ }
+
+ /**
+ * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
+ * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
+ *
+ * @param domain
+ * a transactional editing domain. May not be {@code null}
+ * @param fallback
+ * an executor to use for scheduling tasks when the {@code domain} does not have a
+ * write transaction open. May not be {@code null}
+ */
+ public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback) {
+ return createTransactionExecutor(domain, fallback, null, null);
+ }
+
+ /**
+ * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
+ * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
+ * The specified {@code policy}, if any, may determine whether for some task the {@code fallback} should be preferred
+ * over the transaction executor or vice-versa (such as to handle special requirements like tasks needing to run
+ * on the UI thread).
+ *
+ * @param domain
+ * a transactional editing domain. May not be {@code null}
+ * @param fallback
+ * an executor to use for scheduling tasks when the {@code domain} does not have a
+ * write transaction open. May not be {@code null}
+ * @param policy
+ * an optional executor selection policy (may be {@code null}). The policy is queried for ranking of both
+ * the transaction executor and the {@code fallback}, unless there is no transaction active, which always excludes the
+ * transaction executor. In the event of a tie, the transaction executor is always selected
+ */
+ public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback, IExecutorPolicy policy) {
+ return createTransactionExecutor(domain, fallback, policy, null);
+ }
+
+ /**
+ * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
+ * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
+ *
+ * @param domain
+ * a transactional editing domain. May not be {@code null}
+ * @param fallback
+ * an executor to use for scheduling tasks when the {@code domain} does not have a
+ * write transaction open. May not be {@code null}
+ * @param options
+ * a map of options to apply to the nested transaction in which tasks are executed. May be {@code null} if not needed
+ */
+ public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback, Map<?, ?> options) {
+ return createTransactionExecutor(domain, fallback, null, options);
+ }
+
+ /**
+ * Creates an {@link Executor} that executes {@link Runnable}s at the pre-commit phase of the active write
+ * transaction of the specified editing {@code domain} or at some other time if no write transaction is active.
+ * The specified {@code policy}, if any, may determine whether for some task the {@code fallback} should be preferred
+ * over the transaction executor or vice-versa (such as to handle special requirements like tasks needing to run
+ * on the UI thread).
+ *
+ * @param domain
+ * a transactional editing domain. May not be {@code null}
+ * @param fallback
+ * an executor to use for scheduling tasks when the {@code domain} does not have a
+ * write transaction open. May not be {@code null}
+ * @param policy
+ * an optional executor selection policy (may be {@code null}). The policy is queried for ranking of both
+ * the transaction executor and the {@code fallback}, unless there is no transaction active, which always excludes the
+ * transaction executor. In the event of a tie, the transaction executor is always selected
+ * @param options
+ * a map of options to apply to the nested transaction in which tasks are executed. May be {@code null} if not needed
+ */
+ public static Executor createTransactionExecutor(TransactionalEditingDomain domain, Executor fallback, IExecutorPolicy policy, Map<?, ?> options) {
+ if ((domain == null) || (fallback == null)) {
+ throw new NullPointerException();
+ }
+
+ return new TransactionPrecommitExecutor(domain, fallback, policy, options);
+ }
+
+ /**
+ * Create a privileged progress runnable, which is like a regular {@linkplain TransactionalEditingDomain#createPrivilegedRunnable(Runnable)
+ * privileged runnable} except that it is given a progress monitor for progress reporting.
+ *
+ * @param domain
+ * an editing domain
+ * @param runnable
+ * a progress runnable that is to borrow the {@code domain}'s active transaction on the modal context thread
+ * @return the privileged runnable, ready to pass into the {@link IExecutorService} or other such API
+ */
+ public static IProgressRunnable createPrivilegedRunnable(TransactionalEditingDomain domain, final IProgressRunnable runnable) {
+ IProgressMonitor monitorHolder[] = { null };
+
+ Runnable privileged = domain.createPrivilegedRunnable(() -> runnable.run(monitorHolder[0]));
+
+ return monitor -> {
+ monitorHolder[0] = monitor;
+ privileged.run();
+ };
+ }
+
+ /**
+ * Create a privileged progress callable, which is like a {@linkplain TransactionalEditingDomain#createPrivilegedRunnable(Runnable)
+ * privileged runnable} except that it is given a progress monitor for progress reporting and it computes a result.
+ *
+ * @param callable
+ * an editing domain
+ * @param callable
+ * a progress callable that is to borrow the {@code domain}'s active transaction on the modal context thread
+ * @return the privileged callable, ready to pass into the {@link IExecutorService} or other such API
+ */
+ public static <V> IProgressCallable<V> createPrivilegedCallable(TransactionalEditingDomain domain, final IProgressCallable<V> callable) {
+ IProgressMonitor monitorHolder[] = { null };
+ AtomicReference<V> resultHolder = new AtomicReference<V>();
+ Exception failHolder[] = { null };
+
+ Runnable privileged = domain.createPrivilegedRunnable(() -> {
+ try {
+ resultHolder.set(callable.call(monitorHolder[0]));
+ } catch (Exception e) {
+ failHolder[0] = e;
+ }
+ });
+
+ return monitor -> {
+ monitorHolder[0] = monitor;
+
+ privileged.run();
+
+ if (failHolder[0] != null) {
+ throw failHolder[0];
+ }
+
+ return resultHolder.get();
+ };
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/.classpath b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.classpath
new file mode 100644
index 00000000000..eca7bdba8f0
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/.project b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.project
new file mode 100644
index 00000000000..29096480f24
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.papyrus.infra.tools</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.core.prefs b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..b3aa6d60f94
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,291 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=260
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=false
+org.eclipse.jdt.core.formatter.join_wrapped_lines=false
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=260
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=5
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.ui.prefs b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 00000000000..954281dbc31
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,68 @@
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=true
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_functional_interfaces=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=false
+cleanup.format_source_code=false
+cleanup.format_source_code_changes_only=false
+cleanup.insert_inferred_type_arguments=false
+cleanup.make_local_variable_final=true
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=true
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=false
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=false
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_redundant_type_arguments=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_anonymous_class_creation=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_lambda=true
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup.use_type_arguments=false
+cleanup_profile=_Papyrus
+cleanup_settings_version=2
+eclipse.preferences.version=1
+formatter_profile=_Papyrus
+formatter_settings_version=12
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="false" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * Constructor.\n *\n * ${tags}\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*****************************************************************************\n * Copyright (c) ${year} CEA LIST and others.\n * \n * All rights reserved. This program and the accompanying materials\n * are made available under the terms of the Eclipse Public License v1.0\n * which accompanies this distribution, and is available at\n * http\://www.eclipse.org/legal/epl-v10.html\n *\n * Contributors\:\n * CEA LIST - Initial API and implementation\n * \n *****************************************************************************/\n</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * @author ${user}\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/**\n * ${see_to_overridden}\n *\n * ${tags}\n */</template><template autoinsert\="false" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${see_to_target}\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF b/plugins/infra/core/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..6960891c38a
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Export-Package: org.eclipse.papyrus.infra.tools,
+ org.eclipse.papyrus.infra.tools.comparator,
+ org.eclipse.papyrus.infra.tools.databinding,
+ org.eclipse.papyrus.infra.tools.notify,
+ org.eclipse.papyrus.infra.tools.spi,
+ org.eclipse.papyrus.infra.tools.util
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.papyrus.infra.core.log;bundle-version="1.2.0",
+ org.eclipse.core.databinding;bundle-version="1.4.1",
+ org.eclipse.emf.ecore.xmi;bundle-version="2.9.0",
+ 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.2.0.qualifier
+Eclipse-BuddyPolicy: dependent
+Bundle-Name: %Bundle-Name
+Bundle-Activator: org.eclipse.papyrus.infra.tools.Activator
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.papyrus.infra.tools
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/OSGI-INF/l10n/bundle.properties b/plugins/infra/core/org.eclipse.papyrus.infra.tools/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 00000000000..a09eb7ffd46
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,3 @@
+#Properties file for org.eclipse.papyrus.infra.tools
+Bundle-Vendor = Eclipse Modeling Project
+Bundle-Name = Papyrus Tools \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/about.html b/plugins/infra/core/org.eclipse.papyrus.infra.tools/about.html
new file mode 100644
index 00000000000..dd3c089a94c
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>November 14, 2008</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/build.properties b/plugins/infra/core/org.eclipse.papyrus.infra.tools/build.properties
new file mode 100644
index 00000000000..0565497ea41
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ about.html,\
+ OSGI-INF/
+src.includes = about.html
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.tools/pom.xml
new file mode 100644
index 00000000000..6c56c86b074
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/pom.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.eclipse.papyrus</groupId>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>org.eclipse.papyrus.infra.tools</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project> \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/Activator.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/Activator.java
new file mode 100644
index 00000000000..4c598486f70
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/Activator.java
@@ -0,0 +1,93 @@
+/*****************************************************************************
+ * Copyright (c) 2011, 2016 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 = bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.papyrus.infra.core.log.LogHelper;
+import org.eclipse.papyrus.infra.tools.spi.INotificationBuilderFactory;
+import org.eclipse.papyrus.infra.tools.util.IExecutorService;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends Plugin {
+
+ /**
+ * The plug-in ID
+ */
+ public static final String PLUGIN_ID = "org.eclipse.papyrus.infra.tools"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The plug-in's logger
+ */
+ public static LogHelper log;
+
+ private DelegatingUIExecutorService uiExecutorService;
+
+ private ServiceTracker<INotificationBuilderFactory, INotificationBuilderFactory> notificationBuilderTracker;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ log = new LogHelper(this);
+
+ notificationBuilderTracker = new ServiceTracker<>(context, INotificationBuilderFactory.class, null);
+ notificationBuilderTracker.open();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ notificationBuilderTracker.close();
+
+ if (uiExecutorService != null) {
+ uiExecutorService.shutdown(context);
+ uiExecutorService = null;
+ }
+
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ public synchronized IExecutorService getUIExecutorService() {
+ if (uiExecutorService == null) {
+ uiExecutorService = new DelegatingUIExecutorService(getBundle().getBundleContext());
+ }
+ return uiExecutorService;
+ }
+
+ public INotificationBuilderFactory getNotificationBuilderFactory() {
+ return notificationBuilderTracker.getService();
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/DelegatingUIExecutorService.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/DelegatingUIExecutorService.java
new file mode 100644
index 00000000000..2efa8fc0ede
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/DelegatingUIExecutorService.java
@@ -0,0 +1,235 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.papyrus.infra.tools.spi.IExecutorServiceFactory;
+import org.eclipse.papyrus.infra.tools.util.IExecutorService;
+import org.eclipse.papyrus.infra.tools.util.IProgressCallable;
+import org.eclipse.papyrus.infra.tools.util.IProgressRunnable;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * An executor service that delegates to the UI executor provided by the
+ * registered factory service, if available. Otherwise, it delegates to
+ * a default single-thread executor. Reacts to registration changes in
+ * the {@link IExecutorServiceFactory} by replacing the default executor
+ * with a "real" UI executor when available and replacing the "real" UI
+ * executor with a default executor when no longer available.
+ */
+class DelegatingUIExecutorService implements IExecutorService, ServiceTrackerCustomizer<IExecutorServiceFactory, IExecutorService> {
+ private final BundleContext context;
+ private final ServiceTracker<IExecutorServiceFactory, IExecutorService> tracker;
+ private ExecutorService delegate;
+
+ DelegatingUIExecutorService(BundleContext context) {
+ this.context = context;
+ this.tracker = new ServiceTracker<>(context, IExecutorServiceFactory.class, this);
+
+ tracker.open();
+
+ delegate = tracker.getService();
+ if (delegate == null) {
+ delegate = Executors.newSingleThreadExecutor();
+ }
+ }
+
+ synchronized void shutdown(BundleContext context) {
+ if (context == this.context) {
+ if (delegate != null) {
+ delegate.shutdown();
+ delegate = null;
+ }
+
+ tracker.close();
+ }
+ }
+
+ @Override
+ public synchronized IExecutorService addingService(ServiceReference<IExecutorServiceFactory> reference) {
+ IExecutorService result = context.getService(reference).createExecutor();
+
+ if (delegate != null) {
+ delegate.shutdown();
+ }
+
+ delegate = result;
+
+ return result;
+ }
+
+ @Override
+ public synchronized void removedService(ServiceReference<IExecutorServiceFactory> reference, IExecutorService service) {
+ context.ungetService(reference);
+
+ if (service == delegate) {
+ delegate.shutdown();
+ delegate = Executors.newSingleThreadExecutor();
+ }
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<IExecutorServiceFactory> reference, IExecutorService service) {
+ // Pass
+ }
+
+ //
+ // ExecutorService protocol
+ //
+
+ @Override
+ public void shutdown() {
+ throw new IllegalStateException("Executor is shared"); //$NON-NLS-1$
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ throw new IllegalStateException("Executor is shared"); //$NON-NLS-1$
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ delegate.execute(command);
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return delegate.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return delegate.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return delegate.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return delegate.submit(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return delegate.submit(task, result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return delegate.submit(task);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
+ return delegate.invokeAll(tasks);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
+ return delegate.invokeAll(tasks, timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+ return delegate.invokeAny(tasks);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return delegate.invokeAny(tasks, timeout, unit);
+ }
+
+ //
+ // IExecutorService protocol
+ //
+
+ @Override
+ public void syncExec(Runnable task) throws InterruptedException, ExecutionException {
+ if (delegate instanceof IExecutorService) {
+ ((IExecutorService) delegate).syncExec(task);
+ } else {
+ Future<?> future = delegate.submit(task);
+ // Wait for it
+ future.get();
+ }
+ }
+
+ @Override
+ public <V> V syncCall(Callable<V> callable) throws InterruptedException, ExecutionException {
+ if (delegate instanceof IExecutorService) {
+ return ((IExecutorService) delegate).syncCall(callable);
+ } else {
+ Future<V> future = delegate.submit(callable);
+ // Wait for it
+ return future.get();
+ }
+ }
+
+ @Override
+ public Future<?> submit(IProgressRunnable task) {
+ if (delegate instanceof IExecutorService) {
+ return ((IExecutorService) delegate).submit(task);
+ } else {
+ return delegate.submit(() -> task.run(new NullProgressMonitor()));
+ }
+ }
+
+ @Override
+ public <V> Future<V> submit(IProgressCallable<V> callable) {
+ if (delegate instanceof IExecutorService) {
+ return ((IExecutorService) delegate).submit(callable);
+ } else {
+ return delegate.submit(() -> callable.call(new NullProgressMonitor()));
+ }
+ }
+
+ @Override
+ public void syncExec(IProgressRunnable task) throws InterruptedException, ExecutionException {
+ if (delegate instanceof IExecutorService) {
+ ((IExecutorService) delegate).syncExec(task);
+ } else {
+ Future<?> future = delegate.submit(() -> task.run(new NullProgressMonitor()));
+ // Wait for it
+ future.get();
+ }
+ }
+
+ @Override
+ public <V> V syncCall(IProgressCallable<V> callable) throws InterruptedException, ExecutionException {
+ if (delegate instanceof IExecutorService) {
+ return ((IExecutorService) delegate).syncCall(callable);
+ } else {
+ Future<V> future = delegate.submit(() -> callable.call(new NullProgressMonitor()));
+ // Wait for it
+ return future.get();
+ }
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/comparator/CompositeComparator.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/comparator/CompositeComparator.java
new file mode 100644
index 00000000000..535294eb0df
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/comparator/CompositeComparator.java
@@ -0,0 +1,63 @@
+/*****************************************************************************
+ * Copyright (c) 2013 CEA LIST.
+ *
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.comparator;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class allows to compare elements on several levels
+ *
+ * @author vl222926
+ * @param <T>
+ *
+ */
+
+public class CompositeComparator<T> implements Comparator<T> {
+
+ /**
+ * the list of the comparator
+ */
+ private final List<Comparator<T>> comparators;
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param comparators
+ */
+ public CompositeComparator(final List<Comparator<T>> comparators) {
+ this.comparators = comparators;
+ }
+
+ /**
+ *
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ *
+ * @param o1
+ * @param o2
+ * @return
+ */
+ public int compare(T o1, T o2) {
+ int res = 0;
+ final Iterator<Comparator<T>> iter = comparators.iterator();
+ while (iter.hasNext() && res == 0) {
+ final Comparator<T> current = iter.next();
+ res = current.compare(o1, o2);
+ }
+ return res;
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/AggregatedObservable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/AggregatedObservable.java
new file mode 100644
index 00000000000..7b59541b4f0
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/AggregatedObservable.java
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * Copyright (c) 2011 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import org.eclipse.core.databinding.observable.IObservable;
+
+/**
+ * An interface to aggregate many IObservables in a single one.
+ * A modification on the aggregated observable should be dispatched to all
+ * the encapsulated observables.
+ *
+ * @author Camille Letavernier
+ */
+public interface AggregatedObservable extends IObservable {
+
+ /**
+ * Aggregates the current Observable with the given observable.
+ * Returns the aggregated Observable, or null if the aggregation is not
+ * possible
+ *
+ * Typically, only IObservable with similar ValueTypes can be
+ * aggregated.
+ *
+ * @param observable
+ * The IObservable to aggregate to the current IObservable
+ * @return
+ * The aggregated IObservable, or null if the aggregation is not
+ * possible
+ */
+ public AggregatedObservable aggregate(IObservable observable);
+
+ /**
+ * Tests if the sub-observables have different values
+ *
+ * @return true if the sub-observables have different values
+ */
+ public boolean hasDifferentValues();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservable.java
new file mode 100644
index 00000000000..1e10dec5a1e
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservable.java
@@ -0,0 +1,27 @@
+/*****************************************************************************
+ * Copyright (c) 2011 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.emf.common.command.Command;
+
+
+public interface CommandBasedObservable extends IObservable {
+
+ /**
+ * Returns the EMF Command for modifying this Observable's value
+ *
+ * @param value
+ * @return
+ */
+ public Command getCommand(Object value);
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservableValue.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservableValue.java
new file mode 100644
index 00000000000..06450cf6856
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/CommandBasedObservableValue.java
@@ -0,0 +1,19 @@
+/*****************************************************************************
+ * Copyright (c) 2011 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+
+public interface CommandBasedObservableValue extends CommandBasedObservable, IObservableValue {
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingInvocationHandler.java
new file mode 100644
index 00000000000..838c6a36a93
--- /dev/null
+++ b/plugins/infra/core/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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java
new file mode 100644
index 00000000000..7f2f0206e20
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservable.java
@@ -0,0 +1,347 @@
+/*
+ * 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()));
+ }
+
+ @Override
+ 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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableCollection.java
new file mode 100644
index 00000000000..88c1079029b
--- /dev/null
+++ b/plugins/infra/core/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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableList.java
new file mode 100644
index 00000000000..f7e9fc0e752
--- /dev/null
+++ b/plugins/infra/core/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 = 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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableSet.java
new file mode 100644
index 00000000000..c91aa4f2ed4
--- /dev/null
+++ b/plugins/infra/core/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 = 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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/DelegatingObservableValue.java
new file mode 100644
index 00000000000..9b935c26f36
--- /dev/null
+++ b/plugins/infra/core/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 = 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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java
new file mode 100644
index 00000000000..81bc3f9cd2b
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IDelegatingObservable.java
@@ -0,0 +1,52 @@
+/*
+ * 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/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IMultipleObservableValue.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IMultipleObservableValue.java
new file mode 100644
index 00000000000..b2fd4b1cfeb
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/IMultipleObservableValue.java
@@ -0,0 +1,37 @@
+/*****************************************************************************
+ * 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+
+
+/**
+ * Interface for a multiple selection of observable values.
+ */
+public interface IMultipleObservableValue extends AggregatedObservable, IObservableValue {
+
+ /**
+ * @return the list of sub-observable values
+ */
+ List<IObservableValue> getObservableValues();
+
+ /**
+ * @return the list of observed values
+ */
+ List<Object> getObservedValues();
+
+} \ No newline at end of file
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java
new file mode 100644
index 00000000000..fb683dbc4c2
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/MultipleObservableValue.java
@@ -0,0 +1,181 @@
+/*****************************************************************************
+ * 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
+ * Christian W. Damus (CEA) - bug 444227
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.util.Collection;
+import java.util.LinkedList;
+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.IObservableValue;
+import org.eclipse.core.databinding.observable.value.ValueDiff;
+
+/**
+ * MultipleObservableValue is used to map a single element
+ * to a collection of model elements.
+ *
+ * It is especially used when displaying a Property View for multiple elements,
+ * when we want to edit the same property for all of them.
+ *
+ * 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 ReferenceCountedObservable.Value implements IMultipleObservableValue, IChangeListener {
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param values
+ * The collection of sub-elements for this MultipleObservableValue
+ *
+ */
+ public MultipleObservableValue(Collection<IObservableValue> values) {
+ if (values != null) {
+ observableValues.addAll(values);
+ }
+ }
+
+ /**
+ *
+ * Constructor.
+ *
+ */
+ public MultipleObservableValue() {
+
+ }
+
+ public Object getValueType() {
+ if (observableValues.isEmpty()) {
+ return null;
+ }
+
+ return observableValues.get(0).getValueType();
+ }
+
+ /**
+ * If all objects have the same value, returns this value
+ * Otherwise, returns the defaultGetValue
+ * If the defaultGetValue hasn't been set, returns null
+ */
+ @Override
+ protected Object doGetValue() {
+ if (hasDifferentValues() || observableValues.isEmpty()) {
+ return null;
+ }
+
+ return observableValues.get(0).getValue();
+ }
+
+ private boolean equals(Object value, Object currentValue) {
+ if (value == currentValue) {
+ return true;
+ }
+ if (value == null) {
+ return false;
+ }
+ return value.equals(currentValue);
+ }
+
+ @Override
+ protected void doSetValue(Object value) {
+ for (IObservableValue observable : observableValues) {
+ observable.setValue(value);
+ }
+ }
+
+ public AggregatedObservable aggregate(IObservable observable) {
+ if (observable instanceof IObservableValue) {
+ ReferenceCountedObservable.Util.retain(observable);
+ observableValues.add((IObservableValue) observable);
+ observable.addChangeListener(this);
+ return this;
+ }
+ return null;
+ }
+
+ public List<IObservableValue> getObservableValues() {
+ return observableValues;
+ }
+
+ public List<Object> getObservedValues() {
+ List<Object> result = new LinkedList<Object>();
+ for (IObservableValue value : getObservableValues()) {
+ result.add(value.getValue());
+ }
+ return result;
+ }
+
+ @Override
+ public synchronized void dispose() {
+ super.dispose();
+ for (IObservableValue observable : observableValues) {
+ observable.removeChangeListener(this);
+
+ // I don't own my observables, so I just release them
+ ReferenceCountedObservable.Util.release(observable);
+ }
+
+ observableValues.clear();
+ }
+
+ /**
+ * The {@link IObservableValue}s aggregated by this Observable
+ */
+ protected List<IObservableValue> observableValues = new LinkedList<IObservableValue>();
+
+ public boolean hasDifferentValues() {
+ if (observableValues.isEmpty()) {
+ return false;
+ }
+
+ Object currentValue = null;
+ boolean firstValue = true;
+ for (IObservableValue observable : observableValues) {
+ if (firstValue) {
+ firstValue = false;
+ currentValue = observable.getValue();
+ } else {
+ Object value = observable.getValue();
+ if (equals(value, currentValue)) {
+ continue;
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void handleChange(ChangeEvent event) {
+ // We're not interested in the old and new values
+ // We just return two different values so that a change event is fired
+ super.fireValueChange(new ValueDiff() {
+
+ @Override
+ public Object getOldValue() {
+ return true;
+ }
+
+ @Override
+ public Object getNewValue() {
+ return false;
+ }
+ });
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
new file mode 100644
index 00000000000..d96f2694f0c
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/ReferenceCountedObservable.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ * Christian W. Damus - bug 487027
+ *
+ */
+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);
+ }
+
+ @Override
+ public void retain() {
+ refCount.retain();
+ }
+
+ @Override
+ public void release() {
+ refCount.release();
+ }
+
+ @Override
+ 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<T> extends AbstractObservableValue<T> implements ReferenceCountedObservable {
+
+ private final Support refCount = new Support(this);
+
+ public Value() {
+ super();
+ }
+
+ public Value(Realm realm) {
+ super(realm);
+ }
+
+ @Override
+ public void retain() {
+ refCount.retain();
+ }
+
+ @Override
+ public void release() {
+ refCount.release();
+ }
+
+ @Override
+ 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 {
+
+ @Override
+ 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);
+ }
+ }
+
+ @Override
+ public void handleDispose(DisposeEvent event) {
+ if (event.getObservable() == get()) {
+ clear();
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java
new file mode 100644
index 00000000000..438f111b664
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/TouchableValue.java
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import java.util.Objects;
+
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+
+/**
+ * An analogue of the {@link WritableValue} that supports "touches" to send
+ * change events even though the value is not replaced.
+ */
+public class TouchableValue<T> extends ReferenceCountedObservable.Value<T> {
+ private final Class<? extends T> type;
+
+ private T value;
+
+ public TouchableValue(Realm realm, Class<? extends T> type) {
+ super(realm);
+
+ this.type = type;
+ }
+
+ public TouchableValue(Realm realm, Class<? extends T> type, T initialValue) {
+ super(realm);
+
+ this.type = type;
+ this.value = initialValue;
+ }
+
+ @Override
+ public Object getValueType() {
+ return type;
+ }
+
+ @Override
+ protected T doGetValue() {
+ return value;
+ }
+
+ @Override
+ protected void doSetValue(T value) {
+ if (!Objects.equals(this.value, value)) {
+ T oldValue = this.value;
+ this.value = value;
+ fireValueChange(Diffs.createValueDiff(oldValue, value));
+ }
+ }
+
+ /**
+ * Indicates that some kind of change has happened to the observable's value
+ * that observers should know about, but for which specific change details
+ * are not available.
+ */
+ public void touch() {
+ checkRealm();
+ fireEvent(new ChangeEvent(this));
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java
new file mode 100644
index 00000000000..236d80cfcef
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/databinding/WritableListWithIterator.java
@@ -0,0 +1,288 @@
+/*****************************************************************************
+ * Copyright (c) 2015, 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.databinding;
+
+import static org.eclipse.core.databinding.observable.Diffs.createListDiff;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.ListDiffEntry;
+import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
+import org.eclipse.core.databinding.observable.list.WritableList;
+
+/**
+ * A specialization of the core Databindings {@link WritableList} providing
+ * iterators that support modification.
+ */
+public class WritableListWithIterator<E> extends WritableList<E> implements ReferenceCountedObservable {
+
+ private final ReferenceCountedObservable.Support refCount = new ReferenceCountedObservable.Support(this);
+
+ private final ListDiffVisitor<E> mutationHook = createMutationHook();
+
+ public WritableListWithIterator() {
+ super();
+ }
+
+ public WritableListWithIterator(Realm realm) {
+ super(realm);
+ }
+
+ public WritableListWithIterator(List<E> toWrap, Object elementType) {
+ super(toWrap, elementType);
+ }
+
+ public WritableListWithIterator(Collection<E> collection, Object elementType) {
+ super(collection, elementType);
+ }
+
+ public WritableListWithIterator(Realm realm, List<E> toWrap, Object elementType) {
+ super(realm, toWrap, elementType);
+ }
+
+ public WritableListWithIterator(Realm realm, Collection<E> collection, Object elementType) {
+ super(realm, collection, elementType);
+ }
+
+ //
+ // Mutation hooks
+ //
+
+ void didAdd(E element) {
+ // Pass
+ }
+
+ void didRemove(E element) {
+ // Pass
+ }
+
+ private ListDiffVisitor<E> createMutationHook() {
+ return new ListDiffVisitor<E>() {
+ @Override
+ public void handleAdd(int index, E element) {
+ didAdd(element);
+ }
+
+ @Override
+ public void handleRemove(int index, E element) {
+ didRemove(element);
+ }
+ };
+ }
+
+ @Override
+ protected void fireListChange(ListDiff<E> diff) {
+ diff.accept(mutationHook);
+
+ super.fireListChange(diff);
+ }
+
+ //
+ // Reference counting
+ //
+
+ @Override
+ public void retain() {
+ refCount.retain();
+ }
+
+ @Override
+ public void release() {
+ refCount.release();
+ }
+
+ @Override
+ public void autorelease() {
+ refCount.autorelease();
+ }
+
+ //
+ // Iteration
+ //
+
+ @Override
+ public Iterator<E> iterator() {
+ getterCalled();
+ return new Iter();
+ }
+
+ @Override
+ public ListIterator<E> listIterator() {
+ getterCalled();
+ return new ListIter(0);
+ }
+
+ @Override
+ public ListIterator<E> listIterator(int index) {
+ getterCalled();
+ return new ListIter(index);
+ }
+
+ static <E> ListDiffEntry<E> added(E element, int position) {
+ return Diffs.createListDiffEntry(position, true, element);
+ }
+
+ static <E> ListDiffEntry<E> removed(E element, int position) {
+ return Diffs.createListDiffEntry(position, false, element);
+ }
+
+ //
+ // Nested types
+ //
+
+ private class Iter implements Iterator<E> {
+
+ final ListIterator<E> delegate;
+ int lastReturned = -1;
+
+ Iter() {
+ this(0);
+ }
+
+ Iter(int index) {
+ super();
+
+ this.delegate = wrappedList.listIterator(index);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return delegate.hasNext();
+ }
+
+ @Override
+ public E next() {
+ E result = delegate.next();
+ lastReturned = delegate.previousIndex();
+ return result;
+ }
+
+ E lastReturned() {
+ return ((lastReturned >= 0) && (lastReturned < size()))
+ ? get(lastReturned)
+ : null;
+ }
+
+ @Override
+ public void remove() {
+ E removed = lastReturned();
+
+ delegate.remove();
+
+ // We only get this far if remove succeeded
+ fireListChange(createListDiff(removed(removed, lastReturned)));
+ }
+ }
+
+ private class ListIter extends Iter implements ListIterator<E> {
+
+ ListIter(int index) {
+ super(index);
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return delegate.hasPrevious();
+ }
+
+ @Override
+ public int nextIndex() {
+ return delegate.nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return delegate.previousIndex();
+ }
+
+ @Override
+ public E previous() {
+ E result = delegate.previous();
+ lastReturned = delegate.nextIndex();
+ return result;
+ }
+
+ @Override
+ public void set(E e) {
+ E removed = lastReturned();
+
+ delegate.set(e);
+
+ // We only get this far if remove succeeded
+ fireListChange(createListDiff(removed(removed, lastReturned), added(e, lastReturned)));
+ }
+
+ @Override
+ public void add(E e) {
+ delegate.add(e);
+
+ // We only get this far if add succeeded
+ fireListChange(createListDiff(added(e, previousIndex())));
+ }
+
+ }
+
+ /**
+ * A specialized writable list that owns its elements via strong (and counted) references.
+ * It does not support wrapping an externally-provided list.
+ */
+ public static class Containment<E extends ReferenceCountedObservable> extends WritableListWithIterator<E> {
+
+ public Containment() {
+ super();
+ }
+
+ public Containment(Object elementType) {
+ super(new ArrayList<E>(), elementType);
+ }
+
+ public Containment(Realm realm, Object elementType) {
+ super(realm, new ArrayList<E>(), elementType);
+ }
+
+ public Containment(Realm realm) {
+ super(realm);
+ }
+
+ @Override
+ public synchronized void dispose() {
+ super.dispose();
+
+ // Release my contained elements
+ wrappedList.forEach(ReferenceCountedObservable::release);
+ wrappedList.clear();
+ }
+
+ @Override
+ void didAdd(E element) {
+ if (element != null) {
+ element.retain();
+ }
+ }
+
+ @Override
+ void didRemove(E element) {
+ if (element != null) {
+ element.release();
+ }
+ }
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/IContext.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/IContext.java
new file mode 100644
index 00000000000..93d0e10e3dc
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/IContext.java
@@ -0,0 +1,65 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 ATOS ORIGIN, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.notify;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * The Class Context.
+ */
+public interface IContext {
+
+ /**
+ * This constant identifies developper strings to indicate the code location
+ */
+ public static String STRING_FOR_DEVELOPER = "_please_check_IContext_Constant";
+
+ /**
+ * This constant allows the user to retrieve the composite created if he filled a ICreationComposite
+ */
+ public static String COMPOSITE_CREATED = "composite_created" + STRING_FOR_DEVELOPER;
+
+ /**
+ * This constant allows the user to retrieve the notification if he needs to close it
+ */
+ public static String NOTIFICATION_OBJECT = "notification_object" + STRING_FOR_DEVELOPER;
+
+ /**
+ * This constant determines an action id to the current context
+ */
+ public static String ACTION_ID = "action_id";
+
+ public void put(String s, Object o);
+
+ public Object get(String s);
+
+ public static class Context implements IContext {
+
+ private Map<String, Object> objects = new HashMap<String, Object>();;
+
+ @Override
+ public void put(String s, Object o) {
+ objects.put(s, o);
+ }
+
+ @Override
+ public Object get(String s) {
+ return objects.get(s);
+ }
+
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotification.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotification.java
new file mode 100644
index 00000000000..55bc0f2f8cf
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotification.java
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 ATOS ORIGIN, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.notify;
+
+
+/**
+ * The Interface INotification.
+ * offers some services for a notification
+ */
+public interface INotification {
+
+ /** delete the current notification */
+ void delete();
+
+ /**
+ * whether the current notification is deleted
+ *
+ * @return true if notification is deleted
+ */
+ boolean isDeleted();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotificationBuilder.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotificationBuilder.java
new file mode 100644
index 00000000000..84bb3b4ace6
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/INotificationBuilder.java
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 ATOS ORIGIN, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.notify;
+
+/**
+ * Protocol of a pluggable notification builder.
+ */
+public interface INotificationBuilder {
+ /** asynchronous, determines if the message needs or not to be synchronous with the notification */
+ String ASYNCHRONOUS = "asynchronous";
+
+ /** a message displayed in the notification */
+ String MESSAGE = "message";
+
+ /** a default action in the notification */
+ String ACTION = "default_action";
+
+ /** a delay to display if it is a temporary notification */
+ String DELAY = "delay";
+
+ /** determines if the notification is temporary */
+ String TEMPORARY = "temporary";
+
+ /** a title displayed in the notification */
+ String TITLE = "title";
+
+ /** determines if there is html content in the notification */
+ String HTML = "html";
+
+ /** determines the type according to {@link Type} */
+ String TYPE = "type";
+
+ /**
+ * Set a message for the notification
+ *
+ * @param message
+ * , the message to display
+ * @return this
+ */
+ INotificationBuilder setMessage(String message);
+
+ /**
+ * Determines if the notification is asynchronous (don't force the user to read the notification immediately)
+ *
+ * @param asynchronous
+ * , true if it asynchronous
+ * @return this
+ */
+ INotificationBuilder setAsynchronous(boolean asynchronous);
+
+ /**
+ * Set a default action for the notification
+ *
+ * @param runnable
+ * , a runnable triggered when default action of the notification is selected
+ * The first action added is the default One
+ * @return this
+ */
+ INotificationBuilder addAction(NotificationRunnable runnable);
+
+ /**
+ * Set a delay if the notification is temporary
+ *
+ * @param delayMs
+ * , the delay in ms for visibility
+ * @return this
+ */
+ INotificationBuilder setDelay(long delayMs);
+
+ /**
+ * Set true if the notification is temporary
+ *
+ * @param temporary
+ * @return this
+ */
+ INotificationBuilder setTemporary(boolean temporary);
+
+ /**
+ * Set a title for the notification
+ *
+ * @param title
+ * , the title
+ * @return this
+ */
+ INotificationBuilder setTitle(String title);
+
+ /**
+ * Set if the notification has to understand HTML
+ *
+ * @param useHTML
+ * @return this
+ */
+ INotificationBuilder setHTML(boolean useHTML);
+
+ /**
+ * Set the type of the notification according to {@link Type}
+ *
+ * @param type
+ * , the desired type
+ * @return this
+ */
+ INotificationBuilder setType(Type type);
+
+ /**
+ * Allows the developer to use a specific parameter
+ *
+ * @param name
+ * , the key of the parameter
+ * @param value
+ * , the value
+ * @return this
+ */
+ INotificationBuilder setParameter(String name, Object value);
+
+ /**
+ * Creates a notification according to different parameters
+ */
+ INotification run();
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/LogNotification.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/LogNotification.java
new file mode 100644
index 00000000000..a4554cb06ed
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/LogNotification.java
@@ -0,0 +1,56 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.notify;
+
+import org.eclipse.papyrus.infra.tools.Activator;
+
+/**
+ * A simple notification that just emits a message to the log.
+ */
+public class LogNotification implements INotification {
+
+ public LogNotification(Type type, String message) {
+ super();
+
+ // No message? No log
+ if (message != null) {
+ if (type == null) {
+ type = Type.WARNING;
+ }
+ switch (type) {
+ case ERROR:
+ Activator.log.error(message, null);
+ break;
+ case WARNING:
+ Activator.log.warn(message);
+ break;
+ default:
+ Activator.log.info(message);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void delete() {
+ // A log message is not presented in the UI, so it is always deleted
+ }
+
+ @Override
+ public boolean isDeleted() {
+ // A log message is not presented in the UI, so it is always deleted
+ return true;
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationBuilder.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationBuilder.java
new file mode 100644
index 00000000000..9d54d4204d9
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationBuilder.java
@@ -0,0 +1,388 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 ATOS ORIGIN, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.notify;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.eclipse.papyrus.infra.tools.Activator;
+import org.eclipse.papyrus.infra.tools.spi.INotificationBuilderFactory;
+
+
+/**
+ * A class creating a notification,
+ * the run method launch the message according to the value of the attributes
+ *
+ * @author tristan faure
+ *
+ */
+public class NotificationBuilder implements INotificationBuilder {
+
+ /** The parameters of the notification with the corresponding values */
+ protected Map<String, Object> parameters = new HashMap<String, Object>();
+
+ private static final int YES = 1 << 6; // SWT.YES
+
+ private static final int NO = 1 << 7; // SWT.NO
+
+ /**
+ * Set a message for the notification
+ *
+ * @param message
+ * , the message to display
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setMessage(String message) {
+ parameters.put(MESSAGE, message);
+ return this;
+ }
+
+ /**
+ * Determines if the notification is asynchronous (don't force the user to read the notification immediately)
+ *
+ * @param asynchronous
+ * , true if it asynchronous
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setAsynchronous(boolean asynchronous) {
+ parameters.put(ASYNCHRONOUS, asynchronous);
+ return this;
+ }
+
+ /**
+ * Set a default action for the notification
+ *
+ * @param runnable
+ * , a runnable triggered when default action of the notification is selected
+ * The first action added is the default One
+ * @return this
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public NotificationBuilder addAction(NotificationRunnable runnable) {
+ Collection<NotificationRunnable> runnables = (Collection<NotificationRunnable>) parameters.get(ACTION);
+ if (runnables == null) {
+ runnables = new LinkedList<NotificationRunnable>();
+ parameters.put(ACTION, runnables);
+ }
+ runnables.add(runnable);
+ return this;
+ }
+
+ /**
+ * Set a delay if the notification is temporary
+ *
+ * @param delayMs
+ * , the delay in ms for visibility
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setDelay(long delayMs) {
+ parameters.put(DELAY, delayMs);
+ return this;
+ }
+
+ /**
+ * Set true if the notification is temporary
+ *
+ * @param temporary
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setTemporary(boolean temporary) {
+ parameters.put(TEMPORARY, temporary);
+ return this;
+ }
+
+ /**
+ * Set a title for the notification
+ *
+ * @param title
+ * , the title
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setTitle(String title) {
+ parameters.put(TITLE, title);
+ return this;
+ }
+
+ /**
+ * Set if the notification has to understand HTML
+ *
+ * @param useHTML
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setHTML(boolean useHTML) {
+ parameters.put(HTML, useHTML);
+ return this;
+ }
+
+ /**
+ * Set the type of the notification according to {@link Type}
+ *
+ * @param type
+ * , the desired type
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setType(Type type) {
+ parameters.put(TYPE, type);
+ return this;
+ }
+
+ /**
+ * Allows the developer to use a specific parameter
+ *
+ * @param name
+ * , the key of the parameter
+ * @param value
+ * , the value
+ * @return this
+ */
+ @Override
+ public NotificationBuilder setParameter(String name, Object value) {
+ parameters.put(name, value);
+ return this;
+ }
+
+ /**
+ * Creates a notification according to different parameters
+ */
+ @Override
+ public INotification run() {
+ INotification result;
+
+ INotificationBuilderFactory delegator = Activator.getDefault().getNotificationBuilderFactory();
+ if (delegator != null) {
+ // Create the delegate
+ INotificationBuilder delegate = delegator.createNotificationBuilder();
+
+ // Fill it up
+ parameters.forEach(delegate::setParameter);
+
+ // And run it
+ result = delegate.run();
+ } else {
+ // Just a simple log notification
+ result = new LogNotification(
+ (Type) parameters.get(TYPE),
+ (String) parameters.get(MESSAGE));
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a notification builder already configured to display an information builder
+ *
+ * @return a notification builder
+ */
+ public static NotificationBuilder createInformationBuilder() {
+ NotificationBuilder builder = new NotificationBuilder();
+ return builder;
+ }
+
+ /**
+ * Creates a notification builder already configured to display an asynchronous popup
+ *
+ * @param text
+ * , the text to display
+ * @return a notification builder
+ */
+ public static NotificationBuilder createAsyncPopup(String text) {
+ return new NotificationBuilder().setAsynchronous(true).setTemporary(true).setMessage(text).setDelay(2000);
+ }
+
+ /**
+ * Creates a notification builder already configured to display an asynchronous popup with a specified title
+ *
+ * @param text
+ * , the text to display
+ * @param title
+ * , the title of the popup
+ * @return a notification builder
+ */
+ public static NotificationBuilder createAsyncPopup(String title, String text) {
+ return new NotificationBuilder().setAsynchronous(true).setTemporary(true).setMessage(text).setTitle(title).setDelay(2000);
+ }
+
+ /**
+ * Creates a notification builder already configured to display an information popup
+ *
+ * @param text
+ * , the text to display
+ * @return a notification builder
+ */
+ public static NotificationBuilder createInfoPopup(String text) {
+ return new NotificationBuilder().setAsynchronous(false).setTemporary(false).setMessage(text).setType(Type.INFO);
+ }
+
+ /**
+ * Creates a notification builder already configured to display an warning popup
+ *
+ * @param text
+ * , the text to display
+ * @return a notification builder
+ */
+ public static NotificationBuilder createWarningPopup(String text) {
+ return new NotificationBuilder().setAsynchronous(false).setTemporary(false).setMessage(text).setType(Type.WARNING);
+ }
+
+ /**
+ * Creates a notification builder already configured to display a popup with question icon
+ *
+ * @param text
+ * , the text to display
+ * @return a notification builder
+ */
+ public static NotificationBuilder createQuestionPopup(String text) {
+ return new NotificationBuilder().setAsynchronous(false).setTemporary(false).setMessage(text).setType(Type.QUESTION);
+ }
+
+ /**
+ * Creates a notification builder already configured to display a popup with error icon
+ *
+ * @param text
+ * , the text to display
+ * @return a notification builder
+ */
+ public static NotificationBuilder createErrorPopup(String text) {
+ return new NotificationBuilder().setAsynchronous(false).setTemporary(false).setMessage(text).setType(Type.ERROR);
+ }
+
+ /**
+ * Creates a notification builder already configured to display a yes no question
+ *
+ * @param yes
+ * , the action to launch if yes is selected
+ * @param no
+ * , the action to launch if no is selected
+ * @return a notification builder
+ */
+ public static NotificationBuilder createYesNo(String message, final Runnable yes, final Runnable no) {
+ return new NotificationBuilder().setType(Type.QUESTION).setAsynchronous(false).setTemporary(false).setMessage(message).addAction(new NotificationRunnable() {
+
+ @Override
+ public void run(IContext context) {
+ if (yes != null) {
+ context.put(IContext.ACTION_ID, YES);
+ yes.run();
+ }
+ }
+
+ @Override
+ public String getLabel() {
+ return "Yes";
+ }
+ }).addAction(new NotificationRunnable() {
+
+ @Override
+ public void run(IContext context) {
+ if (no != null) {
+ context.put(IContext.ACTION_ID, NO);
+ no.run();
+ }
+ }
+
+ @Override
+ public String getLabel() {
+ return "No";
+ }
+ });
+ }
+
+ /**
+ * Creates a notification builder already configured to display a yes no question, no runnables are necesary as the user just want the
+ * PopupNotification result
+ * This NotificationRunnable is not intended to be changed to an asynchronous notification for example
+ * When the run method is called use getRsult method in {@link PopupNotification} and test if the value is SWT.YES or SWT.NO
+ *
+ * @param message
+ * , the message to display
+ *
+ * @return a notification builder
+ */
+ public static NotificationBuilder createYesNo(String message) {
+ return new NotificationBuilder().setType(Type.QUESTION).setAsynchronous(false).setTemporary(false).setMessage(message).addAction(new NotificationRunnable() {
+
+ @Override
+ public void run(IContext context) {
+ context.put(IContext.ACTION_ID, YES);
+ }
+
+ @Override
+ public String getLabel() {
+ return "Yes";
+ }
+ }).addAction(new NotificationRunnable() {
+
+ @Override
+ public void run(IContext context) {
+ context.put(IContext.ACTION_ID, NO);
+ }
+
+ @Override
+ public String getLabel() {
+ return "No";
+ }
+ });
+ }
+
+ /**
+ * Creates a notification builder already configured to display a yes no question
+ *
+ * @param yes
+ * , the action to launch if yes is selected
+ * @param no
+ * , the action to launch if no is selected
+ * @return a notification builder
+ */
+ public static NotificationBuilder createYesNo(String message, final NotificationRunnable yes, final NotificationRunnable no) {
+ return new NotificationBuilder().setType(Type.QUESTION).setAsynchronous(false).setTemporary(false).setMessage(message).addAction(new NotificationRunnable() {
+
+ @Override
+ public void run(IContext context) {
+ if (yes != null) {
+ context.put(IContext.ACTION_ID, YES);
+ yes.run(context);
+ }
+ }
+
+ @Override
+ public String getLabel() {
+ return "Yes";
+ }
+ }).addAction(new NotificationRunnable() {
+
+ @Override
+ public void run(IContext context) {
+ if (no != null) {
+ context.put(IContext.ACTION_ID, NO);
+ no.run(context);
+ }
+ }
+
+ @Override
+ public String getLabel() {
+ return "No";
+ }
+ });
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationRunnable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationRunnable.java
new file mode 100644
index 00000000000..3df50c005ab
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/NotificationRunnable.java
@@ -0,0 +1,36 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 ATOS ORIGIN, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.notify;
+
+/**
+ * a runnable
+ *
+ */
+public interface NotificationRunnable {
+
+ /**
+ * Run the runnable
+ *
+ * @param context
+ * , used to fill properties, can contain data
+ */
+ void run(IContext context);
+
+ /**
+ * The label of the runnable
+ *
+ * @return the label
+ */
+ String getLabel();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/Type.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/Type.java
new file mode 100644
index 00000000000..61c73981778
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/notify/Type.java
@@ -0,0 +1,29 @@
+/*****************************************************************************
+ * Copyright (c) 2010, 2016 ATOS ORIGIN, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Tristan Faure (ATOS ORIGIN INTEGRATION) tristan.faure@atosorigin.com - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.notify;
+
+
+/**
+ * The different types of Notifications
+ * <li>INFO</i>
+ * <li>WARNING</i>
+ * <li>ERROR</i>
+ * <li>QUESTION</i>
+ *
+ * @author tristan faure
+ *
+ */
+public enum Type {
+ INFO, WARNING, ERROR, QUESTION
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/IExecutorServiceFactory.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/IExecutorServiceFactory.java
new file mode 100644
index 00000000000..cc2ff542665
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/IExecutorServiceFactory.java
@@ -0,0 +1,25 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.spi;
+
+import org.eclipse.papyrus.infra.tools.util.IExecutorService;
+
+/**
+ * An OSGi service protocol for creation of an executor service on the UI thread.
+ */
+@FunctionalInterface
+public interface IExecutorServiceFactory {
+ /** Creates an executor service that posts runnables on the SWT UI thread. */
+ IExecutorService createExecutor();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/INotificationBuilderFactory.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/INotificationBuilderFactory.java
new file mode 100644
index 00000000000..a527ec3c592
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/spi/INotificationBuilderFactory.java
@@ -0,0 +1,26 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.spi;
+
+import org.eclipse.papyrus.infra.tools.notify.INotificationBuilder;
+
+/**
+ * An OSGi service protocol for creation of a notification builder
+ * (preferably for UI presentation).
+ */
+@FunctionalInterface
+public interface INotificationBuilderFactory {
+ /** Creates notification builder that presents notifications in the UI. */
+ INotificationBuilder createNotificationBuilder();
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/BooleanHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/BooleanHelper.java
new file mode 100644
index 00000000000..296c4751c96
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/BooleanHelper.java
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+/**
+ *
+ * This class provides an useful methods for boolean
+ *
+ */
+public class BooleanHelper {
+
+ private BooleanHelper() {
+ // to prevent instanciation
+ }
+
+ /**
+ *
+ * @param str
+ * a string
+ * @return
+ * <code>true</code> if the string represents a boolean value
+ */
+ public static final boolean isBoolean(final String str) {
+ return "true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ClassLoaderHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ClassLoaderHelper.java
new file mode 100644
index 00000000000..46751c90c81
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ClassLoaderHelper.java
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * Copyright (c) 2010 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.papyrus.infra.tools.Activator;
+
+/**
+ * A Helper class for Class Loading.
+ *
+ * @author Camille Letavernier
+ */
+// This class needs the "BuddyPolicy" set to "dependent" in the Manifest.MF,
+// in order to be able to retrieve the classes it loads
+//
+// This is the org.eclipse.papyrus.infra.tools class loader which is used for loading
+// a class, instead of each caller's ClassLoader
+//
+// Plug-ins using this class should also either set their Buddy-policy to dependent or
+// reexport the dependency to oep.infra.tools
+public class ClassLoaderHelper {
+
+ /**
+ * Usually, there are few classes with many different accesses. Using a cache, we can improve
+ * the performances between 10 and 20 times, with really few memory consumption
+ */
+ private static final Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
+
+ /**
+ * Loads the class matching the given className. Exceptions are caught and sent
+ * to the Logger.
+ *
+ * @param className
+ * The qualified name of the Class to load.
+ * @return
+ * The loaded Class, or null if an error occured
+ */
+ public static Class<?> loadClass(String className) {
+ try {
+ Class<?> result = classes.get(className);
+ if (result == null) {
+ result = Activator.getDefault().getBundle().loadClass(className);
+ classes.put(className, result);
+ }
+ return result;
+ } catch (ClassNotFoundException ex) {
+ Activator.log.error(String.format("The class %s doesn't exist", className), ex); //$NON-NLS-1$
+ } catch (NullPointerException ex) {
+ Activator.log.error("Cannot load class " + className, ex); //$NON-NLS-1$
+ }
+
+ return null;
+ }
+
+ /**
+ * Loads and returns the class denoted by the given className.
+ * Checks that the loaded class is a subtype of the given Class.
+ *
+ * @param className
+ * The qualified name of the class to be loaded
+ * @param asSubClass
+ * The interface or class that the loaded class must implement or extend
+ * @return
+ * The loaded class, or null if the class doesn't exist or is invalid.
+ * In such a case, the exception is logged.
+ */
+ public static <T> Class<? extends T> loadClass(String className, Class<T> asSubClass) {
+ Class<?> theClass = loadClass(className);
+ if (theClass == null) {
+ return null;
+ }
+
+ try {
+ Class<? extends T> typedClass = theClass.asSubclass(asSubClass);
+ return typedClass;
+ } catch (ClassCastException ex) {
+ Activator.log.error(String.format("The class %1$s doesn't extend or implement %2$s", className, asSubClass.getName()), ex); //$NON-NLS-1$
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new instance of class denoted by the given className.
+ * Checks that the instantiated class is a subtype of the given class
+ *
+ * @param className
+ * The qualified name of the class to be instantiated
+ * @param asSubclass
+ * The interface or class that the loaded class must implement or extend
+ * @return
+ * An instance of the loaded class, or null if a valid instance
+ * cannot be created. In such a case, the exception is logged.
+ */
+ public static <T> T newInstance(String className, Class<T> asSubclass) {
+ Class<? extends T> typedClass = loadClass(className, asSubclass);
+ if (typedClass == null) {
+ return null;
+ }
+
+ return newInstance(typedClass);
+ }
+
+ /**
+ * Returns a new Instance of the given class
+ *
+ * @param className
+ * The qualified name of the Class to instantiate
+ * @return
+ * A new instance of the given class, or null if the class couldn't be
+ * instantiated
+ */
+ public static Object newInstance(String className) {
+ return newInstance(loadClass(className));
+ }
+
+ /**
+ * Returns a new Instance of the given class
+ *
+ * @param theClass
+ * The Class to instantiate
+ * @return
+ * A new instance of the given class, or null if the class couldn't be
+ * instantiated
+ */
+ public static <T extends Object> T newInstance(Class<T> theClass) {
+ if (theClass == null) {
+ return null;
+ }
+
+ try {
+ return theClass.newInstance();
+ } catch (IllegalAccessException ex) {
+ Activator.log.error("Cannot find a valid public constructor for the class " + theClass.getName(), ex); //$NON-NLS-1$
+ } catch (InstantiationException ex) {
+ Activator.log.error(String.format("The class %s cannot be instantiated.", theClass.getName()), ex); //$NON-NLS-1$
+ }
+
+ return null;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CompositeServiceTracker.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CompositeServiceTracker.java
new file mode 100644
index 00000000000..2dab30d9eba
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CompositeServiceTracker.java
@@ -0,0 +1,92 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.lang.reflect.Array;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BinaryOperator;
+import java.util.stream.Stream;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * A service tracker that provides a single service as a composite of
+ * registered service implementations.
+ */
+public class CompositeServiceTracker<S> extends ServiceTracker<S, S> {
+ private final AtomicReference<S> delegate = new AtomicReference<>();
+
+ private final Class<S> serviceType;
+ private final S identity;
+ private final BinaryOperator<S> composer;
+
+ /**
+ * Initializes me with the bundle context in which I track resolver services,
+ * an identity service that generally performs trivially (e.g., no-ops or default behaviour),
+ * and an operator that composes two service instances.
+ *
+ * @param context
+ * the bundle context
+ * @param serviceType
+ * the service protocol type
+ * @param identity
+ * the basic no-op or default service instance
+ * @param composer
+ * an operator that composes two services instances into one
+ */
+ public CompositeServiceTracker(BundleContext context, Class<S> serviceType, S identity, BinaryOperator<S> composer) {
+ super(context, serviceType, null);
+
+ this.serviceType = serviceType;
+ this.identity = identity;
+ this.composer = composer;
+ }
+
+ @Override
+ public final S getService() {
+ S result = this.delegate.get();
+ if (result == null) {
+ // Recompute
+ @SuppressWarnings("unchecked")
+ S[] services = (S[]) Array.newInstance(serviceType, getTrackingCount());
+ result = Stream.of(getServices(services))
+ .filter(Objects::nonNull) // If the array has more slots than we have services
+ .reduce(identity, composer);
+ this.delegate.set(result);
+ }
+
+ return result;
+ }
+
+ @Override
+ public S addingService(ServiceReference<S> reference) {
+ S result = super.addingService(reference);
+
+ // We will have to recompute our delegates
+ delegate.set(null);
+
+ return result;
+ }
+
+ @Override
+ public void removedService(ServiceReference<S> reference, S service) {
+ super.removedService(reference, service);
+
+ // We will have to recompute our delegates
+ delegate.set(null);
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CoreExecutors.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CoreExecutors.java
new file mode 100644
index 00000000000..14604007bc2
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/CoreExecutors.java
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.papyrus.infra.tools.Activator;
+
+/**
+ * A provider of {@link Executor}s offering various synchronous and asynchronous
+ * execution characteristics.
+ */
+public class CoreExecutors {
+
+ // Not instantiable by clients
+ private CoreExecutors() {
+ super();
+ }
+
+ /**
+ * Obtains a service that posts tasks for asynchronous execution on the
+ * SWT display thread, if there is one. If there is no display, then
+ * a default background-thread executor is supplied by the Java platform.
+ *
+ * @return an executor service on the UI thread (if there is a UI). Never
+ * {@code null} and always the same instance. Clients may not shut down
+ * this executor; attempting to do so will result in {@link IllegalStateException}s
+ */
+ public static IExecutorService getUIExecutorService() {
+ return Activator.getDefault().getUIExecutorService();
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/FileUtils.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/FileUtils.java
new file mode 100644
index 00000000000..ca804c1bc0f
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/FileUtils.java
@@ -0,0 +1,129 @@
+/*****************************************************************************
+ * 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.papyrus.infra.tools.Activator;
+
+/**
+ * @author VL222926
+ *
+ */
+public class FileUtils {
+
+ public static final String PLATFORM_STRING = "platform"; //$NON-NLS-1$
+
+ public static final String PLUGIN_STRING = "plugin"; //$NON-NLS-1$
+
+ public static final String SLASH_STRING = "/"; //$NON-NLS-1$
+
+ public static final String COLON_STRING = ":"; //$NON-NLS-1$
+
+ public static final String DOT_STRING = ".";//$NON-NLS-1$
+
+ public static final String TEXT_EXTENSION = "txt";//$NON-NLS-1$
+
+ public static final String CSV_EXTENSIOn = "csv";//$NON-NLS-1$
+
+ public static final String UNDERSCORE = "_";//$NON-NLS-1$
+
+ public static final String LINE_SEPARATOR = "line.separator";//$NON-NLS-1$
+
+ private FileUtils() {
+ // to prevent instanciation
+ }
+
+ /**
+ * return the system property line seperator
+ */
+ public static final String getSystemPropertyLineSeparator(){
+ return System.getProperty(LINE_SEPARATOR);
+ }
+
+ /**
+ * this method read a file and return a string, the line separator used will we System.getProperty("line.separator")
+ *
+ * @param pluginName
+ * the name of the plugin owning the file
+ * @param filePath
+ * the path of the file
+ * @param fileNameWithExtension
+ * the name fo the file with its extension
+ * @return
+ */
+ public static final String getStringFromPlatformFile(final String pluginName, final String filePath, final String fileNameWithExtension) {
+ return getStringFromPlatformFile(pluginName, filePath, fileNameWithExtension, System.getProperty("line.separator")); //$NON-NLS-1$
+ }
+
+ /**
+ *
+ * @param pluginName
+ * the name of the plugin owning the file
+ * @param filePath
+ * the path of the file
+ * @param fileNameWithExtension
+ * the name fo the file with its extension
+ * @param lineSeparator
+ * the line separator to use
+ * @return
+ */
+ public static final String getStringFromPlatformFile(final String pluginName, final String filePath, final String fileNameWithExtension, final String lineSeparator) {
+ Assert.isNotNull(pluginName);
+ Assert.isNotNull(filePath);
+ Assert.isNotNull(fileNameWithExtension);
+ StringBuilder pathBuilder = new StringBuilder();
+ pathBuilder.append(PLATFORM_STRING);
+ pathBuilder.append(COLON_STRING);
+ pathBuilder.append(SLASH_STRING);
+ pathBuilder.append(PLUGIN_STRING);
+ pathBuilder.append(SLASH_STRING);
+ pathBuilder.append(pluginName);
+ if (!filePath.startsWith(SLASH_STRING)) {
+ pathBuilder.append(SLASH_STRING);
+ }
+ pathBuilder.append(filePath);
+ if (!filePath.endsWith(SLASH_STRING)) {
+ pathBuilder.append(SLASH_STRING);
+ }
+ pathBuilder.append(fileNameWithExtension);
+ StringBuilder builder = new StringBuilder();
+ URL url;
+ try {
+ url = new URL(pathBuilder.toString());
+ InputStream inputStream = url.openConnection().getInputStream();
+ BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
+ String inputLine = in.readLine();
+
+ while (inputLine != null) {
+ builder.append(inputLine);
+ inputLine = in.readLine();
+ if (inputLine != null) {
+ builder.append(lineSeparator); // $NON-NLS-1$
+ }
+ }
+
+ in.close();
+
+ } catch (IOException e) {
+ Activator.log.error(e);
+ }
+ return builder.toString();
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IExecutorService.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IExecutorService.java
new file mode 100644
index 00000000000..e816ade859c
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IExecutorService.java
@@ -0,0 +1,107 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+/**
+ * A specialized {@link ExecutorService} that also provides for synchronous
+ * execution of tasks, with the possibility that these could be optimized
+ * where appropriate (such as in a UI-thread executor to use a {@code syncExec}
+ * call).
+ */
+public interface IExecutorService extends ExecutorService {
+ /**
+ * Synchronously executes a {code task}. The task will run on the
+ * appropriate executor thread, as usual, but control will return
+ * to the caller only when its execution is complete.
+ *
+ * @param task
+ * the task to execute
+ *
+ * @throws InterruptedException
+ * on interruption, as per {@link Future#get()}
+ * @throws ExecutionException
+ * on failed execution, as per {@link Future#get()}
+ */
+ void syncExec(Runnable task) throws InterruptedException, ExecutionException;
+
+ /**
+ * Synchronously invokes a {code callable}. The callable will run on the
+ * appropriate executor thread, as usual, but control will return
+ * to the caller only when its execution is complete.
+ *
+ * @param callable
+ * the task to execute
+ *
+ * @throws InterruptedException
+ * on interruption, as per {@link Future#get()}
+ * @throws ExecutionException
+ * on failed execution, as per {@link Future#get()}
+ */
+ <V> V syncCall(Callable<V> callable) throws InterruptedException, ExecutionException;
+
+ /**
+ * Submits a {@code task} with support for progress reporting.
+ *
+ * @param task
+ * the progress-metered task to execute
+ *
+ * @return a future of undefined type that can be used, for example, to wait for the {@code task} to complete
+ */
+ Future<?> submit(IProgressRunnable task);
+
+ /**
+ * Submits a {@code callable} with support for progress reporting.
+ *
+ * @param task
+ * the progress-metered task to execute
+ *
+ * @return the future result of the {@code callable}
+ */
+ <V> Future<V> submit(IProgressCallable<V> callable);
+
+ /**
+ * Synchronously executes a progress-monitored {code task}. The task will run on the
+ * appropriate executor thread, as usual, but control will return
+ * to the caller only when its execution is complete.
+ *
+ * @param task
+ * the task to execute
+ *
+ * @throws InterruptedException
+ * on interruption, as per {@link Future#get()}
+ * @throws ExecutionException
+ * on failed execution, as per {@link Future#get()}
+ */
+ void syncExec(IProgressRunnable task) throws InterruptedException, ExecutionException;
+
+ /**
+ * Synchronously invokes a progress-monitored {code callable}. The callable will run on the
+ * appropriate executor thread, as usual, but control will return
+ * to the caller only when its execution is complete.
+ *
+ * @param callable
+ * the task to execute
+ *
+ * @throws InterruptedException
+ * on interruption, as per {@link Future#get()}
+ * @throws ExecutionException
+ * on failed execution, as per {@link Future#get()}
+ */
+ <V> V syncCall(IProgressCallable<V> callable) throws InterruptedException, ExecutionException;
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressCallable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressCallable.java
new file mode 100644
index 00000000000..ff2601ca8ad
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressCallable.java
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.concurrent.Callable;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * An analogue of the Eclipse JFace {@code IRunnableWithProgress} interface,
+ * a protocol for executable computations that can report measurable progress.
+ * Implementations of the {@link IExecutorService} can supply suitable progress
+ * reporting to these callables.
+ *
+ * @see IExecutorService
+ */
+@FunctionalInterface
+public interface IProgressCallable<V> {
+ /**
+ * Computes a result.
+ *
+ * @param monitor
+ * for reporting of progress of the task
+ *
+ * @return the result of the computation
+ *
+ * @throws Exception
+ * if the computation fails unrecoverably
+ */
+ V call(IProgressMonitor monitor) throws Exception;
+
+ /**
+ * Adapts a plain Java {@code runnable} task to a progress-runnable task.
+ *
+ * @param label
+ * an user-presentable label for the task
+ * @param runnable
+ * a plain runnable
+ *
+ * @return a progress runnable decorating the plain {@code runnable}
+ */
+ static <V> IProgressCallable<V> convert(String label, Callable<V> callable) {
+ return progress -> {
+ if (progress != null) {
+ progress.beginTask(label, IProgressMonitor.UNKNOWN);
+ }
+
+ try {
+ return callable.call();
+ } finally {
+ if (progress != null) {
+ progress.done();
+ }
+ }
+ };
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressRunnable.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressRunnable.java
new file mode 100644
index 00000000000..124099a64fc
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IProgressRunnable.java
@@ -0,0 +1,61 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * An analogue of the Eclipse JFace {@code IRunnableWithProgress} interface,
+ * a protocol for executable tasks that can report measurable progress.
+ * Implementations of the {@link IExecutorService} can supply suitable progress
+ * reporting to these runnables.
+ *
+ * @see IExecutorService
+ */
+@FunctionalInterface
+public interface IProgressRunnable {
+ /**
+ * Executes the task.
+ *
+ * @param monitor
+ * for reporting of progress of the task
+ */
+ void run(IProgressMonitor monitor);
+
+ /**
+ * Adapts a plain Java {@code runnable} task to a progress-runnable task.
+ *
+ * @param label
+ * an user-presentable label for the task
+ * @param runnable
+ * a plain runnable
+ *
+ * @return a progress runnable decorating the plain {@code runnable}
+ */
+ static IProgressRunnable convert(String label, Runnable runnable) {
+ return progress -> {
+ if (progress != null) {
+ progress.beginTask(label, IProgressMonitor.UNKNOWN);
+ }
+
+ try {
+ runnable.run();
+ } finally {
+ if (progress != null) {
+ progress.done();
+ }
+ }
+ };
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IntegerAndSpreadsheetNumberConverter.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IntegerAndSpreadsheetNumberConverter.java
new file mode 100644
index 00000000000..3c4f5f265c6
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/IntegerAndSpreadsheetNumberConverter.java
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+/**
+ *
+ * This class allows to converter an int into a String like a spreadsheet numerotation and vice-versa. Fox example :
+ * <ul>
+ * <li>1 <-> A</li>
+ * <li>26 <-> Z</li>
+ * <li>27 <-> AA</li>
+ * <li>28<-> AB</li>
+ * </ul>
+ *
+ *
+ *
+ *
+ */
+// adapted code from http://www.developpez.net/forums/d1197058/dotnet/general-dotnet/contribuez/extensions-types-int-string-conversion-format-colonne-excel/
+public class IntegerAndSpreadsheetNumberConverter {
+
+ /**
+ *
+ * Constructor.
+ *
+ */
+ private IntegerAndSpreadsheetNumberConverter() {
+ // to prevent instanciation
+ }
+
+ /**
+ *
+ * @param number
+ * an integer
+ * @return
+ * the string representing this integer in a spreedsheet
+ */
+ public static String toString(int number) {
+ if (number <= 0) {
+ throw new NumberFormatException();
+ }
+ int tmp = number;
+ String string = ""; //$NON-NLS-1$
+ while (tmp > 0) {
+ final int r = (tmp - 1) % 26;
+ string = (char) ('A' + r) + string;
+ tmp = (tmp - r) / 26;
+ }
+
+ return string;
+ }
+
+ /**
+ *
+ * @unused
+ * @param string
+ * a string
+ * @return
+ * the number corresponding to the string
+ */
+ public static int toInt(String string) {
+ if (string == null || string.length() == 0) {
+ throw new NumberFormatException();
+ }
+ string = string.toUpperCase();
+ int multiplier = 1;
+ int columnNumber = 0;
+ for (int i = string.length() - 1; i >= 0; i--) {
+ final char c = string.charAt(i);
+ if (c < 'A' || c > 'Z') {
+ throw new NumberFormatException();
+ }
+ final int value = (c - 'A' + 1) * multiplier;
+ columnNumber += value;
+ multiplier *= 26;
+ }
+ return columnNumber;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterables2.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterables2.java
new file mode 100644
index 00000000000..826a0fbf094
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterables2.java
@@ -0,0 +1,66 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.ListIterator;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+/**
+ * Utilities for working with iterables that are not provided by {@linkplain Iterables Guava}.
+ */
+public class Iterables2 {
+ /**
+ * Not instantiable by clients.
+ */
+ private Iterables2() {
+ super();
+ }
+
+ /**
+ * Brute-force topological sort of objects by a partial ordering relation.
+ *
+ * @param items
+ * the items to be sorted
+ * @param partOrder
+ * a partial ordering relation on the items
+ * @return the topologically sorted {@code items} as a new mutable list
+ */
+ public static <T> List<T> topoSort(Iterable<T> items, Comparator<? super T> partOrder) {
+ List<T> unsorted = Lists.newLinkedList(items);
+ List<T> result = Lists.newArrayListWithCapacity(unsorted.size());
+
+ while (!unsorted.isEmpty()) {
+ T min = unsorted.remove(0);
+
+ for (ListIterator<T> iter = unsorted.listIterator(); iter.hasNext();) {
+ T next = iter.next();
+ if (partOrder.compare(next, min) < 0) {
+ // Found a new minimum. Put the old one back for next pass
+ iter.set(min);
+ min = next;
+ }
+ }
+
+ // Whatever's the minimum now is the next in our partial ordering
+ result.add(min);
+ }
+
+ return result;
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterators2.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterators2.java
new file mode 100644
index 00000000000..bd10ef4e9d7
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Iterators2.java
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.Iterator;
+
+import org.eclipse.emf.common.util.TreeIterator;
+
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.Iterators;
+
+/**
+ * Utilities for working with iterators that are not provided by {@linkplain Iterators Guava}.
+ */
+public class Iterators2 {
+ /**
+ * Not instantiable by clients.
+ */
+ private Iterators2() {
+ super();
+ }
+
+ /**
+ * Filters an EMF tree iterator for elements of a particular {@code type}.
+ *
+ * @param treeIterator
+ * the tree iterator to filter
+ * @param type
+ * the type of elements to include in the filtered tree iterator
+ * @return the filtered tree iterator
+ */
+ public static <T> TreeIterator<T> filter(final TreeIterator<?> treeIterator, final Class<T> type) {
+ class FilteredTreeIterator extends AbstractIterator<T> implements TreeIterator<T> {
+ final Iterator<T> delegate = Iterators.filter(treeIterator, type);
+
+ @Override
+ protected T computeNext() {
+ return delegate.hasNext() ? delegate.next() : endOfData();
+ }
+
+ public void prune() {
+ treeIterator.prune();
+ }
+ }
+
+ return new FilteredTreeIterator();
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ListHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ListHelper.java
new file mode 100644
index 00000000000..382c964daa5
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ListHelper.java
@@ -0,0 +1,81 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListHelper {
+
+ /**
+ * Converts an array to a List
+ *
+ * This method is similar to Arrays.asList, except that it returns
+ * a writeable list
+ *
+ * @param array
+ * The array to transform into a List
+ * @return
+ * A List containing the same elements as the array
+ */
+ public static <T> List<T> asList(T[] array) {
+ if (array == null) {
+ return new ArrayList<T>();
+ }
+
+ List<T> result = new ArrayList<T>(array.length);
+ for (T t : array) {
+ result.add(t);
+ }
+ return result;
+ }
+
+ /**
+ * Invokes the toString() method recursively on this list's elements.
+ * The values are separated by ", "
+ *
+ * @param list
+ * The list whose string representation to return
+ * @return
+ *
+ * @see #deepToString(List, String)
+ */
+ public static String deepToString(List<?> list) {
+ return deepToString(list, ", ");
+ }
+
+ /**
+ * Invokes the toString() method recursively on this list's elements.
+ * The values are separated by the given separator
+ *
+ * @param list
+ * The list whose string representation to return
+ * @param separator
+ * The string to insert between each element's string representation
+ * @return
+ *
+ * @see #deepToString(List)
+ */
+ public static String deepToString(List<?> list, String separator) {
+ boolean firstElement = true;
+ String result = "";
+ for (Object item : list) {
+ if (firstElement) {
+ firstElement = false;
+ } else {
+ result += separator;
+ }
+ result += item == null ? null : item.toString();
+ }
+ return result;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/PlatformHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/PlatformHelper.java
new file mode 100644
index 00000000000..316b4683f9a
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/PlatformHelper.java
@@ -0,0 +1,141 @@
+/*****************************************************************************
+ * Copyright (c) 2013, 2015 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bug 479999
+ * Christian W. Damus - bug 469188
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.find;
+
+import java.util.function.Supplier;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.emf.common.notify.Notifier;
+
+import com.google.common.base.Predicates;
+
+
+public class PlatformHelper {
+
+ /**
+ * Attempt to get an adapter of the specified target {@code type} from an {@code object}
+ * by any means available.
+ *
+ * @param object
+ * an object to adapt
+ * @param type
+ * the type of adapter to get
+ *
+ * @return the best-effort adapter of the given {@code type} or {@code null} if no
+ * adapter is available
+ */
+ public static <T> T getAdapter(Object object, Class<T> type) {
+ T result = null;
+
+ // Don't provide adapters for null
+ if (object != null) {
+ if (type.isInstance(object)) {
+ result = type.cast(object);
+ } else if (object instanceof IAdaptable) {
+ result = getIntrinsicAdapter((IAdaptable) object, type);
+ }
+
+ if (result == null) {
+ result = getExtrinsicAdapter(object, type);
+
+ if ((result == null) && (object instanceof Notifier)) {
+ result = getEMFAdapter((Notifier) object, type);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static <T> T getIntrinsicAdapter(IAdaptable adaptable, Class<T> type) {
+ T result = null;
+
+ Object attempt = adaptable.getAdapter(type);
+ if (type.isInstance(attempt)) {
+ result = type.cast(attempt);
+ }
+
+ return result;
+ }
+
+ private static <T> T getExtrinsicAdapter(Object object, Class<T> type) {
+ T result = null;
+
+ Object attempt = Platform.getAdapterManager().getAdapter(object, type);
+ if (type.isInstance(attempt)) {
+ result = type.cast(attempt);
+ }
+
+ return result;
+ }
+
+ private static <T> T getEMFAdapter(Notifier notifier, Class<T> type) {
+ return find(filter(notifier.eAdapters(), type), Predicates.alwaysTrue(), null);
+ }
+
+ /**
+ * Get an adapter of the specified target {@code type} from an {@code object} by any means available.
+ *
+ * @param object
+ * an object to adapt. May be {@code null}, in which case the {@code defaultAdapter} is returned
+ * @param type
+ * the type of adapter to get
+ * @param defaultAdapter
+ * a default adapter to return if none can be obtained (may be {@code null}
+ *
+ * @return the best-effort adapter of the given {@code type}, else the {@code defaultAdapter}
+ */
+ public static <T> T getAdapter(Object object, Class<T> type, T defaultAdapter) {
+ T result = defaultAdapter;
+
+ if (object != null) {
+ T adapter = getAdapter(object, type);
+ if (adapter != null) {
+ result = adapter;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Get an adapter of the specified target {@code type} from an {@code object} by any means available.
+ *
+ * @param object
+ * an object to adapt. May be {@code null}, in which case the {@code defaultAdapter} is returned
+ * @param type
+ * the type of adapter to get
+ * @param defaultSupplier
+ * a supplier to consult for a default adapter in the case that none can be
+ * obtained by the usual means (may be {@code null}
+ *
+ * @return the best-effort adapter of the given {@code type}, else the {@code defaultAdapter}
+ */
+ public static <T> T getAdapter(Object object, Class<T> type, Supplier<T> defaultAdapter) {
+ T result = null;
+
+ if (object != null) {
+ T adapter = getAdapter(object, type);
+ if (adapter != null) {
+ result = adapter;
+ }
+ }
+
+ return (result != null) ? result : defaultAdapter.get();
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReferenceCounted.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReferenceCounted.java
new file mode 100644
index 00000000000..6c0444c3086
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReferenceCounted.java
@@ -0,0 +1,175 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A convenient reference-counting utility with automatic disposal and asynchronous auto-disposal (intended
+ * for use with the UI event-loop executor service). Sub-classes leveraging the self-disposing behaviour of
+ * the self-owning instance must override the {@link #dispose()} method.
+ */
+public class ReferenceCounted<T> {
+ private final T owner;
+ private final AtomicInteger refCount = new AtomicInteger();
+ private Runnable disposeAction;
+ private final ExecutorService autoReleaseExecutor;
+
+ /**
+ * Initializes me with my owner for which I count references.
+ * <p>
+ * I do not support {@link #autoRelease()}.
+ *
+ * @param owner
+ * my owner
+ * @param disposeAction
+ * action to run when the reference count reaches zero to dispose my {@code owner}
+ *
+ * @throws IllegalArgumentException
+ * if the {@code disposeAction} is {@code null} because, really,
+ * why would you need reference counting for an object that doesn't need to be disposed?
+ */
+ public ReferenceCounted(T owner, Runnable disposeAction) {
+ this(owner, null, disposeAction);
+ }
+
+ /**
+ * Initializes me with my owner for which I count references.
+ * <p>
+ * I support {@link #autoRelease()} using the given executor.
+ *
+ * @param owner
+ * my owner
+ * @param autoReleaseExecutor
+ * the executor on which to schedule auto-release invocations
+ * @param disposeAction
+ * action to run when the reference count reaches zero to dispose my {@code owner}
+ *
+ * @throws IllegalArgumentException
+ * if the {@code disposeAction} is {@code null} because, really,
+ * why would you need reference counting for an object that doesn't need to be disposed?
+ */
+ public ReferenceCounted(T owner, ExecutorService autoReleaseExecutor, Runnable disposeAction) {
+ super();
+
+ if (disposeAction == null) {
+ throw new IllegalArgumentException("null disposeAction"); //$NON-NLS-1$
+ }
+
+ this.owner = owner;
+ this.autoReleaseExecutor = autoReleaseExecutor;
+ this.disposeAction = disposeAction;
+ }
+
+ /**
+ * Initializes me as a self-disposing reference-counted instance. This constructor is only suitable
+ * for chaining from subclasses.
+ * <p>
+ * I do not support {@link #autoRelease()}.
+ */
+ protected ReferenceCounted() {
+ this((ExecutorService) null);
+ }
+
+ /**
+ * Initializes me as a self-disposing reference-counted instance. This constructor is only suitable
+ * for chaining from subclasses.
+ * <p>
+ * I support {@link #autoRelease()} using the given executor.
+ *
+ * @param autoReleaseExecutor
+ * the executor on which to schedule auto-release invocations
+ */
+ @SuppressWarnings("unchecked")
+ protected ReferenceCounted(ExecutorService autoReleaseExecutor) {
+ super();
+
+ this.owner = (T) this;
+ this.disposeAction = new SelfDisposeAction();
+ this.autoReleaseExecutor = autoReleaseExecutor;
+ }
+
+ /**
+ * Retains me, incrementing my retain count. The caller must eventually {@link #release()} me (even if via the {@link #autoRelease()} method)
+ * if I am to become disposable.
+ *
+ * @return my owner, for convenience of call chaining
+ */
+ public final T retain() {
+ refCount.incrementAndGet();
+ return owner;
+ }
+
+ /**
+ * Releases me, decrementing my retain count. When my retain count reaches zero, I dispose myself using the
+ * configured dispose-action runnable.
+ */
+ public final void release() {
+ if (refCount.decrementAndGet() <= 0) {
+ if (disposeAction != null) {
+ try {
+ disposeAction.run();
+ } finally {
+ disposeAction = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Automatically releases me some time in the future, as determined by the auto-release executor with which
+ * I was configured. A particularly convenient executor is one that posts an asynchronous execution on the
+ * display thread, to automatically dispose my owner (or me, as the case may be) when the display thread
+ * returns to the event loop.
+ *
+ * @return my owner, for convenience of call chaining
+ *
+ * @throws IllegalStateException
+ * if I have no auto-release executor
+ */
+ public final T autoRelease() {
+ if (autoReleaseExecutor == null) {
+ throw new IllegalStateException("no auto-release executor available"); //$NON-NLS-1$
+ }
+
+ // Don't submit the dispose action because we could still be retained!
+ autoReleaseExecutor.execute(new Runnable() {
+
+ public void run() {
+ release();
+ }
+ });
+ return owner;
+ }
+
+ /**
+ * For classes that extend the {@code ReferenceCount}, this must be overridden to implement disposal.
+ * The default implementation throws {@link UnsupportedOperationException} to ensure that subclasses
+ * override it.
+ */
+ protected void dispose() {
+ throw new UnsupportedOperationException("dispose is unimplemented"); //$NON-NLS-1$
+ }
+
+ //
+ // Nested types
+ //
+
+ private class SelfDisposeAction implements Runnable {
+ public void run() {
+ dispose();
+ }
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReflectHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReflectHelper.java
new file mode 100644
index 00000000000..da91097bbc2
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/ReflectHelper.java
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.lang.reflect.Method;
+
+/**
+ *
+ * This helper provides methods to get methods reflectively
+ * It is not the better way to access to method, but sometimes it can be interested to avoid to duplicate
+ * lot of code
+ *
+ */
+public class ReflectHelper {
+
+ /**
+ *
+ * Should not be instantiated
+ *
+ */
+ private ReflectHelper() {
+ // prevents instantiation
+ }
+
+ /**
+ * Warning : each call of this method should be tested with a JUnit test, in order to know
+ * when the API has changed
+ *
+ * @param aClass
+ * a class
+ * @param methodName
+ * the name of the method to find
+ * @param parameterTypes
+ * an array owning the type of the parameters of the called method
+ * @return
+ * the wanted method
+ * @throws NoSuchMethodException
+ * @throws SecurityException
+ */
+ public static Method getMethod(final Class<?> aClass, final String methodName, Class<?>[] parameterTypes) throws SecurityException, NoSuchMethodException {
+ Method m = null;
+ m = aClass.getDeclaredMethod(methodName, parameterTypes);
+ m.setAccessible(true);
+ return m;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/StringHelper.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/StringHelper.java
new file mode 100644
index 00000000000..9513da778c5
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/StringHelper.java
@@ -0,0 +1,174 @@
+/*****************************************************************************
+ * Copyright (c) 2008-2013 CEA LIST.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+/**
+ * A library of static helpers for string-related operations
+ */
+public class StringHelper {
+
+ /**
+ * Compares two strings. Two Strings are equal if they are both null,
+ * or if s1.equals(s2)
+ *
+ * @param s1
+ * @param s2
+ * @return
+ */
+ public static boolean equals(String s1, String s2) {
+ if (s1 == s2) {
+ return true;
+ }
+
+ if (s1 == null) {
+ return false;
+ }
+
+ return s1.equals(s2);
+ }
+
+ /**
+ * Converts a camelCase name to a human-readable Label
+ *
+ * Example: aUMLElement -> A UML element
+ *
+ * @param camelCaseName
+ * @return
+ * A formatted version of the given variable name
+ */
+ public static String camelCaseToLabel(String camelCaseName) {
+ // "CamelCase" to "Natural case"
+ String formattedValue = camelCaseName;
+
+ // replace fooBar by foo Bar
+ formattedValue = formattedValue.replaceAll("([a-z])([A-Z])", "$1 $2"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // replace FOOAndBar by FOO And Bar
+ formattedValue = formattedValue.replaceAll("([A-Z]+)([A-Z])([a-z])", "$1 $2$3"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Capitalize the first word and lower the other ones : foo Bar -> Foo bar
+ // Keep the upper case for acronyms FOO Bar -> FOO bar
+ String[] words = formattedValue.split("\\s+"); //$NON-NLS-1$
+ formattedValue = firstToUpper(words[0]);
+ for (int i = 1; i < words.length; i++) {
+ formattedValue += " "; //$NON-NLS-1$
+ if (words[i].matches("^[A-Z]{2,}")) { //$NON-NLS-1$
+ formattedValue += words[i];
+ } else {
+ formattedValue += firstToLower(words[i]);
+ }
+ }
+
+ //Activator.log.debug("\"" + formattedValue + "\""); //$NON-NLS-1$ //$NON-NLS-2$
+ return formattedValue;
+ }
+
+ /**
+ * @param source
+ * @return
+ * the given String with the first letter capitalized
+ */
+ public static String firstToUpper(String source) {
+ if (source.length() == 0) {
+ return source;
+ }
+ return source.substring(0, 1).toUpperCase() + source.substring(1);
+ }
+
+ /**
+ * @param source
+ * @return
+ * the given String with the first letter lowered
+ */
+ public static String firstToLower(String source) {
+ if (source.length() == 0) {
+ return source;
+ }
+ return source.substring(0, 1).toLowerCase() + source.substring(1);
+ }
+
+ /**
+ * Returns the same string, except for "null" which is converted to the empty string
+ *
+ * @param str
+ * @return
+ */
+ public static String trimToEmpty(String str) {
+ return str == null ? "" : str; //$NON-NLS-1$
+ }
+
+
+ /*
+ *
+ * The following methods have been copied from UML2Util (org.eclipse.uml2.common.util/UML2Util)
+ */
+
+
+ /**
+ * Obtains a valid Java identifier based on the specified name.
+ *
+ * @param name
+ * The name from which to obtain a valid identifier.
+ * @return A valid (Java) identifier.
+ */
+ public static String toJavaIdentifier(String label) {
+ return getValidJavaIdentifier(label, new StringBuffer()).toString();
+ }
+
+ /**
+ * Appends a valid Java identifier based on the specified name to the
+ * specified buffer.
+ *
+ * @param name
+ * The name from which to obtain the valid identifier.
+ * @param validJavaIdentifier
+ * The buffer to which to append the valid identifier.
+ * @return The buffer.
+ */
+ protected static StringBuffer getValidJavaIdentifier(String name, StringBuffer validJavaIdentifier) {
+
+ if (isEmpty(name)) {
+ validJavaIdentifier.append('_');
+ } else {
+ char char_0 = name.charAt(0);
+
+ if (Character.isJavaIdentifierStart(char_0)) {
+ validJavaIdentifier.append(char_0);
+ } else {
+ validJavaIdentifier.append('_');
+
+ if (Character.isJavaIdentifierPart(char_0)) {
+ validJavaIdentifier.append(char_0);
+ }
+ }
+
+ for (int i = 1; i < name.length(); i++) {
+ char char_i = name.charAt(i);
+
+ if (Character.isJavaIdentifierPart(char_i)) {
+ validJavaIdentifier.append(char_i);
+ }
+ }
+ }
+
+ return validJavaIdentifier;
+ }
+
+ /**
+ * Determines whether the specified string is empty, i.e. is <code>null</code> or has a length of zero.
+ *
+ * @param string
+ * The string in question.
+ * @return <code>true</code> if the string is empty; <code>false</code> otherwise.
+ */
+ public static boolean isEmpty(String string) {
+ return string == null || string.length() == 0;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Suppliers2.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Suppliers2.java
new file mode 100644
index 00000000000..4dbfd0ecebf
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/Suppliers2.java
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.papyrus.infra.tools.Activator;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Utilities for working with suppliers that are not provided by {@linkplain Suppliers Guava}.
+ */
+public class Suppliers2 {
+ /**
+ * Not instantiable by clients.
+ */
+ private Suppliers2() {
+ super();
+ }
+
+ /**
+ * Obtain a supplier that eventually provides the value of a {@code future} result.
+ * Accessing the supplier returns {@code null} until the {@code future}'s value is
+ * available, after which it returns that value. If the future completes with an
+ * exception, then the supplier will always provide {@code null} and the exception
+ * will not be accessible.
+ *
+ * @param future
+ * a future result
+ * @return a supplier of the eventual value of the {@code future}
+ */
+ public static <V> Supplier<V> eventualSupplier(Future<V> future) {
+ return eventualSupplier(future, null);
+ }
+
+ /**
+ * Obtain a supplier that eventually provides the value of a {@code future} result.
+ * Accessing the supplier returns the given {@code defaultValue} until the {@code future}'s value is
+ * available, after which it returns that value. If the future completes with an
+ * exception, then the supplier will always provide the default and the exception
+ * will not be accessible.
+ *
+ * @param future
+ * a future result
+ * @param defaultValue
+ * the default value to provide until the future is done
+ * @return a supplier of the eventual value of the {@code future}
+ */
+ public static <V> Supplier<V> eventualSupplier(Future<V> future, V defaultValue) {
+ return (future instanceof ListenableFuture<?>)
+ ? new ListenableFutureSupplier<V>((ListenableFuture<V>) future, defaultValue)
+ : new FutureSupplier<V>(future, defaultValue);
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class FutureSupplier<V> implements Supplier<V> {
+ private Future<V> future;
+ private V value;
+
+ FutureSupplier(Future<V> future, V defaultValue) {
+ this.future = future;
+ this.value = defaultValue;
+ }
+
+ public V get() {
+ if ((value == null) && (future != null) && future.isDone()) {
+ try {
+ if (future.isCancelled()) {
+ Activator.log.warn("Future of EventualSupplier was cancelled: " + future); //$NON-NLS-1$
+ } else {
+ value = future.get();
+ }
+ } catch (InterruptedException e) {
+ // Shouldn't happen on a done future
+ Activator.log.error("Interrupted on a done future.", e); //$NON-NLS-1$
+ } catch (ExecutionException e) {
+ // Normal case. There will never be a value
+ Activator.log.error("Future execution failed", e.getCause());
+ } finally {
+ future = null;
+ }
+ }
+
+ return value;
+ }
+ }
+
+ private static class ListenableFutureSupplier<V> implements Supplier<V> {
+ private AtomicReference<V> value;
+
+ ListenableFutureSupplier(ListenableFuture<V> future, V defaultValue) {
+ value = new AtomicReference<V>(defaultValue);
+
+ Futures.addCallback(future, new FutureCallback<V>() {
+ public void onSuccess(V result) {
+ value.set(result);
+ }
+
+ public void onFailure(Throwable t) {
+ // Normal case. There will never be a value
+ Activator.log.error("Future execution failed", t);
+ }
+ });
+ }
+
+ public V get() {
+ return value.get();
+ }
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypeUtils.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypeUtils.java
new file mode 100644
index 00000000000..05439dcf825
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypeUtils.java
@@ -0,0 +1,185 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms 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 - bug 433206
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.tools.util;
+
+import java.math.BigDecimal;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author VL222926
+ *
+ */
+public class TypeUtils {
+
+ /**
+ * Constructor.
+ *
+ */
+ private TypeUtils() {
+ // to prevent instanciation
+ }
+
+ /**
+ *
+ * @param str
+ * a string representing a boolean
+ * @return
+ * <code>true</code> if the string represents a valid boolean
+ */
+ public static final boolean isBooleanValue(String str) {
+ return "true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ *
+ * @param str
+ * a string representing a boolean
+ * @return
+ * <code>true</code> if the string represents a valid boolean
+ */
+ public static final boolean isIntegerValue(String str) {
+ /** the pattern that checks visual ids are valid integers */
+ Pattern digit = Pattern.compile("-?\\d+"); //$NON-NLS-1$
+ boolean result = false;
+ Matcher matcher = digit.matcher(str);
+ if (matcher != null) {
+ result = matcher.matches();
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param str
+ * a string
+ * @return
+ * <code>true</code> if the string represents a double
+ */
+ public static final boolean isDoubleValue(String str) {
+ try {
+ new BigDecimal(str);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param str
+ * a string
+ * @return
+ * <code>true</code> if the string represents a double
+ */
+ public static final boolean isNaturalValue(String str) {
+ boolean res = isIntegerValue(str);
+ if (res) {
+ int tmp = Integer.parseInt(str);
+ return tmp >= 0;
+ }
+ return res;
+ }
+
+ /**
+ *
+ * @param object
+ * an object
+ * @return
+ * <code>true</code> if the object represents a numeric value
+ */
+ public static final boolean isNumericValue(Object object) {
+ if (object instanceof String) {
+ try {
+ new BigDecimal((String) object);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+ if (object instanceof Integer || object instanceof Double || object instanceof Float) {
+ return true;
+ }
+ return false;
+
+ }
+
+ /**
+ * Attempts to cast an {@code object} as the required {@code type}.
+ *
+ * @param object
+ * an object to cast
+ * @param type
+ * the type to cast it to
+ *
+ * @return the {@code object} or {@code null} if it is not of the required {@code type}
+ */
+ public static <T> T as(Object object, Class<T> type) {
+ T result = null;
+
+ if (type.isInstance(object)) {
+ result = type.cast(object);
+ }
+
+ return result;
+ }
+
+ /**
+ * Attempts to cast an {@code object} as an instance of the type implied by the given {@code default_}.
+ *
+ * @param object
+ * an object to cast
+ * @param default_
+ * the default value to return if it is not of the required type. May not be {@code null}
+ *
+ * @return the {@code object} or {@code default_} if it is not of the required type
+ *
+ * @throws NullPointerException
+ * if {@code default_} is {@code null}
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T as(Object object, T default_) {
+ T result = default_;
+
+ if (default_.getClass().isInstance(object)) {
+ result = (T) object;
+ }
+
+ return result;
+ }
+
+ /**
+ * Attempts to cast the object at an {@code index} of an {@code array} as the required {@code type}.
+ *
+ * @param array
+ * an array of objects
+ * @param index
+ * the position of an object in the {@code array}
+ * @param type
+ * the type to cast it to
+ *
+ * @return the {@code index}-th object in the {@code array} or {@code null} if it is not of the required {@code type} or the {@code array} has no such {@code index}
+ */
+ public static <T> T as(Object[] array, int index, Class<T> type) {
+ Object object = ((index >= 0) && (index < array.length)) ? array[index] : null;
+ T result = null;
+
+ if (type.isInstance(object)) {
+ result = type.cast(object);
+ }
+
+ return result;
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypesConstants.java b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypesConstants.java
new file mode 100644
index 00000000000..b4c256c14a5
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus/infra/tools/util/TypesConstants.java
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * Copyright (c) 2013 CEA LIST.
+ *
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.infra.tools.util;
+
+/**
+ *
+ * This class provides some constants used to identify java types
+ *
+ * @author vl222926
+ *
+ */
+public class TypesConstants {
+
+
+ private TypesConstants() {
+ // to prevent instanciation
+ }
+
+ public static final String STRING = "String"; //$NON-NLS-1$
+
+ public static final String BOOLEAN = "Boolean"; //$NON-NLS-1$
+
+ public static final String INTEGER = "Integer"; //$NON-NLS-1$
+
+ public static final String DOUBLE = "Double"; //$NON-NLS-1$
+
+}
diff --git a/plugins/infra/core/pom.xml b/plugins/infra/core/pom.xml
new file mode 100644
index 00000000000..575e5d7aaef
--- /dev/null
+++ b/plugins/infra/core/pom.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>org.eclipse.papyrus.infra-core</artifactId>
+ <packaging>pom</packaging>
+ <parent>
+ <groupId>org.eclipse.papyrus</groupId>
+ <artifactId>org.eclipse.papyrus.infra</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <name>Papyrus Infra Core</name>
+ <description>Core frameworks for Papyrus, including the Service Registry, Editor, and logging and other utilities.</description>
+
+ <modules>
+ <module>org.eclipse.papyrus.infra.core</module>
+ <module>org.eclipse.papyrus.infra.core.log</module>
+ <module>org.eclipse.papyrus.infra.core.pluginexplorer</module>
+ <module>org.eclipse.papyrus.infra.core.sasheditor</module>
+ <module>org.eclipse.papyrus.infra.core.sasheditor.di</module>
+ <module>org.eclipse.papyrus.infra.core.sashwindows.di</module>
+ <module>org.eclipse.papyrus.infra.tools</module>
+ </modules>
+</project>

Back to the top