Generic OpenDiagramFromComponentAdapter 71/1871/4
authorjsimomaa <jani.simomaa@gmail.com>
Thu, 21 Jun 2018 06:46:10 +0000 (09:46 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 21 Jun 2018 08:03:56 +0000 (08:03 +0000)
gitlab #30

Change-Id: Ib17df6e2a29642aa2e98976e4ad8df0cafd4e72e

bundles/org.simantics.modeling.ui/plugin.xml
bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/OpenDiagramFromComponentAdapter.java [new file with mode: 0644]

index 578c752ba37504fe7e6b46024d17081cdeb3829a..039fed00abfe428941bbec2434e2aad109b1e487 100644 (file)
                id="org.simantics.modeling.ui.context">
          </inContext>-->
       </adapterClass>
+      <adapterClass
+            groupId="org.simantics.diagramEditor.group"
+            priority="50"
+            class="org.simantics.modeling.ui.diagramEditor.OpenDiagramFromComponentAdapter">
+         <!--<inContext
+               id="org.simantics.modeling.ui.context">
+         </inContext>-->
+      </adapterClass>
       <adapterClass
             priority="150"
             class="org.simantics.modeling.ui.diagramEditor.OpenSheetAdapter">
diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/OpenDiagramFromComponentAdapter.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/OpenDiagramFromComponentAdapter.java
new file mode 100644 (file)
index 0000000..e015796
--- /dev/null
@@ -0,0 +1,372 @@
+package org.simantics.modeling.ui.diagramEditor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.simantics.Simantics;
+import org.simantics.databoard.util.URIStringUtils;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.NamedResource;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.RVI;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.flag.FlagUtil;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.DiagramHints;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ComponentUtils;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.modeling.actions.NavigateToTarget;
+import org.simantics.modeling.actions.NavigationTargetChooserDialog;
+import org.simantics.modeling.ui.Activator;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.ui.utils.ResourceAdaptionUtils;
+import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;
+import org.simantics.utils.datastructures.MapSet;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.strings.AlphanumComparator;
+import org.simantics.utils.strings.EString;
+import org.simantics.utils.threads.SWTThread;
+import org.simantics.utils.threads.ThreadUtils;
+import org.simantics.utils.ui.AdaptionUtils;
+import org.simantics.utils.ui.workbench.WorkbenchUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class OpenDiagramFromComponentAdapter extends AbstractResourceEditorAdapter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(OpenDiagramFromComponentAdapter.class);
+
+    private static final String EDITOR_ID = "org.simantics.modeling.ui.diagramEditor";
+
+    public OpenDiagramFromComponentAdapter() {
+        super("Open Diagram Containing This Component", Activator.SYMBOL_ICON);
+    }
+
+    @Override
+    public boolean canHandle(ReadGraph graph, Object input) throws DatabaseException {
+        Pair<Resource, String> p = tryGetResource(graph, input);
+        if (p == null)
+            return false;
+        Variable v = AdaptionUtils.adaptToSingle(input, Variable.class);
+        Collection<Runnable> rs = tryFindDiagram(graph, p.first, v, p.second);
+        return !rs.isEmpty();
+    }
+
+    private Pair<Resource, String> tryGetResource(ReadGraph graph, Object input) throws DatabaseException {
+        Resource r = ResourceAdaptionUtils.toSingleResource(input);
+        if (r != null)
+            return Pair.make(r, "");
+        Variable v = AdaptionUtils.adaptToSingle(input, Variable.class);
+        return findResource(graph, v);
+    }
+
+    private Pair<Resource, String> findResource(ReadGraph graph, Variable v) throws DatabaseException {
+        List<String> path = null;
+        while (v != null) {
+            Resource r = v.getPossibleRepresents(graph);
+            if (r != null) {
+                String rvi = "";
+                if (path != null) {
+                    int pathLength = path.size();
+                    for (int i = 0; i < pathLength; i++)
+                        path.set(i, URIStringUtils.escape(path.get(i)));
+                    Collections.reverse(path);
+                    rvi = EString.implode(path, "/");
+                }
+                return Pair.make(r, rvi);
+            }
+            if (path == null)
+                path = new ArrayList<>(2);
+            path.add( v.getName(graph) );
+            v = v.browsePossible(graph, ".");
+        }
+        return null;
+    }
+
+    @Override
+    public void openEditor(final Object input) throws Exception {
+        final Display d = Display.getCurrent();
+        if (d == null)
+            return;
+
+        Simantics.getSession().syncRequest(new ReadRequest() {
+            @Override
+            public void run(ReadGraph graph) throws DatabaseException {
+                Pair<Resource, String> r = tryGetResource(graph, input);
+                if (r == null)
+                    return;
+
+                Variable v = AdaptionUtils.adaptToSingle(input, Variable.class);
+
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug(getClass().getSimpleName() + ".openEditor: input's nearest parent resource URI: " + NameUtils.getURIOrSafeNameInternal(graph, r.first));
+                    LOGGER.debug(getClass().getSimpleName() + ".openEditor: input's nearest parent RVI: " + r.second);
+                    LOGGER.debug(getClass().getSimpleName() + ".openEditor: input variable URI: " + (v != null ? v.getURI(graph) : "null"));
+                }
+
+                final Collection<Runnable> rs = tryFindDiagram(graph, r.first, v, r.second);
+                if (rs.isEmpty())
+                    return;
+
+                SWTThread.getThreadAccess(d).asyncExec(() -> rs.forEach(Runnable::run));
+            }
+        });
+    }
+
+    private Collection<Runnable> tryFindDiagram(ReadGraph g, Resource component, Variable variable, String rviFromComponent) throws DatabaseException {
+        try {
+            return findDiagram(g, component, variable, rviFromComponent);
+        } catch (DatabaseException e) {
+            return Collections.emptyList();
+        }
+    }
+
+    private Collection<Runnable> findDiagram(ReadGraph g, Resource component, Variable variable, String rviFromComponent) throws DatabaseException {
+        Layer0 l0 = Layer0.getInstance(g);
+        StructuralResource2 STR = StructuralResource2.getInstance(g);
+        ModelingResources MOD = ModelingResources.getInstance(g);
+
+        if (g.isInstanceOf(component, STR.Component)) {
+            Collection<Runnable> result = new ArrayList<>(1);
+
+            Resource composite = g.getSingleObject(component, l0.PartOf);
+            Resource diagram = ComponentUtils.getPossibleCompositeDiagram(g, composite);
+
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug(getClass().getSimpleName() + ".findDiagram: component: " + NameUtils.getURIOrSafeNameInternal(g, component));
+                LOGGER.debug(getClass().getSimpleName() + ".findDiagram: composite: " + NameUtils.getURIOrSafeNameInternal(g, composite));
+            }
+
+            Collection<Resource> referenceElements = diagram == null ? g.getObjects(component, MOD.HasParentComponent_Inverse) : Collections.<Resource>emptyList();
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug(getClass().getSimpleName() + ".findDiagram: diagram: " + NameUtils.getURIOrSafeNameInternal(g, diagram));
+                LOGGER.debug(getClass().getSimpleName() + ".findDiagram: referenceElements: " + referenceElements.size());
+                for (Object object : referenceElements)
+                    LOGGER.debug("\t" + NameUtils.getURIOrSafeNameInternal(g, (Resource) object));
+            }
+            if (diagram == null && referenceElements.isEmpty())
+                return Collections.emptyList();
+
+            Variable compositeVariable = Variables.getPossibleVariable(g, composite);
+            if (compositeVariable == null)
+                return Collections.emptyList();
+            final Resource indexRoot = Variables.getPossibleIndexRoot(g, compositeVariable);
+            if (indexRoot == null)
+                return Collections.emptyList();
+            if (LOGGER.isDebugEnabled())
+                LOGGER.debug(getClass().getSimpleName() + ".findDiagram: Model: " + indexRoot);
+
+            if (diagram != null) {
+                if(OpenDiagramFromConfigurationAdapter.isLocked(g, diagram))
+                    return Collections.emptyList();
+
+                RVI rvi = null;
+                boolean allowNullRvi = false;
+                if (variable != null) {
+                    // Get proper RVI from variable if it exists.
+                    Variable context = Variables.getPossibleContext(g, variable);
+                    if (context != null) {
+                        // We want the composite's RVI, not the component in it.
+                        Variable parent = findFirstParentComposite(g, variable);
+                        if (parent != null) {
+                            rvi = parent.getPossibleRVI(g);
+                            if (LOGGER.isDebugEnabled())
+                                LOGGER.debug(getClass().getSimpleName() + ".findDiagram: resolved RVI: " + rvi);
+                        }
+                    }
+                } else {
+                    allowNullRvi = true;
+                    rvi = compositeVariable.getPossibleRVI(g);
+                    if (LOGGER.isDebugEnabled())
+                        LOGGER.debug(getClass().getSimpleName() + ".findDiagram: resolved RVI from resource path: " + rvi);
+                }
+                if (rvi == null && !allowNullRvi)
+                    return Collections.emptyList();
+
+                Collection<Object> selectedObjects = findElementObjects(g, component, rviFromComponent);
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug(getClass().getSimpleName() + ".findDiagram: selected objects: " + selectedObjects.size());
+                    for (Object object : selectedObjects)
+                        LOGGER.debug("\t" + NameUtils.getURIOrSafeNameInternal(g, (Resource) object));
+                }
+                // Prevent diagram from opening if there's nothing to select
+                // on the diagram based on the received input.
+                if (!selectedObjects.isEmpty())
+                    result.add( NavigateToTarget.editorActivator(EDITOR_ID, diagram, indexRoot, rvi, editorActivationCallback(selectedObjects)) );
+            } else {
+                final MapSet<NamedResource, Resource> referencingDiagrams = listReferenceDiagrams(g, referenceElements);
+                final Set<NamedResource> diagrams = referencingDiagrams.getKeys();
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug(getClass().getSimpleName() + ".findDiagram: selected objects: " + diagrams.size());
+                    for (NamedResource d : diagrams) {
+                        LOGGER.debug("\t" + NameUtils.getURIOrSafeNameInternal(g, d.getResource()) + ":");
+                        for (Resource referenceElement : referencingDiagrams.getValues(d)) {
+                            LOGGER.debug("\t\t" + NameUtils.getURIOrSafeNameInternal(g, referenceElement));
+                        }
+                    }
+                }
+                switch (diagrams.size()) {
+                case 0:
+                    // Prevent diagram from opening if there's nothing to select
+                    // on the diagram based on the received input.
+                    break;
+
+                case 1:
+                    // Open the one diagram straight away.
+                    NamedResource singleDiagram = diagrams.iterator().next();
+                    RVI rvi = getDiagramCompositeRvi(g, singleDiagram.getResource());
+                    if (rvi != null) {
+                        Collection<Resource> selectedObjects = referencingDiagrams.getValues(singleDiagram);
+                        result.add( NavigateToTarget.editorActivator(EDITOR_ID, singleDiagram.getResource(), indexRoot, rvi, editorActivationCallback(selectedObjects)) );
+                    }
+                    break;
+
+                default:
+                    final Map<NamedResource, RVI> diagramToRvi = new TreeMap<>(COMPARATOR);
+                    for (NamedResource d : diagrams) {
+                        RVI rvi2 = getDiagramCompositeRvi(g, d.getResource());
+                        if (rvi2 != null)
+                            diagramToRvi.put(d, rvi2);
+                    }
+                    result.add(() -> {
+                        NamedResource selected = queryTarget(WorkbenchUtils.getActiveWorkbenchWindowShell(), diagramToRvi.keySet());
+                        if (selected != null) {
+                            Collection<Resource> selectedObjects = referencingDiagrams.getValues(selected);
+                            RVI drvi = diagramToRvi.get(selected);
+                            NavigateToTarget.editorActivator(EDITOR_ID, selected.getResource(), indexRoot, drvi, editorActivationCallback(selectedObjects)).run();
+                        }
+                    });
+                    break;
+                }
+            }
+            return result;
+        }
+
+        // Nothing to open
+        return Collections.emptyList();
+    }
+
+    private RVI getDiagramCompositeRvi(ReadGraph graph, Resource diagram) throws DatabaseException {
+        ModelingResources MOD = ModelingResources.getInstance(graph);
+        Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
+        if (composite == null)
+            return null;
+        Variable v = Variables.getPossibleVariable(graph, composite);
+        return v != null ? v.getPossibleRVI(graph) : null;
+    }
+
+    private Consumer<IEditorPart> editorActivationCallback(final Collection<? extends Object> selectedObjects) {
+        return part -> {
+            final ICanvasContext openedCanvas = (ICanvasContext) part.getAdapter(ICanvasContext.class);
+            assert openedCanvas != null;
+            // CanvasContext-wide denial of initial zoom-to-fit on diagram open.
+            openedCanvas.getDefaultHintContext().setHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT, Boolean.FALSE);
+            ThreadUtils.asyncExec(openedCanvas.getThreadAccess(),
+                    NavigateToTarget.elementSelectorZoomer(openedCanvas, selectedObjects, false));
+        };
+    }
+
+    private Variable findFirstParentComposite(ReadGraph graph, Variable v) throws DatabaseException {
+        Variable first = findFirstWithRepresentation(graph, v);
+        if (first == null)
+            return null;
+        Variable parent = first.getParent(graph);
+        return parent;
+    }
+
+    private Variable findFirstWithRepresentation(ReadGraph graph, Variable v) throws DatabaseException {
+        while (v != null) {
+            Resource represents = v.getPossibleRepresents(graph);
+            if (LOGGER.isDebugEnabled())
+                LOGGER.debug(v.getURI(graph) + " -> " + NameUtils.getURIOrSafeNameInternal(graph, represents));
+            if (represents != null)
+                return v;
+            v = v.getParent(graph);
+        }
+        return null;
+    }
+
+    public static Collection<Object> findElementObjects(ReadGraph g, Resource component, String rviFromComponent) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        ModelingResources MOD = ModelingResources.getInstance(g);
+        final Collection<Object> selectedObjects = new ArrayList<>(4);
+        if (rviFromComponent.isEmpty()) {
+            // The selected objects are configuration objects
+            for (Resource element : g.getObjects(component, MOD.ComponentToElement)) {
+                if (g.isInstanceOf(element, DIA.Flag) && FlagUtil.isExternal(g, element)) {
+                    // Use external flag primarily if one exists in the correspondences
+                    selectedObjects.clear();
+                    selectedObjects.add(element);
+                    break;
+                } else if (g.isInstanceOf(element, DIA.RouteGraphConnection)) {
+                    selectedObjects.add(element);
+                } else if (g.isInstanceOf(element, DIA.Connection)) {
+                    // Okay, we need to find a part of the connection
+                    ConnectionUtil cu = new ConnectionUtil(g);
+                    cu.gatherConnectionParts(element, selectedObjects);
+                } else {
+                    selectedObjects.add(element);
+                }
+            }
+        }
+        return selectedObjects;
+    }
+
+    protected MapSet<NamedResource, Resource> listReferenceDiagrams(ReadGraph graph, Collection<Resource> referenceElements) throws DatabaseException {
+        ModelingResources MOD = ModelingResources.getInstance(graph);
+
+        // Make result diagram ordering stable and logical by using our own comparator.
+        MapSet<NamedResource, Resource> diagrams = new MapSet.Tree<>(COMPARATOR);
+
+        for (Resource referenceElement : referenceElements) {
+            final Resource diagram = NavigateToTarget.getOwnerList(graph, referenceElement);
+            if (diagram == null)
+                continue;
+            Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
+            if (composite == null)
+                continue;
+            Variable v = Variables.getPossibleVariable(graph, composite);
+            if (v == null)
+                continue;
+
+            String rvi = URIStringUtils.unescape( Variables.getRVI(graph, v) );
+
+            diagrams.add(new NamedResource(rvi, diagram), referenceElement);
+        }
+
+        return diagrams;
+    }
+
+    private static final Comparator<? super NamedResource> COMPARATOR =
+            (o1, o2) -> AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getName(), o2.getName());
+
+    protected NamedResource queryTarget(final Shell parentShell, Collection<NamedResource> options) {
+        NavigationTargetChooserDialog dialog = new NavigationTargetChooserDialog(
+                parentShell, options.toArray(new NamedResource[options.size()]),
+                "Choose Diagram with Component Reference",
+                "Select single diagram from list");
+        return dialog.open() != Window.OK ? null : dialog.getSelection();
+    }
+
+}