diff options
author | Christian W. Damus | 2016-08-23 21:07:27 +0000 |
---|---|---|
committer | Christian W. Damus | 2016-08-23 21:07:27 +0000 |
commit | ae293862c718921e47a62e456d48f3c19cfbe78e (patch) | |
tree | fe95bc4745b4db378f7caecebf754c62454df2da /plugins/infra/emf | |
parent | 7e0c2b842c458bd621914b68da67ce97ee6fed21 (diff) | |
download | org.eclipse.papyrus-ae293862c718921e47a62e456d48f3c19cfbe78e.tar.gz org.eclipse.papyrus-ae293862c718921e47a62e456d48f3c19cfbe78e.tar.xz org.eclipse.papyrus-ae293862c718921e47a62e456d48f3c19cfbe78e.zip |
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
Diffstat (limited to 'plugins/infra/emf')
6 files changed, 452 insertions, 96 deletions
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<URI, URI> outgoingReferences = HashMultimap.create(); final SetMultimap<URI, URI> incomingReferences = HashMultimap.create(); - final SetMultimap<URI, URI> resourceToShards = HashMultimap.create(); - final SetMultimap<URI, URI> shardToParents = HashMultimap.create(); + final SetMultimap<URI, URI> resourceToSubunits = HashMultimap.create(); + final SetMultimap<URI, URI> subunitToParents = HashMultimap.create(); // These are abstracted as URIs without extension SetMultimap<URI, URI> aggregateOutgoingReferences; SetMultimap<URI, URI> aggregateIncomingReferences; - SetMultimap<URI, URI> aggregateResourceToShards; - SetMultimap<URI, URI> aggregateShardToParents; + SetMultimap<URI, URI> aggregateResourceToSubunits; + SetMultimap<URI, URI> aggregateSubunitToParents; final SetMultimap<URI, String> shards = HashMultimap.create(); /** @@ -219,36 +220,53 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde } @Override - public ListenableFuture<SetMultimap<URI, URI>> getShardsAsync() { - return afterIndex(getShardsCallable()); + public ListenableFuture<SetMultimap<URI, URI>> getSubunitsAsync() { + return afterIndex(getSubunitsCallable()); } @Override - public SetMultimap<URI, URI> getShards() throws CoreException { - return sync(afterIndex(getShardsCallable())); + public SetMultimap<URI, URI> getSubunits() throws CoreException { + return sync(afterIndex(getSubunitsCallable())); } - Callable<SetMultimap<URI, URI>> getShardsCallable() { - return sync(() -> ImmutableSetMultimap.copyOf(resourceToShards)); + Callable<SetMultimap<URI, URI>> getSubunitsCallable() { + return sync(() -> ImmutableSetMultimap.copyOf(resourceToSubunits)); } @Override - public ListenableFuture<Set<URI>> getShardsAsync(URI resourceURI) { - return afterIndex(getShardsCallable(resourceURI)); + public ListenableFuture<Set<URI>> getSubunitsAsync(URI resourceURI) { + return getSubunitsAsync(resourceURI, true); } @Override - public Set<URI> getShards(URI resourceURI) throws CoreException { - return sync(afterIndex(getShardsCallable(resourceURI))); + public Set<URI> getSubunits(URI resourceURI) throws CoreException { + return getSubunits(resourceURI, true); } - Callable<Set<URI>> getShardsCallable(URI shardURI) { + @Override + public ListenableFuture<Set<URI>> getSubunitsAsync(URI resourceURI, boolean shardOnly) { + return afterIndex(getSubunitsCallable(resourceURI, shardOnly)); + } + + @Override + public Set<URI> getSubunits(URI resourceURI, boolean shardOnly) throws CoreException { + return sync(afterIndex(getSubunitsCallable(resourceURI, shardOnly))); + } + + Callable<Set<URI>> getSubunitsCallable(URI shardURI, boolean shardOnly) { return sync(() -> { String ext = shardURI.fileExtension(); URI withoutExt = shardURI.trimFileExtension(); - Set<URI> result = getAggregateShards().get(withoutExt).stream() - // Only those that actually are shards - .filter(AbstractCrossReferenceIndex.this::isShard0) + + Stream<URI> intermediateResult = getAggregateShards().get(withoutExt).stream(); + + if (shardOnly) { + // Only those that actually are shards + intermediateResult = intermediateResult + .filter(AbstractCrossReferenceIndex.this::isShard0); + } + + Set<URI> result = intermediateResult .map(uri -> uri.appendFileExtension(ext)) .collect(Collectors.toSet()); @@ -260,16 +278,16 @@ public abstract class AbstractCrossReferenceIndex implements ICrossReferenceInde SetMultimap<URI, URI> result; synchronized (sync) { - if (aggregateResourceToShards == null) { + if (aggregateResourceToSubunits == null) { // Compute the aggregate now - aggregateResourceToShards = HashMultimap.create(); - for (Map.Entry<URI, URI> next : resourceToShards.entries()) { - aggregateResourceToShards.put(next.getKey().trimFileExtension(), + aggregateResourceToSubunits = HashMultimap.create(); + for (Map.Entry<URI, URI> 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<Set<URI>> getParentsAsync(URI shardURI) { - return afterIndex(getParentsCallable(shardURI)); + return getParentsAsync(shardURI, true); + } + + @Override + public ListenableFuture<Set<URI>> getParentsAsync(URI resourceURI, boolean shardOnly) { + return afterIndex(getParentsCallable(resourceURI, shardOnly)); } @Override public Set<URI> getParents(URI shardURI) throws CoreException { - return sync(afterIndex(getParentsCallable(shardURI))); + return getParents(shardURI, true); } - Callable<Set<URI>> getParentsCallable(URI shardURI) { + @Override + public Set<URI> getParents(URI resourceURI, boolean shardOnly) throws CoreException { + return sync(afterIndex(getParentsCallable(resourceURI, shardOnly))); + } + + Callable<Set<URI>> getParentsCallable(URI shardURI, boolean shardOnly) { return sync(() -> { Set<URI> 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<URI, URI> result; synchronized (sync) { - if (aggregateShardToParents == null) { + if (aggregateSubunitToParents == null) { // Compute the aggregate now - aggregateShardToParents = HashMultimap.create(); - for (Map.Entry<URI, URI> next : shardToParents.entries()) { - aggregateShardToParents.put(next.getKey().trimFileExtension(), + aggregateSubunitToParents = HashMultimap.create(); + for (Map.Entry<URI, URI> 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<Set<URI>> getRootsAsync(URI shardURI) { - return afterIndex(getRootsCallable(shardURI)); + return getRootsAsync(shardURI, true); + } + + @Override + public ListenableFuture<Set<URI>> getRootsAsync(URI shardURI, boolean shardOnly) { + return afterIndex(getRootsCallable(shardURI, shardOnly)); } @Override public Set<URI> getRoots(URI shardURI) throws CoreException { - return sync(afterIndex(getRootsCallable(shardURI))); + return getRoots(shardURI, true); } @Override - public Set<URI> getRoots(URI shardURI, ICrossReferenceIndex alternate) throws CoreException { + public Set<URI> getRoots(URI shardURI, boolean shardOnly) throws CoreException { + return sync(afterIndex(getRootsCallable(shardURI, shardOnly))); + } + + @Override + public Set<URI> getRoots(URI subunitURI, ICrossReferenceIndex alternate) throws CoreException { + return getRoots(subunitURI, true, alternate); + } + + @Override + public Set<URI> getRoots(URI subunitURI, boolean shardOnly, ICrossReferenceIndex alternate) throws CoreException { if (alternate == this) { throw new IllegalArgumentException("self alternate"); //$NON-NLS-1$ } Callable<Set<URI>> elseCallable = (alternate == null) ? null - : () -> alternate.getRoots(shardURI); + : () -> alternate.getRoots(subunitURI, shardOnly); - return ifAvailable(getRootsCallable(shardURI), elseCallable); + return ifAvailable(getRootsCallable(subunitURI, shardOnly), elseCallable); } - Callable<Set<URI>> getRootsCallable(URI shardURI) { + Callable<Set<URI>> getRootsCallable(URI subunitURI, boolean shardOnly) { return sync(() -> { Set<URI> 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<URI> resultBuilder = ImmutableSet.builder(); - SetMultimap<URI, URI> shardToParents = getAggregateShardToParents(); + SetMultimap<URI, URI> subunitToParents = getAggregateShardToParents(); // Breadth-first search of the parent graph Queue<URI> queue = Lists.newLinkedList(); Set<URI> 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<Dispatcher> listeners = new CopyOnWriteArrayList<>(); + private final WorkspaceModelIndex<CrossReferencedFile> 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<String> crossReferences = Sets.newHashSet(); private XMIElement shard; - private Set<String> shards = Sets.newHashSet(); + private Set<String> subunits = Sets.newHashSet(); // The (optional) parent references in the annotation private Set<String> parents = Sets.newHashSet(); @@ -93,8 +93,8 @@ public class CrossReferenceIndexHandler extends DefaultHandler { return shard != null; } - public Set<String> getShards() { - return shards; + public Set<String> getSubunits() { + return subunits; } public Set<String> 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<SetMultimap<URI, URI>> getShardsCallable() { + Callable<SetMultimap<URI, URI>> 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<URI>) 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<URI> 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<SetMultimap<URI, URI>> getShardsAsync(); + ListenableFuture<SetMultimap<URI, URI>> 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<URI, URI> getShards() throws CoreException; + SetMultimap<URI, URI> 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<Set<URI>> 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<Set<URI>> 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<Set<URI>> getShardsAsync(URI resourceURI); + Set<URI> 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<URI> getShards(URI resourceURI) throws CoreException; + Set<URI> 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<Set<URI>> 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<Set<URI>> 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<URI> 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<URI> 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<Set<URI>> 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<Set<URI>> 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<URI> 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<URI> getRoots(URI shardURI, ICrossReferenceIndex alternate) throws CoreException; + Set<URI> 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<URI> 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<URI> 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); + } } } } |