From ae293862c718921e47a62e456d48f3c19cfbe78e Mon Sep 17 00:00:00 2001
From: Christian W. Damus
Date: Tue, 23 Aug 2016 17:07:27 -0400
Subject: Bug 497865: [Control Mode] UI for dependent controlled units
https://bugs.eclipse.org/bugs/show_bug.cgi?id=497865
Add an option to the standard control (create fragment) dialog for
option to create sub-model unit (independently openable) or not (in
which case it's a 'shard').
Add an approver interface to the participant protocols for validation of
control/uncontrol requests and also to determine whether an object
supports the sub-model unit form of controlled resource. Use this to
let the UML participant disable the sub-model unit option for
non-packages.
Add a label decorator for controlled unit resources in the Project
Explorer, with different presentation for 'shards' as for sub-model
units that are independently openable.
Add a context menu on model elements to toggle their independent
sub-model status when they are currently controlled units. This is
supported by another optional participant protocol for changing
sub-unit mode so that for UML we can add/remove redundant profile
applications on packages as necessary.
(cherry-picked from streams/2.0-maintenance)
Change-Id: I08ac9cc64d70432851f8e7e815f352f4b00d50f2
---
.../resource/controlMode.mediawiki | 39 +++-
.../resource/images/ControlAction.png | Bin 20669 -> 78105 bytes
.../resource/images/ControlDialog.png | Bin 15203 -> 31434 bytes
.../resource/images/Decorations.png | Bin 0 -> 97131 bytes
.../resource/images/ToggleSubmodelAction.png | Bin 0 -> 89416 bytes
.../resource/images/UncontrolAction.png | Bin 21812 -> 85197 bytes
.../resource/AbstractCrossReferenceIndex.java | 141 ++++++++----
.../emf/internal/resource/CrossReferenceIndex.java | 174 +++++++++++++-
.../resource/CrossReferenceIndexHandler.java | 10 +-
.../resource/OnDemandCrossReferenceIndex.java | 8 +-
.../infra/emf/resource/ICrossReferenceIndex.java | 196 +++++++++++++---
.../infra/emf/resource/ShardResourceHelper.java | 19 +-
.../META-INF/MANIFEST.MF | 5 +-
.../build.properties | 13 +-
.../icons/full/ovr16/shard_deco.png | Bin 0 -> 1283 bytes
.../icons/full/ovr16/shard_deco.pxm | Bin 0 -> 146911 bytes
.../icons/full/ovr16/shard_deco@2x.png | Bin 0 -> 1330 bytes
.../icons/full/ovr16/submodel_deco.png | Bin 0 -> 1276 bytes
.../icons/full/ovr16/submodel_deco.pxm | Bin 0 -> 146911 bytes
.../icons/full/ovr16/submodel_deco@2x.png | Bin 0 -> 1330 bytes
.../plugin.properties | 40 +++-
.../plugin.xml | 79 ++++++-
.../schema/participant.exsd | 253 +++++++++++----------
.../services/controlmode/ControlModeManager.java | 171 +++++++++++++-
.../services/controlmode/ControlModePlugin.java | 22 +-
.../controlmode/ControlModeRequestParameters.java | 25 +-
.../services/controlmode/IControlModeManager.java | 96 +++++++-
.../controlmode/commands/BasicControlCommand.java | 13 +-
.../controlmode/handler/ControlCommandHandler.java | 26 ++-
.../handler/UncontrolCommandHandler.java | 19 +-
.../internal/handler/SubmodelState.java | 119 ++++++++++
.../internal/handler/ToggleSubmodelHandler.java | 108 +++++++++
.../internal/ui/ControlledUnitLabelDecorator.java | 184 +++++++++++++++
.../controlmode/messages/messages.properties | 15 ++
.../participants/IControlCommandApprover.java | 57 +++++
.../participants/IShardModeCommandParticipant.java | 72 ++++++
.../controlmode/ui/CreateModelFragmentDialog.java | 161 ++++++++++++-
.../ui/IControlModeFragmentDialogProvider.java | 75 +++++-
.../.classpath | 14 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
.../META-INF/MANIFEST.MF | 6 +-
.../pom.xml | 2 +-
.../profile/UMLProfileControlParticipant.java | 75 +++++-
...MoveStereotypeApplicationToControlResource.java | 8 +-
.../profile/helpers/ProfileApplicationHelper.java | 10 +-
.../helpers/SafeDialogOpenerDuringValidation.java | 12 +-
46 files changed, 1959 insertions(+), 314 deletions(-)
create mode 100644 plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/Decorations.png
create mode 100644 plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ToggleSubmodelAction.png
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.png
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.pxm
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco@2x.png
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.png
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.pxm
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco@2x.png
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/internal/handler/SubmodelState.java
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/internal/handler/ToggleSubmodelHandler.java
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/internal/ui/ControlledUnitLabelDecorator.java
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/participants/IControlCommandApprover.java
create mode 100644 plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/participants/IShardModeCommandParticipant.java
(limited to 'plugins')
diff --git a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/controlMode.mediawiki b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/controlMode.mediawiki
index cca9840a0ac..6598fad7c1f 100644
--- a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/controlMode.mediawiki
+++ b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/controlMode.mediawiki
@@ -10,22 +10,53 @@ This is specially helpful working in collaborative environments, since the Team
==Create a submodel==
To create a submodel:
#Right click on the element which you wish to move to a different resource (a Package in the common case)
-#Select ''Create Submodel''[[Image:images/ControlAction.png]]
+#Select ''Create Submodel'' [[Image:images/ControlAction.png]]
#The following dialog appears. [[Image:images/ControlDialog.png]]
#Select the desired location for the new resource
#Click ''OK''.
+'''Important:''' The selected element has not been moved just yet. The model editor will be dirty i.e. there will be an asterisk next to its name in the tab. In order to complete the submodel creation, you need to '''save''' your model.
+The new resource is created in the chosen location with the chosen file name, whilst a green decorator is shown in the Model Explorer view of the parent model to indicate this element is in a submodel.
-'''Important:''' The selected element has not been moved just yet. The model editor will be dirty i.e. there will be an asterisk next to its name in the tab. In order to complete the submodel creation, you need to '''save''' your model.
+Note that, for a package, the dialog shows an option to have it be openable and editable in its own editor, independently of the package containing it (and the one containing that, etc.).
+This can be convenient, but it does mean that the package has to redundantly apply all of the
+profile applications applied to the packages containing it, so that stereotypes can properly be
+manipulated in the editor. This can result in inconsistencies if a profile definition changes
+over time and not all applications in all packages are kept up-to-date consistently with
+stereotype migrations. This option is available only for packages, because only they can have
+profile applications. For other kinds of elements, this option is disabled and the resources
+created for them will always open in the context of the containing model.
+
+When this option is not selected, then the resulting submodel cannot be opened separately.
+Attempting to open it, usually by double-clicking it in the Project Explorer, will instead
+open the top-most independent submodel (or the root model package). Or, if that is already
+open, just activate its editor. This ensures that the nesting package context, and especially
+its profile applications, is always available to properly manage stereotypes.
+
+==Toggle the Independent Submodel Support==
+
+Once a package has been stored in a submodel resource, the support for editing it independently
+can be added and removed at any time:
+#Right-click on the submodel package in the Model Explorer.
+#If the package is independently editable, the ''Independent Submodel'' menu item will have a check mark. Otherwise, it will not.
+#Click the ''Independent Submodel'' menu item to change whether the package can be edited independently or not.
+[[Image:images/ToggleSubmodelAction.png]]
+
+'''Important:''' These changes will only take permanent effect on the next '''save''' action. Until then, the resource on disk is not changed and so what happens when it is opened in an editor will not change.
+
+In the Project Explorer view, the submodel resource is indicated by a filled blue
+note-paper decoration or a hollow blue decoration in the case that it is not an independently
+openable submodel.
+
+[[Image:images/Decorations.png|400px]]
-The new resource is created in the chosen location with the chosen file name, whilst a green decorator is shown in the model explorer view of the parent model to indicate this element is in a submodel.
==Reintegrate a submodel into the main model==
To reintegrate a submodel into the main model:
#Open the parent model
-#Right click on the submodel element in the model explorer (recognizable with the green decorator).
+#Right click on the submodel element in the Model Explorer (recognizable with the green decorator).
#Select ''Reintegrate Submodel''
[[Image:images/UncontrolAction.png]]
diff --git a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlAction.png b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlAction.png
index e367d368785..d2ae4b42e11 100644
Binary files a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlAction.png and b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlAction.png differ
diff --git a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlDialog.png b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlDialog.png
index 6070123c5f1..73cb67e17db 100644
Binary files a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlDialog.png and b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ControlDialog.png differ
diff --git a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/Decorations.png b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/Decorations.png
new file mode 100644
index 00000000000..ace910b1d35
Binary files /dev/null and b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/Decorations.png differ
diff --git a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ToggleSubmodelAction.png b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ToggleSubmodelAction.png
new file mode 100644
index 00000000000..67127c4f343
Binary files /dev/null and b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/ToggleSubmodelAction.png differ
diff --git a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/UncontrolAction.png b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/UncontrolAction.png
index 9a8573519e3..5b525e97731 100644
Binary files a/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/UncontrolAction.png and b/plugins/doc/org.eclipse.papyrus.infra.services.controlmode.doc/resource/images/UncontrolAction.png differ
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/AbstractCrossReferenceIndex.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/AbstractCrossReferenceIndex.java
index b1470389b6e..f755c220494 100644
--- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/AbstractCrossReferenceIndex.java
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/AbstractCrossReferenceIndex.java
@@ -21,6 +21,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
@@ -51,14 +52,14 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde
final SetMultimap outgoingReferences = HashMultimap.create();
final SetMultimap incomingReferences = HashMultimap.create();
- final SetMultimap resourceToShards = HashMultimap.create();
- final SetMultimap shardToParents = HashMultimap.create();
+ final SetMultimap resourceToSubunits = HashMultimap.create();
+ final SetMultimap subunitToParents = HashMultimap.create();
// These are abstracted as URIs without extension
SetMultimap aggregateOutgoingReferences;
SetMultimap aggregateIncomingReferences;
- SetMultimap aggregateResourceToShards;
- SetMultimap aggregateShardToParents;
+ SetMultimap aggregateResourceToSubunits;
+ SetMultimap aggregateSubunitToParents;
final SetMultimap shards = HashMultimap.create();
/**
@@ -219,36 +220,53 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde
}
@Override
- public ListenableFuture> getShardsAsync() {
- return afterIndex(getShardsCallable());
+ public ListenableFuture> getSubunitsAsync() {
+ return afterIndex(getSubunitsCallable());
}
@Override
- public SetMultimap getShards() throws CoreException {
- return sync(afterIndex(getShardsCallable()));
+ public SetMultimap getSubunits() throws CoreException {
+ return sync(afterIndex(getSubunitsCallable()));
}
- Callable> getShardsCallable() {
- return sync(() -> ImmutableSetMultimap.copyOf(resourceToShards));
+ Callable> getSubunitsCallable() {
+ return sync(() -> ImmutableSetMultimap.copyOf(resourceToSubunits));
}
@Override
- public ListenableFuture> getShardsAsync(URI resourceURI) {
- return afterIndex(getShardsCallable(resourceURI));
+ public ListenableFuture> getSubunitsAsync(URI resourceURI) {
+ return getSubunitsAsync(resourceURI, true);
}
@Override
- public Set getShards(URI resourceURI) throws CoreException {
- return sync(afterIndex(getShardsCallable(resourceURI)));
+ public Set getSubunits(URI resourceURI) throws CoreException {
+ return getSubunits(resourceURI, true);
}
- Callable> getShardsCallable(URI shardURI) {
+ @Override
+ public ListenableFuture> getSubunitsAsync(URI resourceURI, boolean shardOnly) {
+ return afterIndex(getSubunitsCallable(resourceURI, shardOnly));
+ }
+
+ @Override
+ public Set getSubunits(URI resourceURI, boolean shardOnly) throws CoreException {
+ return sync(afterIndex(getSubunitsCallable(resourceURI, shardOnly)));
+ }
+
+ Callable> getSubunitsCallable(URI shardURI, boolean shardOnly) {
return sync(() -> {
String ext = shardURI.fileExtension();
URI withoutExt = shardURI.trimFileExtension();
- Set result = getAggregateShards().get(withoutExt).stream()
- // Only those that actually are shards
- .filter(AbstractCrossReferenceIndex.this::isShard0)
+
+ Stream intermediateResult = getAggregateShards().get(withoutExt).stream();
+
+ if (shardOnly) {
+ // Only those that actually are shards
+ intermediateResult = intermediateResult
+ .filter(AbstractCrossReferenceIndex.this::isShard0);
+ }
+
+ Set result = intermediateResult
.map(uri -> uri.appendFileExtension(ext))
.collect(Collectors.toSet());
@@ -260,16 +278,16 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde
SetMultimap result;
synchronized (sync) {
- if (aggregateResourceToShards == null) {
+ if (aggregateResourceToSubunits == null) {
// Compute the aggregate now
- aggregateResourceToShards = HashMultimap.create();
- for (Map.Entry next : resourceToShards.entries()) {
- aggregateResourceToShards.put(next.getKey().trimFileExtension(),
+ aggregateResourceToSubunits = HashMultimap.create();
+ for (Map.Entry next : resourceToSubunits.entries()) {
+ aggregateResourceToSubunits.put(next.getKey().trimFileExtension(),
next.getValue().trimFileExtension());
}
}
- result = aggregateResourceToShards;
+ result = aggregateResourceToSubunits;
}
return result;
@@ -277,21 +295,32 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde
@Override
public ListenableFuture> getParentsAsync(URI shardURI) {
- return afterIndex(getParentsCallable(shardURI));
+ return getParentsAsync(shardURI, true);
+ }
+
+ @Override
+ public ListenableFuture> getParentsAsync(URI resourceURI, boolean shardOnly) {
+ return afterIndex(getParentsCallable(resourceURI, shardOnly));
}
@Override
public Set getParents(URI shardURI) throws CoreException {
- return sync(afterIndex(getParentsCallable(shardURI)));
+ return getParents(shardURI, true);
}
- Callable> getParentsCallable(URI shardURI) {
+ @Override
+ public Set getParents(URI resourceURI, boolean shardOnly) throws CoreException {
+ return sync(afterIndex(getParentsCallable(resourceURI, shardOnly)));
+ }
+
+ Callable> getParentsCallable(URI shardURI, boolean shardOnly) {
return sync(() -> {
Set result;
URI withoutExt = shardURI.trimFileExtension();
- // If it's not a shard, it has no parents, by definition
- if (!isShard0(withoutExt)) {
+ // If it's not a shard, it has no parents, by definition, unless we're also
+ // including sub-model units
+ if (shardOnly && !isShard0(withoutExt)) {
result = Collections.emptySet();
} else {
String ext = shardURI.fileExtension();
@@ -309,16 +338,16 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde
SetMultimap result;
synchronized (sync) {
- if (aggregateShardToParents == null) {
+ if (aggregateSubunitToParents == null) {
// Compute the aggregate now
- aggregateShardToParents = HashMultimap.create();
- for (Map.Entry next : shardToParents.entries()) {
- aggregateShardToParents.put(next.getKey().trimFileExtension(),
+ aggregateSubunitToParents = HashMultimap.create();
+ for (Map.Entry next : subunitToParents.entries()) {
+ aggregateSubunitToParents.put(next.getKey().trimFileExtension(),
next.getValue().trimFileExtension());
}
}
- result = aggregateShardToParents;
+ result = aggregateSubunitToParents;
}
return result;
@@ -326,53 +355,69 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde
@Override
public ListenableFuture> getRootsAsync(URI shardURI) {
- return afterIndex(getRootsCallable(shardURI));
+ return getRootsAsync(shardURI, true);
+ }
+
+ @Override
+ public ListenableFuture> getRootsAsync(URI shardURI, boolean shardOnly) {
+ return afterIndex(getRootsCallable(shardURI, shardOnly));
}
@Override
public Set getRoots(URI shardURI) throws CoreException {
- return sync(afterIndex(getRootsCallable(shardURI)));
+ return getRoots(shardURI, true);
}
@Override
- public Set getRoots(URI shardURI, ICrossReferenceIndex alternate) throws CoreException {
+ public Set getRoots(URI shardURI, boolean shardOnly) throws CoreException {
+ return sync(afterIndex(getRootsCallable(shardURI, shardOnly)));
+ }
+
+ @Override
+ public Set getRoots(URI subunitURI, ICrossReferenceIndex alternate) throws CoreException {
+ return getRoots(subunitURI, true, alternate);
+ }
+
+ @Override
+ public Set getRoots(URI subunitURI, boolean shardOnly, ICrossReferenceIndex alternate) throws CoreException {
if (alternate == this) {
throw new IllegalArgumentException("self alternate"); //$NON-NLS-1$
}
Callable> elseCallable = (alternate == null)
? null
- : () -> alternate.getRoots(shardURI);
+ : () -> alternate.getRoots(subunitURI, shardOnly);
- return ifAvailable(getRootsCallable(shardURI), elseCallable);
+ return ifAvailable(getRootsCallable(subunitURI, shardOnly), elseCallable);
}
- Callable> getRootsCallable(URI shardURI) {
+ Callable> getRootsCallable(URI subunitURI, boolean shardOnly) {
return sync(() -> {
Set result;
- URI withoutExt = shardURI.trimFileExtension();
+ URI withoutExt = subunitURI.trimFileExtension();
- // If it's not a shard, it has no roots, by definition
- if (!isShard0(withoutExt)) {
+ // If we need shards only and it's not a shard, it has no roots, by definition
+ if (shardOnly && !isShard0(withoutExt)) {
result = Collections.emptySet();
} else {
// TODO: Cache this?
ImmutableSet.Builder resultBuilder = ImmutableSet.builder();
- SetMultimap shardToParents = getAggregateShardToParents();
+ SetMultimap subunitToParents = getAggregateShardToParents();
// Breadth-first search of the parent graph
Queue queue = Lists.newLinkedList();
Set cycleDetect = Sets.newHashSet();
- String ext = shardURI.fileExtension();
+ String ext = subunitURI.fileExtension();
queue.add(withoutExt);
for (URI next = queue.poll(); next != null; next = queue.poll()) {
if (cycleDetect.add(next)) {
- if (shardToParents.containsKey(next)) {
- queue.addAll(shardToParents.get(next));
- } else {
- // It's a root
+ // Even if it looks like a shard but has no parents, it's a root
+ if ((!shardOnly || isShard0(next)) && subunitToParents.containsKey(next)) {
+ queue.addAll(subunitToParents.get(next));
+ } else if (!next.equals(withoutExt)) {
+ // It's a root (and not the original resource we were asked about)
resultBuilder.add(next.appendFileExtension(ext));
}
}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndex.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndex.java
index df13d34186c..6730912a182 100644
--- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndex.java
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndex.java
@@ -18,6 +18,9 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.xml.parsers.SAXParser;
@@ -32,6 +35,8 @@ import org.eclipse.papyrus.infra.emf.Activator;
import org.eclipse.papyrus.infra.emf.resource.index.IWorkspaceModelIndexProvider;
import org.eclipse.papyrus.infra.emf.resource.index.WorkspaceModelIndex;
import org.eclipse.papyrus.infra.emf.resource.index.WorkspaceModelIndex.PersistentIndexHandler;
+import org.eclipse.papyrus.infra.emf.resource.index.WorkspaceModelIndexAdapter;
+import org.eclipse.papyrus.infra.emf.resource.index.WorkspaceModelIndexEvent;
import org.xml.sax.helpers.DefaultHandler;
import com.google.common.util.concurrent.ListenableFuture;
@@ -43,6 +48,8 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
private static final CrossReferenceIndex INSTANCE = new CrossReferenceIndex();
+ private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>();
+
private final WorkspaceModelIndex index;
/**
@@ -56,9 +63,23 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
"papyrusCrossRefs", //$NON-NLS-1$
"org.eclipse.emf.ecore.xmi", //$NON-NLS-1$
null, indexer(), MAX_INDEX_JOBS);
+ index.addListener(new WorkspaceModelIndexAdapter() {
+
+ @Override
+ public void indexCalculated(WorkspaceModelIndexEvent event) {
+ indexChanged();
+ }
+
+ @Override
+ public void indexRecalculated(WorkspaceModelIndexEvent event) {
+ indexChanged();
+ }
+ });
}
public void dispose() {
+ listeners.clear();
+
index.dispose();
}
@@ -66,6 +87,63 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
return INSTANCE;
}
+ /**
+ * Registers a {@code handler} for updates to the index.
+ *
+ * @param handler
+ * invoked whenever the contents of the index change. No assumption
+ * must be made about the thread or kind of thread on which this call-back
+ * is invoked. If the thread context is important, use the
+ * {@link #onIndexChanged(Consumer, Executor)} API, instead
+ *
+ * @return a runnable that, when executed, will disconnect the {@code handler} so that
+ * it will no longer receive updates
+ *
+ * @see #onIndexChanged(Consumer, Executor)
+ */
+ public Runnable onIndexChanged(Consumer super CrossReferenceIndex> handler) {
+ return onIndexChanged(handler, null);
+ }
+
+ /**
+ * Registers a {@code handler} for updates to the index.
+ *
+ * @param handler
+ * invoked whenever the contents of the index change. No assumption
+ * must be made about the thread or kind of thread on which this call-back
+ * is invoked
+ * @param exec
+ * an executor on which to submit invocation of the {@code handler}, in case
+ * it needs to run on a specific thread. May be {@code null} to run in
+ * whatever thread processes index updates (about which, then, no assumptions
+ * may be made by the handler
+ *
+ * @return a runnable that, when executed, will disconnect the {@code handler} so that
+ * it will no longer receive updates
+ */
+ public Runnable onIndexChanged(Consumer super CrossReferenceIndex> handler, Executor exec) {
+ Runnable result;
+
+ if (handler != null) {
+ Dispatcher dispatcher = new Dispatcher(this, handler, exec);
+ if (listeners.add(dispatcher)) {
+ result = dispatcher::dispose;
+ } else {
+ result = Dispatcher::pass;
+ }
+ } else {
+ result = Dispatcher::pass;
+ }
+
+ return result;
+ }
+
+ private void indexChanged() {
+ if (!listeners.isEmpty()) {
+ listeners.forEach(Dispatcher::dispatch);
+ }
+ }
+
//
// Indexing
//
@@ -117,12 +195,12 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
unindexResource(file);
// update the forward mapping
- resourceToShards.putAll(resourceURI, index.getShards());
+ resourceToSubunits.putAll(resourceURI, index.getShards());
outgoingReferences.putAll(resourceURI, index.getCrossReferences());
// and the reverse mapping
for (URI next : index.getShards()) {
- shardToParents.put(next, resourceURI);
+ subunitToParents.put(next, resourceURI);
}
for (URI next : index.getCrossReferences()) {
incomingReferences.put(next, resourceURI);
@@ -152,20 +230,20 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
synchronized (sync) {
// purge the aggregates (for model-set "resource without URI")
- aggregateResourceToShards = null;
- aggregateShardToParents = null;
+ aggregateResourceToSubunits = null;
+ aggregateSubunitToParents = null;
aggregateOutgoingReferences = null;
aggregateIncomingReferences = null;
setShard(resourceURI, false);
// And remove all traces of this resource
- resourceToShards.removeAll(resourceURI);
+ resourceToSubunits.removeAll(resourceURI);
outgoingReferences.removeAll(resourceURI);
// the multimap's entry collection that underlies the key-set
// is modified as we go, so take a safe copy of the keys
- for (URI next : new ArrayList<>(shardToParents.keySet())) {
- shardToParents.remove(next, resourceURI);
+ for (URI next : new ArrayList<>(subunitToParents.keySet())) {
+ subunitToParents.remove(next, resourceURI);
}
for (URI next : new ArrayList<>(incomingReferences.keySet())) {
incomingReferences.remove(next, resourceURI);
@@ -211,7 +289,7 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
this.isShard = handler.isShard();
this.crossReferences = handler.getCrossReferences();
- this.shards = handler.getShards();
+ this.shards = handler.getSubunits();
}
boolean isShard() {
@@ -246,4 +324,84 @@ public class CrossReferenceIndex extends AbstractCrossReferenceIndex {
return CrossReferenceIndex.INSTANCE.index;
}
}
+
+ private static final class Dispatcher {
+ private final CrossReferenceIndex owner;
+ private final Consumer super CrossReferenceIndex> handler;
+ private final Executor exec;
+
+ Dispatcher(CrossReferenceIndex owner,
+ Consumer super CrossReferenceIndex> handler,
+ Executor exec) {
+
+ super();
+
+ this.owner = owner;
+ this.handler = handler;
+ this.exec = exec;
+ }
+
+ static void pass() {
+ // Pass
+ }
+
+ public void dispose() {
+ owner.listeners.remove(this);
+ }
+
+ void dispatch() {
+ if (exec == null) {
+ handler.accept(owner);
+ } else {
+ exec.execute(() -> handler.accept(owner));
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((exec == null) ? 0 : exec.hashCode());
+ result = prime * result + ((handler == null) ? 0 : handler.hashCode());
+ result = prime * result + ((owner == null) ? 0 : owner.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Dispatcher other = (Dispatcher) obj;
+ if (exec == null) {
+ if (other.exec != null) {
+ return false;
+ }
+ } else if (!exec.equals(other.exec)) {
+ return false;
+ }
+ if (handler == null) {
+ if (other.handler != null) {
+ return false;
+ }
+ } else if (!handler.equals(other.handler)) {
+ return false;
+ }
+ if (owner == null) {
+ if (other.owner != null) {
+ return false;
+ }
+ } else if (!owner.equals(other.owner)) {
+ return false;
+ }
+ return true;
+ }
+
+ }
}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndexHandler.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndexHandler.java
index 4b6dbe96778..fe2a824a803 100644
--- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndexHandler.java
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/CrossReferenceIndexHandler.java
@@ -44,7 +44,7 @@ public class CrossReferenceIndexHandler extends DefaultHandler {
private Set crossReferences = Sets.newHashSet();
private XMIElement shard;
- private Set shards = Sets.newHashSet();
+ private Set subunits = Sets.newHashSet();
// The (optional) parent references in the annotation
private Set parents = Sets.newHashSet();
@@ -93,8 +93,8 @@ public class CrossReferenceIndexHandler extends DefaultHandler {
return shard != null;
}
- public Set getShards() {
- return shards;
+ public Set getSubunits() {
+ return subunits;
}
public Set getParents() {
@@ -145,8 +145,8 @@ public class CrossReferenceIndexHandler extends DefaultHandler {
// Don't index internal references
if (!xref.equals(fileURI)) {
if (element.isContainment()) {
- // Cross-resource containment is a shard relationship
- shards.add(xref.toString());
+ // Cross-resource containment is a sub-unit relationship
+ subunits.add(xref.toString());
} else if (isShard() && (element.parent == shard) && element.isRole(eAnnotationReferencesName)) {
// Handle shard parent resource reference. This is
// *not* a regular cross-resource reference
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/OnDemandCrossReferenceIndex.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/OnDemandCrossReferenceIndex.java
index 43090ea830a..9c7146bee09 100644
--- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/OnDemandCrossReferenceIndex.java
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/internal/resource/OnDemandCrossReferenceIndex.java
@@ -108,7 +108,7 @@ public class OnDemandCrossReferenceIndex extends AbstractCrossReferenceIndex {
}
@Override
- Callable> getShardsCallable() {
+ Callable> getSubunitsCallable() {
// We don't parse on-the-fly for child shards; it requires scanning
// the whole resource
return () -> ImmutableSetMultimap.of();
@@ -153,7 +153,7 @@ public class OnDemandCrossReferenceIndex extends AbstractCrossReferenceIndex {
doIndex(next);
// And then, breadth-first, its parents that aren't already indexed
- shardToParents.get(next).stream()
+ subunitToParents.get(next).stream()
.filter(((Predicate) shards::containsKey).negate())
.forEach(toIndex::offer);
}
@@ -178,13 +178,13 @@ public class OnDemandCrossReferenceIndex extends AbstractCrossReferenceIndex {
}
// Clear the aggregate map because we now have updates to include
- aggregateShardToParents = null;
+ aggregateSubunitToParents = null;
setShard(resourceURI, handler.isShard());
Set parents = handler.getParents().stream()
.map(URI::createURI)
.collect(Collectors.toSet());
- shardToParents.putAll(resourceURI, parents);
+ subunitToParents.putAll(resourceURI, parents);
}
}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ICrossReferenceIndex.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ICrossReferenceIndex.java
index 1e154444729..0f12edc3a3d 100644
--- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ICrossReferenceIndex.java
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ICrossReferenceIndex.java
@@ -200,64 +200,117 @@ public interface ICrossReferenceIndex {
boolean isShard(URI resourceURI) throws CoreException;
/**
- * Asynchronously queries the mapping of URIs of resources to URIs of shards that are their immediate
- * children.
+ * Asynchronously queries the mapping of URIs of resources to URIs of sub-units
+ * that are their direct children.
*
- * @return a future result of the mapping of resource URIs to shard URIs
+ * @return a future result of the mapping of resource URIs to sub-unit URIs
*/
- ListenableFuture> getShardsAsync();
+ ListenableFuture> getSubunitsAsync();
/**
- * Queries the mapping of URIs of resources to URIs of shards that are their immediate
- * children.
+ * Queries the mapping of URIs of resources to URIs of controlled units that are
+ * their direct children.
*
- * @return the mapping of resource URIs to shard URIs
+ * @return the mapping of resource URIs to sub-unit URIs
*
* @throws CoreException
- * if the index either fails to compute the shards or if
+ * if the index either fails to compute the sub-units or if
* the calling thread is interrupted in waiting for the result
*/
- SetMultimap getShards() throws CoreException;
+ SetMultimap getSubunits() throws CoreException;
/**
- * Asynchronously queries the URIs of resources that are immediate shards of a
+ * Asynchronously queries the URIs of controlled units that are direct children of a
+ * given resource. Equivalent to calling {@link #getSUbunitsAsync(URI, boolean)}
+ * with a {@code true} argument.
+ *
+ * @param resourceURI
+ * the URI of a resource
+ * @return a future result of the URIs of sub-units that are its direct children
+ *
+ * @see #getSubunitsAsync(URI, boolean)
+ */
+ ListenableFuture> getSubunitsAsync(URI resourceURI);
+
+ /**
+ * Asynchronously queries the URIs of controlled units that are direct children of a
* given resource.
*
* @param resourceURI
* the URI of a resource
- * @return a future result of the URIs of shards that are its immediate children
+ * @param shardOnly
+ * whether to consider only sub-units that are shards
+ * @return a future result of the URIs of sub-units that are its direct children
+ */
+ ListenableFuture> getSubunitsAsync(URI resourceURI, boolean shardOnly);
+
+ /**
+ * Queries the URIs of controlled units that are direct children of a
+ * given resource. Equivalent to calling {@link #getSubunits(URI, boolean)}
+ * with a {@code true} argument.
+ *
+ * @param resourceURI
+ * the URI of a resource
+ * @return the URIs of sub-units that are its direct children
+ *
+ * @throws CoreException
+ * if the index either fails to compute the sub-units or if
+ * the calling thread is interrupted in waiting for the result
+ * @see #getSubunits(URI, boolean)
*/
- ListenableFuture> getShardsAsync(URI resourceURI);
+ Set getSubunits(URI resourceURI) throws CoreException;
/**
- * Queries the URIs of resources that are immediate shards of a
+ * Queries the URIs of controlled units that are direct children of a
* given resource.
*
* @param resourceURI
* the URI of a resource
- * @return the URIs of shards that are its immediate children
+ * @param shardOnly
+ * whether to consider only sub-units that are shards
+ * @return the URIs of sub-units that are its direct children
*
* @throws CoreException
- * if the index either fails to compute the shards or if
+ * if the index either fails to compute the sub-units or if
* the calling thread is interrupted in waiting for the result
*/
- Set getShards(URI resourceURI) throws CoreException;
+ Set getSubunits(URI resourceURI, boolean shardOnly) throws CoreException;
/**
* Asynchronously queries URIs of resources that are immediate parents of a given
- * (potential) shard resource.
+ * (potential) shard resource. Equivalent to calling {@link #getParentsAsync(URI, boolean)}
+ * with a {@code true} argument.
*
* @param shardURI
* the URI of a potential shard resource. It needs not necessarily actually
* be a shard, in which case it trivially wouldn't have any parents
* @return the future result of the URIs of resources that are immediate parents of
* the shard
+ *
+ * @see #getParentsAsync(URI, boolean)
*/
ListenableFuture> getParentsAsync(URI shardURI);
+ /**
+ * Asynchronously queries URIs of resources that are immediate parents of a given
+ * resource, whether it is a shard or a sub-model unit.
+ *
+ * @param resourceURI
+ * the URI of a potential shard or sub-model resource. It needs not necessarily
+ * actually be a shard or a sub-model, in which case it wouldn't have any parents
+ * @param shardOnly
+ * whether to consider only shards as validly having parents (useful for
+ * determining required resource dependencies)
+ *
+ * @return the future result of the URIs of resources that are immediate parents of
+ * the resource
+ */
+ ListenableFuture> getParentsAsync(URI resourceURI, boolean shardOnly);
+
/**
* Queries URIs of resources that are immediate parents of a given
- * (potential) shard resource.
+ * (potential) shard resource. Equivalent to calling {@link #getParents(URI, boolean)}
+ * with a {@code true} argument.
*
* @param shardURI
* the URI of a potential shard resource. It needs not necessarily actually
@@ -268,23 +321,63 @@ public interface ICrossReferenceIndex {
* @throws CoreException
* if the index either fails to compute the parents or if
* the calling thread is interrupted in waiting for the result
+ *
+ * @see #getParents(URI, boolean)
*/
Set getParents(URI shardURI) throws CoreException;
+ /**
+ * Queries URIs of resources that are immediate parents of a given
+ * resource, whether it is a shard or a sub-model unit.
+ *
+ * @param resourceURI
+ * the URI of a potential shard or sub-model resource. It needs not necessarily
+ * actually be a shard or a sub-model, in which case it wouldn't have any parents
+ * @param shardOnly
+ * whether to consider only shards as validly having parents (useful for
+ * determining required resource dependencies)
+ * @return the URIs of resources that are immediate parents of
+ * the resource
+ *
+ * @throws CoreException
+ * if the index either fails to compute the parents or if
+ * the calling thread is interrupted in waiting for the result
+ */
+ Set getParents(URI resourceURI, boolean shardOnly) throws CoreException;
+
/**
* Asynchronously queries URIs of resources that are roots (ultimate parents) of a given
- * (potential) shard resource.
+ * (potential) shard resource. Equivalent to calling {@link #getRootsAsync(URI, boolean)}
+ * with a {@code true} argument.
*
* @param shardURI
* the URI of a potential shard resource. It needs not necessarily actually
* be a shard, in which case it trivially wouldn't have any parents
* @return the future result of the URIs of resources that are roots of its parent graph
+ *
+ * @see #getRootsAsync(URI, boolean)
*/
ListenableFuture> getRootsAsync(URI shardURI);
+ /**
+ * Asynchronously queries URIs of resources that are roots (ultimate parents) of a given
+ * resource.
+ *
+ * @param resourceURI
+ * the URI of a potential sub-unit resource. It needs not necessarily actually
+ * be a sub-unit, in which case it wouldn't have any parents and, therefore,
+ * no roots
+ * @param shardOnly
+ * whether to consider only shards as validly having roots (useful for
+ * determining required resource dependencies)
+ * @return the future result of the URIs of resources that are roots of its parent graph
+ */
+ ListenableFuture> getRootsAsync(URI resourceURI, boolean shardOnly);
+
/**
* Queries URIs of resources that are roots (ultimate parents) of a given
- * (potential) shard resource.
+ * (potential) shard resource. Equivalent to calling {@link #getRoots(URI, boolean)}
+ * with a {@code true} argument.
*
* @param shardURI
* the URI of a potential shard resource. It needs not necessarily actually
@@ -294,17 +387,20 @@ public interface ICrossReferenceIndex {
* @throws CoreException
* if the index either fails to compute the roots or if
* the calling thread is interrupted in waiting for the result
+ * @see #getRoots(URI, boolean)
*/
Set getRoots(URI shardURI) throws CoreException;
/**
- * Attempts to queries URIs of resources that are roots (ultimate parents) of a given
- * (potential) shard resource. If the receiver is not ready to provide a complete
+ * Attempts to query URIs of resources that are roots (ultimate parents) of a given
+ * (potential) sub-unit resource. If the receiver is not ready to provide a complete
* and/or correct result, then fall back to an {@code alternate}, if any.
+ * Equivalent to calling {@link #getRoots(URI, boolean, ICrossReferenceIndex)} with
+ * a {@code true} value for the {@code shardOnly} parameter.
*
- * @param shardURI
- * the URI of a potential shard resource. It needs not necessarily actually
- * be a shard, in which case it trivially wouldn't have any parents
+ * @param subunitURI
+ * the URI of a potential sub-unit resource. It needs not necessarily actually
+ * be a sub-unit, in which case it trivially wouldn't have any parents
* @param alternate
* a fall-back index from which to get the roots if I am not ready
* to provide them, or {@code null} if not required
@@ -317,6 +413,54 @@ public interface ICrossReferenceIndex {
* if the index is not available and the {@code alternate} fails
* @throws IllegalArgumentException
* if the {@code alternate} is myself (attempted recursion)
+ *
+ * @see #getRoots(URI, boolean, ICrossReferenceIndex)
*/
- Set getRoots(URI shardURI, ICrossReferenceIndex alternate) throws CoreException;
+ Set getRoots(URI subunitURI, ICrossReferenceIndex alternate) throws CoreException;
+
+ /**
+ * Queries URIs of resources that are roots (ultimate parents) of a given
+ * resource.
+ *
+ * @param resourceURI
+ * the URI of a potential sub-unit resource. It needs not necessarily actually
+ * be a sub-unit, in which case it trivially wouldn't have any parents and,
+ * therefore, no roots
+ * @param shardOnly
+ * whether to consider only shards as validly having roots (useful for
+ * determining required resource dependencies)
+ * @return the URIs of resources that are roots of its parent graph
+ *
+ * @throws CoreException
+ * if the index either fails to compute the roots or if
+ * the calling thread is interrupted in waiting for the result
+ */
+ Set getRoots(URI resourceURI, boolean shardOnly) throws CoreException;
+
+ /**
+ * Attempts to query URIs of resources that are roots (ultimate parents) of a given
+ * (potential) sub-unit resource. If the receiver is not ready to provide a complete
+ * and/or correct result, then fall back to an {@code alternate}, if any.
+ *
+ * @param subunitURI
+ * the URI of a potential sub-unit resource. It needs not necessarily actually
+ * be a sub-unit, in which case it trivially wouldn't have any parents
+ * @param shardOnly
+ * whether to consider only shards as validly having roots (useful for
+ * determining required resource dependencies)
+ * @param alternate
+ * a fall-back index from which to get the roots if I am not ready
+ * to provide them, or {@code null} if not required
+ * @return the URIs of resources that are roots of its parent graph, or {@code null}
+ * if the receiver cannot provide a result and there is no {@code alternate}.
+ * Note that {@code null} is only returned in this failure case; any successful
+ * result is at least an empty set
+ *
+ * @throws CoreException
+ * if the index is not available and the {@code alternate} fails
+ * @throws IllegalArgumentException
+ * if the {@code alternate} is myself (attempted recursion)
+ */
+ Set getRoots(URI subunitURI, boolean shardOnly, ICrossReferenceIndex alternate) throws CoreException;
+
}
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ShardResourceHelper.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ShardResourceHelper.java
index 29c004eb5c6..ad172b55d5e 100644
--- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ShardResourceHelper.java
+++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/resource/ShardResourceHelper.java
@@ -213,6 +213,11 @@ public class ShardResourceHelper implements AutoCloseable {
resource.getContents(),
annotation);
}
+
+ result = new CommandWrapper(
+ "Toggle Submodel",
+ "Toggle the ability to open the resource as an independent sub-model unit",
+ result);
} else {
// Create the annotation
EAnnotation annotation = EcoreFactory.eINSTANCE.createEAnnotation();
@@ -252,7 +257,11 @@ public class ShardResourceHelper implements AutoCloseable {
// Ensure attachment of the adapter on first execution and record the
// annotation, if not already closed
- result = new CommandWrapper(result) {
+ result = new CommandWrapper(
+ "Toggle Submodel",
+ "Toggle the ability to open the resource as an independent sub-model unit",
+ result) {
+
@Override
public void execute() {
super.execute();
@@ -347,9 +356,7 @@ public class ShardResourceHelper implements AutoCloseable {
}
}
- if (result != null) {
- attachAnnotationAdapter(annotationOwner);
- }
+ attachAnnotationAdapter(annotationOwner);
}
return result;
@@ -412,7 +419,9 @@ public class ShardResourceHelper implements AutoCloseable {
if (annotationAdapter != null) {
Adapter adapter = annotationAdapter;
annotationAdapter = null;
- adapter.getTarget().eAdapters().remove(adapter);
+ if (adapter.getTarget() != null) {
+ adapter.getTarget().eAdapters().remove(adapter);
+ }
}
}
}
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/META-INF/MANIFEST.MF b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/META-INF/MANIFEST.MF
index 03a66ee9175..baca073f4d7 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/META-INF/MANIFEST.MF
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/META-INF/MANIFEST.MF
@@ -2,6 +2,8 @@ Manifest-Version: 1.0
Export-Package: org.eclipse.papyrus.infra.services.controlmode,
org.eclipse.papyrus.infra.services.controlmode.commands,
org.eclipse.papyrus.infra.services.controlmode.handler,
+ org.eclipse.papyrus.infra.services.controlmode.internal.handler;x-internal:=true,
+ org.eclipse.papyrus.infra.services.controlmode.internal.ui;x-internal:=true,
org.eclipse.papyrus.infra.services.controlmode.participants,
org.eclipse.papyrus.infra.services.controlmode.ui,
org.eclipse.papyrus.infra.services.controlmode.util;uses:="org.eclipse.emf.ecore"
@@ -15,7 +17,8 @@ Require-Bundle: org.eclipse.emf.edit.ui;bundle-version="[2.12.0,3.0.0)";visibili
org.eclipse.papyrus.infra.services.edit;bundle-version="[2.0.0,3.0.0)",
org.eclipse.papyrus.infra.core.sashwindows.di;bundle-version="[1.2.0,2.0.0)";visibility:=reexport,
org.eclipse.papyrus.infra.ui;bundle-version="[1.2.0,2.0.0)",
- org.eclipse.papyrus.infra.types.core;bundle-version="[3.0.0,4.0.0)"
+ org.eclipse.papyrus.infra.types.core;bundle-version="[3.0.0,4.0.0)",
+ org.eclipse.papyrus.infra.onefile;bundle-version="[2.0.0,3.0.0)"
Bundle-Vendor: %providerName
Bundle-ActivationPolicy: lazy
Bundle-Version: 1.4.0.qualifier
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/build.properties b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/build.properties
index 85f999f60cd..71872030c97 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/build.properties
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/build.properties
@@ -1,5 +1,15 @@
#
-#Mon Sep 12 09:29:37 CEST 2011
+# Copyright (c) 2009, 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:
+# Atos Origin - Initial API and implementation
+# Christian W. Damus - bug 497865
+#
bin.includes = META-INF/,\
.,\
plugin.xml,\
@@ -8,6 +18,7 @@ bin.includes = META-INF/,\
about.html,\
icons/,\
model/
+bin.excludes = icons/**/*.pxm
output..=bin/
src.includes = about.html
source..=src/
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.png b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.png
new file mode 100644
index 00000000000..f218590a367
Binary files /dev/null and b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.png differ
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.pxm b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.pxm
new file mode 100644
index 00000000000..f650eb05258
Binary files /dev/null and b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco.pxm differ
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco@2x.png b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco@2x.png
new file mode 100644
index 00000000000..6f554582eb2
Binary files /dev/null and b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/shard_deco@2x.png differ
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.png b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.png
new file mode 100644
index 00000000000..7589ba96994
Binary files /dev/null and b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.png differ
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.pxm b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.pxm
new file mode 100644
index 00000000000..a10adfda411
Binary files /dev/null and b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco.pxm differ
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco@2x.png b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco@2x.png
new file mode 100644
index 00000000000..8715e4e0f3d
Binary files /dev/null and b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/icons/full/ovr16/submodel_deco@2x.png differ
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.properties b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.properties
index db95d842854..7ccda077a6d 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.properties
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.properties
@@ -1,12 +1,32 @@
-#/*****************************************************************************
-# * Copyright (c) 2009 Atos Origin.
-# *
-# *
-# * All rights reserved. This program and the accompanying materials
-# * are made available under the terms of the Eclipse Public License v1.0
-# * which accompanies this distribution, and is available at
-# * http://www.eclipse.org/legal/epl-v10.html
-# *
-# *****************************************************************************/
+#
+# Copyright (c) 2009, 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:
+# Atos Origin - Initial API and implementation
+# Christian W. Damus - bug 497865
+#
pluginName=Papyrus Control Mode
providerName=Eclipse Modeling Project
+
+cmd.control.desc = Move this submodel into its own resource (file)
+cmd.control.name = Create submodel unit
+cmd.control.showDlg.name = Show control mode dialog
+cmd.control.resource.name = Resource Location
+cmd.uncontrol.desc = Reintegrate this submodel into the resource (file) of the parent model
+cmd.uncontrol.name = Reintegrate submodel unit into parent model
+cmd.control.label = Create Submodel
+cmd.control.tip = Move this submodel into its own resource (file)
+cmd.uncontrol.label = Reintegrate Submodel
+cmd.uncontrol.tip = Reintegrate this submodel into the resource (file) of the parent model
+cmd.submodel.desc = Change whether the controlled unit is an independent sub-model unit
+cmd.submodel.name = Toggle Submodel Unit
+cmd.submodel.label = Independent Submodel
+cmd.submodel.tip = Change whether the controlled unit is an independent sub-model unit
+decorator.controlmode.label = Controlled Units
+decorator.controlmode.desc = Decorates controlled unit resources in the Project Explorer.
+extpt.participant.name = Control-mode Participants
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.xml b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.xml
index f1264b55313..78725c65d9a 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.xml
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/plugin.xml
@@ -1,7 +1,7 @@
-
+
+ name="%cmd.control.name">
@@ -56,7 +56,7 @@
@@ -65,9 +65,19 @@
+ name="%cmd.uncontrol.name">
+
+
+
+
+ tooltip="%cmd.control.tip">
+ tooltip="%cmd.uncontrol.tip">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %decorator.controlmode.desc
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/schema/participant.exsd b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/schema/participant.exsd
index 58c31156260..ad0fe87029c 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/schema/participant.exsd
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/schema/participant.exsd
@@ -1,125 +1,128 @@
-
-
-
-
-
-
-
-
- This extension point is used to register participant to compute the control command.
-WARNING : The attribute ID is not being used for now
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Participant to the control command. This participant should implements either IControlCommandParticipant or IUncontrolCommandParticipant or both
-
-
-
-
-
-
- Class which implements the participant. This participant should implements either IControlCommandParticipant or IUncontrolCommandParticipant or both
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 0.8.2
-
-
-
-
-
-
-
-
- <extension
- point="org.eclipse.papyrus.infra.services.controlmode.participant">
- <participant
- class="org.eclipse.papyrus.infra.services.controlmode.participants.NotationControlModeParticipant">
- </participant>
-</extension>
-
-
-
-
-
-
-
-
- [Enter API information here.]
-
-
-
-
-
-
-
-
- [Enter information about supplied implementation of this extension point.]
-
-
-
-
-
-
-
-
- License
-
-The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available at http://www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" will mean the Content.
-
-If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org.
-
-
-
-
+
+
+
+
+
+
+
+
+ This extension point is used to register participant to compute the control command.
+WARNING : The attribute ID is not being used for now
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Registration of a participant in the construction of control commands.
+
+
+
+
+
+
+ Class that implements the participant. This participant should implements either IControlCommandParticipant or IUncontrolCommandParticipant or both.
+
+As of the 1.3 release, another participant interface may be
+implemented for validation of a control mode request: IControlCommandApprover.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.8.2
+
+
+
+
+
+
+
+
+ <extension
+ point="org.eclipse.papyrus.infra.services.controlmode.participant">
+ <participant
+ class="org.eclipse.papyrus.infra.services.controlmode.participants.NotationControlModeParticipant">
+ </participant>
+</extension>
+
+
+
+
+
+
+
+
+ [Enter API information here.]
+
+
+
+
+
+
+
+
+ [Enter information about supplied implementation of this extension point.]
+
+
+
+
+
+
+
+
+ Copyright (c) 2013, 2016 ATOS Origin, 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
+
+
+
+
diff --git a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/ControlModeManager.java b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/ControlModeManager.java
index c24a70c8588..1fd54c7ffc4 100644
--- a/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/ControlModeManager.java
+++ b/plugins/infra/services/org.eclipse.papyrus.infra.services.controlmode/src/org/eclipse/papyrus/infra/services/controlmode/ControlModeManager.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013 Atos.
+ * Copyright (c) 2013, 2016 Atos, 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
@@ -7,19 +7,27 @@
*
* Contributors:
* Arthur Daussy - initial API and implementation
+ * Christian W. Damus - bug 497865
******************************************************************************/
package org.eclipse.papyrus.infra.services.controlmode;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.core.command.ICompositeCommand;
@@ -30,8 +38,10 @@ import org.eclipse.papyrus.infra.services.controlmode.commands.BasicUncontrolCom
import org.eclipse.papyrus.infra.services.controlmode.commands.CreateControlResource;
import org.eclipse.papyrus.infra.services.controlmode.commands.RemoveControlResourceCommand;
import org.eclipse.papyrus.infra.services.controlmode.messages.Messages;
+import org.eclipse.papyrus.infra.services.controlmode.participants.IControlCommandApprover;
import org.eclipse.papyrus.infra.services.controlmode.participants.IControlCommandParticipant;
import org.eclipse.papyrus.infra.services.controlmode.participants.IControlModeParticipant;
+import org.eclipse.papyrus.infra.services.controlmode.participants.IShardModeCommandParticipant;
import org.eclipse.papyrus.infra.services.controlmode.participants.IUncontrolCommandParticipant;
/**
@@ -66,6 +76,7 @@ public class ControlModeManager implements IControlModeManager {
*/
protected final class PartipantComparator implements Comparator {
+ @Override
public int compare(IControlModeParticipant arg0, IControlModeParticipant arg1) {
int i = arg1.getPriority();
int j = arg0.getPriority();
@@ -95,21 +106,35 @@ public class ControlModeManager implements IControlModeManager {
protected static String PARTICPANT_ATTRIBUTE = "class"; //$NON-NLS-1$
/**
- * @return the unique instance of the manager
+ * Hold all the {@link IControlCommandParticipant}
*/
- public static IControlModeManager getInstance() {
- return SingletonHolder.INSTANCE;
- }
+ protected ArrayList controlCommandParticipants = new ArrayList<>();
/**
- * Hold all the {@link IControlCommandParticipant}
+ * Hold all the {@link IUncontrolCommandParticipant}
*/
- protected ArrayList controlCommandParticipants = new ArrayList();
+ protected ArrayList uncontrolCommandParticipants = new ArrayList<>();
/**
- * Hold all the {@link IUncontrolCommandParticipant}
+ * Hold all the {@link IShardModeCommandParticipant}s.
+ *
+ * @since 1.3
*/
- protected ArrayList uncontrolCommandParticipants = new ArrayList();
+ protected List shardModeCommandParticipants = new ArrayList<>();
+
+ /**
+ * Hold all the {@link IControlCommandApprover}s.
+ *
+ * @since 1.3
+ */
+ protected List controlCommandApprovers = new ArrayList<>();
+
+ /**
+ * @return the unique instance of the manager
+ */
+ public static IControlModeManager getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
/**
*
@@ -118,6 +143,7 @@ public class ControlModeManager implements IControlModeManager {
initParticipants();
}
+ @Override
public ICommand getControlCommand(ControlModeRequest request) {
boolean isOK = verifCorrectCommand(request);
if (!isOK) {
@@ -169,7 +195,7 @@ public class ControlModeManager implements IControlModeManager {
*/
protected ICompositeCommand getPostCommand(ControlModeRequest request) {
boolean isControlRequest = request.isControlRequest();
- CompositeTransactionalCommand cc = new CompositeTransactionalCommand(request.getEditingDomain(), isControlRequest ? CONTROL_COMMAND_POST_COMMANDS : UNCONTROL_COMMAND_POST_COMMANDS);//////$NON-NLS-1$ //$NON-NLS-2$
+ CompositeTransactionalCommand cc = new CompositeTransactionalCommand(request.getEditingDomain(), isControlRequest ? CONTROL_COMMAND_POST_COMMANDS : UNCONTROL_COMMAND_POST_COMMANDS);////// $NON-NLS-1$ //$NON-NLS-2$
if (isControlRequest) {
getPostControlCommand(request, cc);
} else {
@@ -275,6 +301,7 @@ public class ControlModeManager implements IControlModeManager {
}
}
+ @Override
public ICommand getUncontrolCommand(ControlModeRequest request) {
boolean isOK = verifCorrectCommand(request);
if (!isOK) {
@@ -301,6 +328,122 @@ public class ControlModeManager implements IControlModeManager {
return uncontrolCommandParticipants;
}
+ /**
+ * @since 1.3
+ */
+ protected List getShardModeCommandParticipants() {
+ return shardModeCommandParticipants;
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Override
+ public ICommand getShardModeCommand(ControlModeRequest request) {
+ // This does validation of the request, too
+ ICommand baseCommand = IControlModeManager.super.getShardModeCommand(request);
+
+ // Include participants
+
+ ICommand result = new CompositeTransactionalCommand(request.getEditingDomain(), Messages.getString("ControlModeManager.changeControlModeCommand.label")); //$NON-NLS-1$
+ ICommand preCommand = getPreShardModeCommand(request);
+ if (preCommand != null) {
+ result = result.compose(preCommand);
+ }
+
+ result = result.compose(baseCommand);
+
+ ICommand postCommand = getPostShardModeCommand(request);
+ if (postCommand != null) {
+ result = result.compose(postCommand);
+ }
+
+ return result.reduce();
+ }
+
+ /**
+ * @since 1.3
+ */
+ protected ICommand getPreShardModeCommand(ControlModeRequest request) {
+ return getParticipantCommand(request,
+ getShardModeCommandParticipants(),
+ IShardModeCommandParticipant::providesShardModeCommand,
+ IShardModeCommandParticipant::getPreShardModeCommand);
+ }
+
+ /**
+ * @since 1.3
+ */
+ protected ICommand getPostShardModeCommand(ControlModeRequest request) {
+ return getParticipantCommand(request,
+ getShardModeCommandParticipants(),
+ IShardModeCommandParticipant::providesShardModeCommand,
+ IShardModeCommandParticipant::getPostShardModeCommand);
+ }
+
+ private