1 package org.simantics.modeling.ui.diagramEditor;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
10 import java.util.TreeMap;
11 import java.util.function.Consumer;
13 import org.eclipse.jface.window.Window;
14 import org.eclipse.swt.widgets.Display;
15 import org.eclipse.swt.widgets.Shell;
16 import org.eclipse.ui.IEditorPart;
17 import org.simantics.Simantics;
18 import org.simantics.databoard.util.URIStringUtils;
19 import org.simantics.db.ReadGraph;
20 import org.simantics.db.Resource;
21 import org.simantics.db.common.NamedResource;
22 import org.simantics.db.common.request.ReadRequest;
23 import org.simantics.db.common.utils.NameUtils;
24 import org.simantics.db.exception.DatabaseException;
25 import org.simantics.db.layer0.variable.RVI;
26 import org.simantics.db.layer0.variable.Variable;
27 import org.simantics.db.layer0.variable.Variables;
28 import org.simantics.diagram.content.ConnectionUtil;
29 import org.simantics.diagram.flag.FlagUtil;
30 import org.simantics.diagram.stubs.DiagramResource;
31 import org.simantics.g2d.canvas.ICanvasContext;
32 import org.simantics.g2d.diagram.DiagramHints;
33 import org.simantics.layer0.Layer0;
34 import org.simantics.modeling.ComponentUtils;
35 import org.simantics.modeling.ModelingResources;
36 import org.simantics.modeling.actions.NavigateToTarget;
37 import org.simantics.modeling.actions.NavigationTargetChooserDialog;
38 import org.simantics.modeling.ui.Activator;
39 import org.simantics.structural.stubs.StructuralResource2;
40 import org.simantics.ui.utils.ResourceAdaptionUtils;
41 import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;
42 import org.simantics.utils.datastructures.MapSet;
43 import org.simantics.utils.datastructures.Pair;
44 import org.simantics.utils.strings.AlphanumComparator;
45 import org.simantics.utils.strings.EString;
46 import org.simantics.utils.threads.SWTThread;
47 import org.simantics.utils.threads.ThreadUtils;
48 import org.simantics.utils.ui.AdaptionUtils;
49 import org.simantics.utils.ui.workbench.WorkbenchUtils;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * @author Tuukka Lehtonen
56 public class OpenDiagramFromComponentAdapter extends AbstractResourceEditorAdapter {
58 private static final Logger LOGGER = LoggerFactory.getLogger(OpenDiagramFromComponentAdapter.class);
60 private static final String EDITOR_ID = "org.simantics.modeling.ui.diagramEditor"; //$NON-NLS-1$
62 public OpenDiagramFromComponentAdapter() {
63 super(Messages.OpenDiagramFromComponentAdapter_OpenDiagramContainingComponent, Activator.SYMBOL_ICON);
66 protected String getEditorId(ReadGraph g, Resource diagram) throws DatabaseException {
67 ModelingResources MOD = ModelingResources.getInstance(g);
68 String preferredEditorId = g.getPossibleRelatedValue(diagram, MOD.PreferredDiagramEditorID);
69 if(preferredEditorId != null)
70 return preferredEditorId;
76 public boolean canHandle(ReadGraph graph, Object input) throws DatabaseException {
77 Pair<Resource, String> p = tryGetResource(graph, input);
80 Variable v = AdaptionUtils.adaptToSingle(input, Variable.class);
81 Collection<Runnable> rs = tryFindDiagram(graph, p.first, v, p.second);
85 private Pair<Resource, String> tryGetResource(ReadGraph graph, Object input) throws DatabaseException {
86 Resource r = ResourceAdaptionUtils.toSingleResource(input);
88 return Pair.make(r, ""); //$NON-NLS-1$
89 Variable v = AdaptionUtils.adaptToSingle(input, Variable.class);
90 return findResource(graph, v);
93 private Pair<Resource, String> findResource(ReadGraph graph, Variable v) throws DatabaseException {
94 List<String> path = null;
96 Resource r = v.getPossibleRepresents(graph);
98 String rvi = ""; //$NON-NLS-1$
100 int pathLength = path.size();
101 for (int i = 0; i < pathLength; i++)
102 path.set(i, URIStringUtils.escape(path.get(i)));
103 Collections.reverse(path);
104 rvi = EString.implode(path, "/"); //$NON-NLS-1$
106 return Pair.make(r, rvi);
109 path = new ArrayList<>(2);
110 path.add( v.getName(graph) );
111 v = v.browsePossible(graph, "."); //$NON-NLS-1$
117 public void openEditor(final Object input) throws Exception {
118 final Display d = Display.getCurrent();
122 Simantics.getSession().syncRequest(new ReadRequest() {
124 public void run(ReadGraph graph) throws DatabaseException {
125 Pair<Resource, String> r = tryGetResource(graph, input);
129 Variable v = AdaptionUtils.adaptToSingle(input, Variable.class);
131 if (LOGGER.isDebugEnabled()) {
132 LOGGER.debug(getClass().getSimpleName() + ".openEditor: input's nearest parent resource URI: " + NameUtils.getURIOrSafeNameInternal(graph, r.first)); //$NON-NLS-1$
133 LOGGER.debug(getClass().getSimpleName() + ".openEditor: input's nearest parent RVI: " + r.second); //$NON-NLS-1$
134 LOGGER.debug(getClass().getSimpleName() + ".openEditor: input variable URI: " + (v != null ? v.getURI(graph) : "null")); //$NON-NLS-1$ //$NON-NLS-2$
137 final Collection<Runnable> rs = tryFindDiagram(graph, r.first, v, r.second);
141 SWTThread.getThreadAccess(d).asyncExec(() -> rs.forEach(Runnable::run));
146 private Collection<Runnable> tryFindDiagram(ReadGraph g, Resource component, Variable variable, String rviFromComponent) throws DatabaseException {
148 return findDiagram(g, component, variable, rviFromComponent);
149 } catch (DatabaseException e) {
150 return Collections.emptyList();
154 private Collection<Runnable> findDiagram(ReadGraph g, Resource component, Variable variable, String rviFromComponent) throws DatabaseException {
155 Layer0 l0 = Layer0.getInstance(g);
156 StructuralResource2 STR = StructuralResource2.getInstance(g);
157 ModelingResources MOD = ModelingResources.getInstance(g);
159 if (g.isInstanceOf(component, STR.Component)) {
160 Collection<Runnable> result = new ArrayList<>(1);
162 Resource composite = g.getSingleObject(component, l0.PartOf);
163 Resource diagram = ComponentUtils.getPossibleCompositeDiagram(g, composite);
165 String editorId = getEditorId(g, composite);
167 if (LOGGER.isDebugEnabled()) {
168 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: component: " + NameUtils.getURIOrSafeNameInternal(g, component)); //$NON-NLS-1$
169 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: composite: " + NameUtils.getURIOrSafeNameInternal(g, composite)); //$NON-NLS-1$
172 Collection<Resource> referenceElements = diagram == null ? g.getObjects(component, MOD.HasParentComponent_Inverse) : Collections.<Resource>emptyList();
173 if (LOGGER.isDebugEnabled()) {
174 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: diagram: " + NameUtils.getURIOrSafeNameInternal(g, diagram)); //$NON-NLS-1$
175 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: referenceElements: " + referenceElements.size()); //$NON-NLS-1$
176 for (Object object : referenceElements)
177 LOGGER.debug("\t" + NameUtils.getURIOrSafeNameInternal(g, (Resource) object)); //$NON-NLS-1$
179 if (diagram == null && referenceElements.isEmpty())
180 return Collections.emptyList();
182 Variable compositeVariable = Variables.getPossibleVariable(g, composite);
183 if (compositeVariable == null)
184 return Collections.emptyList();
185 final Resource indexRoot = Variables.getPossibleIndexRoot(g, compositeVariable);
186 if (indexRoot == null)
187 return Collections.emptyList();
188 if (LOGGER.isDebugEnabled())
189 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: Model: " + indexRoot); //$NON-NLS-1$
191 if (diagram != null) {
192 if(OpenDiagramFromConfigurationAdapter.isLocked(g, diagram))
193 return Collections.emptyList();
196 boolean allowNullRvi = false;
197 if (variable != null) {
198 // Get proper RVI from variable if it exists.
199 Variable context = Variables.getPossibleContext(g, variable);
200 if (context != null) {
201 // We want the composite's RVI, not the component in it.
202 Variable parent = findFirstParentComposite(g, variable);
203 if (parent != null) {
204 rvi = parent.getPossibleRVI(g);
205 if (LOGGER.isDebugEnabled())
206 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: resolved RVI: " + rvi); //$NON-NLS-1$
211 rvi = compositeVariable.getPossibleRVI(g);
212 if (LOGGER.isDebugEnabled())
213 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: resolved RVI from resource path: " + rvi); //$NON-NLS-1$
215 if (rvi == null && !allowNullRvi)
216 return Collections.emptyList();
218 Collection<Object> selectedObjects = findElementObjects(g, component, rviFromComponent);
219 if (LOGGER.isDebugEnabled()) {
220 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: selected objects: " + selectedObjects.size()); //$NON-NLS-1$
221 for (Object object : selectedObjects)
222 LOGGER.debug("\t" + NameUtils.getURIOrSafeNameInternal(g, (Resource) object)); //$NON-NLS-1$
224 // Prevent diagram from opening if there's nothing to select
225 // on the diagram based on the received input.
226 if (!selectedObjects.isEmpty())
227 result.add( NavigateToTarget.editorActivator(editorId, diagram, indexRoot, rvi, editorActivationCallback(selectedObjects)) );
229 final MapSet<NamedResource, Resource> referencingDiagrams = listReferenceDiagrams(g, referenceElements);
230 final Set<NamedResource> diagrams = referencingDiagrams.getKeys();
231 if (LOGGER.isDebugEnabled()) {
232 LOGGER.debug(getClass().getSimpleName() + ".findDiagram: selected objects: " + diagrams.size()); //$NON-NLS-1$
233 for (NamedResource d : diagrams) {
234 LOGGER.debug("\t" + NameUtils.getURIOrSafeNameInternal(g, d.getResource()) + ":"); //$NON-NLS-1$ //$NON-NLS-2$
235 for (Resource referenceElement : referencingDiagrams.getValues(d)) {
236 LOGGER.debug("\t\t" + NameUtils.getURIOrSafeNameInternal(g, referenceElement)); //$NON-NLS-1$
240 switch (diagrams.size()) {
242 // Prevent diagram from opening if there's nothing to select
243 // on the diagram based on the received input.
247 // Open the one diagram straight away.
248 NamedResource singleDiagram = diagrams.iterator().next();
249 RVI rvi = getDiagramCompositeRvi(g, singleDiagram.getResource());
251 Collection<Resource> selectedObjects = referencingDiagrams.getValues(singleDiagram);
252 result.add( NavigateToTarget.editorActivator(editorId, singleDiagram.getResource(), indexRoot, rvi, editorActivationCallback(selectedObjects)) );
257 final Map<NamedResource, RVI> diagramToRvi = new TreeMap<>(COMPARATOR);
258 for (NamedResource d : diagrams) {
259 RVI rvi2 = getDiagramCompositeRvi(g, d.getResource());
261 diagramToRvi.put(d, rvi2);
264 NamedResource selected = queryTarget(WorkbenchUtils.getActiveWorkbenchWindowShell(), diagramToRvi.keySet());
265 if (selected != null) {
266 Collection<Resource> selectedObjects = referencingDiagrams.getValues(selected);
267 RVI drvi = diagramToRvi.get(selected);
268 NavigateToTarget.editorActivator(editorId, selected.getResource(), indexRoot, drvi, editorActivationCallback(selectedObjects)).run();
278 return Collections.emptyList();
281 private RVI getDiagramCompositeRvi(ReadGraph graph, Resource diagram) throws DatabaseException {
282 ModelingResources MOD = ModelingResources.getInstance(graph);
283 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
284 if (composite == null)
286 Variable v = Variables.getPossibleVariable(graph, composite);
287 return v != null ? v.getPossibleRVI(graph) : null;
290 private Consumer<IEditorPart> editorActivationCallback(final Collection<? extends Object> selectedObjects) {
292 final ICanvasContext openedCanvas = (ICanvasContext) part.getAdapter(ICanvasContext.class);
293 assert openedCanvas != null;
294 // CanvasContext-wide denial of initial zoom-to-fit on diagram open.
295 openedCanvas.getDefaultHintContext().setHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT, Boolean.FALSE);
296 ThreadUtils.asyncExec(openedCanvas.getThreadAccess(),
297 NavigateToTarget.elementSelectorZoomer(openedCanvas, selectedObjects, false));
301 private Variable findFirstParentComposite(ReadGraph graph, Variable v) throws DatabaseException {
302 Variable first = findFirstWithRepresentation(graph, v);
305 Variable parent = first.getParent(graph);
309 private Variable findFirstWithRepresentation(ReadGraph graph, Variable v) throws DatabaseException {
311 Resource represents = v.getPossibleRepresents(graph);
312 if (LOGGER.isDebugEnabled())
313 LOGGER.debug(v.getURI(graph) + " -> " + NameUtils.getURIOrSafeNameInternal(graph, represents)); //$NON-NLS-1$
314 if (represents != null)
316 v = v.getParent(graph);
321 public static Collection<Object> findElementObjects(ReadGraph g, Resource component, String rviFromComponent) throws DatabaseException {
322 DiagramResource DIA = DiagramResource.getInstance(g);
323 ModelingResources MOD = ModelingResources.getInstance(g);
324 final Collection<Object> selectedObjects = new ArrayList<>(4);
325 if (rviFromComponent.isEmpty()) {
326 // The selected objects are configuration objects
327 for (Resource element : g.getObjects(component, MOD.ComponentToElement)) {
328 if (g.isInstanceOf(element, DIA.Flag) && FlagUtil.isExternal(g, element)) {
329 // Use external flag primarily if one exists in the correspondences
330 selectedObjects.clear();
331 selectedObjects.add(element);
333 } else if (g.isInstanceOf(element, DIA.RouteGraphConnection)) {
334 selectedObjects.add(element);
335 } else if (g.isInstanceOf(element, DIA.Connection)) {
336 // Okay, we need to find a part of the connection
337 ConnectionUtil cu = new ConnectionUtil(g);
338 cu.gatherConnectionParts(element, selectedObjects);
340 selectedObjects.add(element);
344 return selectedObjects;
347 protected MapSet<NamedResource, Resource> listReferenceDiagrams(ReadGraph graph, Collection<Resource> referenceElements) throws DatabaseException {
348 ModelingResources MOD = ModelingResources.getInstance(graph);
350 // Make result diagram ordering stable and logical by using our own comparator.
351 MapSet<NamedResource, Resource> diagrams = new MapSet.Tree<>(COMPARATOR);
353 for (Resource referenceElement : referenceElements) {
354 final Resource diagram = NavigateToTarget.getOwnerList(graph, referenceElement);
357 Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
358 if (composite == null)
360 Variable v = Variables.getPossibleVariable(graph, composite);
364 String rvi = URIStringUtils.unescape( Variables.getRVI(graph, v) );
366 diagrams.add(new NamedResource(rvi, diagram), referenceElement);
372 private static final Comparator<? super NamedResource> COMPARATOR =
373 (o1, o2) -> AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getName(), o2.getName());
375 protected NamedResource queryTarget(final Shell parentShell, Collection<NamedResource> options) {
376 NavigationTargetChooserDialog dialog = new NavigationTargetChooserDialog(
377 parentShell, options.toArray(new NamedResource[options.size()]),
378 Messages.OpenDiagramFromComponentAdapter_ChooseDiagramComponetReference,
379 Messages.OpenDiagramFromComponentAdapter_SelectSingleDiagramfromList);
380 return dialog.open() != Window.OK ? null : dialog.getSelection();