Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/infra/core/org.eclipse.papyrus.infra.tools/src/org/eclipse/papyrus')
-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
46 files changed, 5296 insertions, 0 deletions
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$
+
+}

Back to the top