From 2ccc058ac6c303b64a70ad0f0c48ca501bf4aa6a Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 14 Aug 2020 11:37:58 +0300 Subject: [PATCH 01/16] Listen to changes in page settings in DiagramSceneGraphProvider This is needed for use cases where diagrams are transformed to SVG and viewed through browsers. These changes also fix PDF printing to force page border rendering off when not fitting PDF pages to diagram content, but to diagram page size. This avoids printing ugly black half-visible borders around diagrams where page borders are enabled. gitlab #563 Change-Id: I7f666dec34c650489264f3d23cbd72d5102b2f14 --- .../diagram/export/ExportDiagramPdf.java | 8 +++++- .../simantics/modeling/ui/pdf/PDFPainter.java | 10 ++++++- .../ui/sg/DiagramSceneGraphProvider.java | 28 ++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ExportDiagramPdf.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ExportDiagramPdf.java index 23cb9f970..25e72d984 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ExportDiagramPdf.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ExportDiagramPdf.java @@ -183,8 +183,14 @@ public class ExportDiagramPdf implements ExportClass { ThreadUtils.syncExec(workerThread, new Runnable() { @Override public void run() { - try { + try { cctx.getDefaultHintContext().setHint(Hints.KEY_PAGE_DESC, _marginaaliViiva); + if (!fitDiagramContentsToPageMargins) { + // Prevent PDF printing from drawing page borders if the + // print area is fitted directly to the page size. + // This avoids unwanted black half-visible edges. + cctx.getDefaultHintContext().setHint(Hints.KEY_DISPLAY_PAGE, false); + } String bottomLabel = diagramName; if ( drawingTemplate != null && activeProfileEntries.contains(TMPL.DrawingTemplate) ) bottomLabel = null; diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFPainter.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFPainter.java index bec1e5408..0b7cd63e4 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFPainter.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/pdf/PDFPainter.java @@ -89,7 +89,15 @@ public class PDFPainter { ThreadUtils.asyncExec(thread, () -> { try { - PDFBuilder chassis = new PDFBuilder(writer, mapper, pageSize, pageDesc, fitDiagramContentsToPageMargins || isSymbol); + boolean fitToContent = fitDiagramContentsToPageMargins || isSymbol; + if (!fitToContent) { + // Prevent PDF printing from drawing page borders if the + // print area is fitted directly to the page size. + // This avoids unwanted black half-visible edges. + ctx.getDefaultHintContext().setHint(Hints.KEY_DISPLAY_PAGE, false); + } + + PDFBuilder chassis = new PDFBuilder(writer, mapper, pageSize, pageDesc, fitToContent); chassis.paint(ctx, true); } catch (Throwable e) { exception[0] = new DatabaseException(e); diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/sg/DiagramSceneGraphProvider.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/sg/DiagramSceneGraphProvider.java index 8f223a622..302e074fc 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/sg/DiagramSceneGraphProvider.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/sg/DiagramSceneGraphProvider.java @@ -26,6 +26,7 @@ import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; import org.simantics.db.exception.NoSingleResultException; import org.simantics.db.exception.ServiceException; import org.simantics.db.management.ISessionContext; +import org.simantics.db.procedure.Listener; import org.simantics.db.request.Read; import org.simantics.diagram.adapter.DefaultConnectionClassFactory; import org.simantics.diagram.adapter.FlagClassFactory; @@ -98,6 +99,7 @@ import org.simantics.utils.page.PageDesc; import org.simantics.utils.page.PageOrientation; import org.simantics.utils.threads.AWTThread; import org.simantics.utils.threads.IThreadWorkQueue; +import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.ErrorLogger; @@ -290,6 +292,30 @@ public class DiagramSceneGraphProvider implements ICanvasSceneGraphProvider, IDi DiagramDesc diagramDesc = Simantics.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource)); if (diagramDesc != null) setDiagramDesc(ctx, diagramDesc); + + // Create a listener to react to page setting changes. + Simantics.getSession().asyncRequest(DiagramRequests.getDiagramDesc(diagramResource), new Listener() { + @Override + public void execute(DiagramDesc result) { + if (result != null && ctx != null) { + ThreadUtils.asyncExec(ctx.getThreadAccess(), () -> { + if (ctx != null) { + setDiagramDesc(ctx, result); + } + }); + } + } + + @Override + public void exception(Throwable t) { + ErrorLogger.defaultLogError(t); + } + + @Override + public boolean isDisposed() { + return DiagramSceneGraphProvider.this.ctx == null; + } + }); } catch (DatabaseException e) { ErrorLogger.defaultLogError(e); } @@ -298,7 +324,7 @@ public class DiagramSceneGraphProvider implements ICanvasSceneGraphProvider, IDi protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) { IHintContext hints = ctx.getDefaultHintContext(); hints.setHint(Hints.KEY_PAGE_DESC, diagramDesc.getPageDesc()); - //hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible()); + hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible()); hints.setHint(Hints.KEY_DISPLAY_MARGINS, diagramDesc.isMarginsVisible()); } -- 2.43.2 From 617b9475710b80a125597f222f9777224972ce72 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 21 Aug 2020 15:29:39 +0300 Subject: [PATCH 02/16] Fix NPE from flagTransform The bug affected loading of route graph connections about to be split in half when attached to diagram flags at either or both ends. The NPE caused the flag terminal position to receive identity transform as value which is plain wrong. This in turn resulted in the splitting position being completely wrong compared to what the user requested and the result looking very strange. This fixes the immediate problem but still doesn't work properly if the flag is actually attached to a diagram template flag table. In this case the terminal's position will be incorrectly calculated since runtimeDiagram is null. This needs another issue. gitlab #586 Change-Id: If09524c71c701f2c1190d0f915aafbb972c51da4 (cherry picked from commit 2526602db10c29f0c30c4a3cffaf33012d9c4a0e) --- .../src/org/simantics/diagram/function/All.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/function/All.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/function/All.java index 6cd4c542f..5bb793f7d 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/function/All.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/function/All.java @@ -95,6 +95,8 @@ public class All { Resource flag = context.element; Resource runtimeDiagram = context.runtime; + if (runtimeDiagram == null) + return flagTransformImpl(graph, converter, context); Resource diagram = graph.getPossibleObject(runtimeDiagram, DIA.RuntimeDiagram_HasConfiguration); if (diagram == null) -- 2.43.2 From b643293df21959d6416d0ba42637419c33a1dbad Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 21 Aug 2020 16:06:50 +0300 Subject: [PATCH 03/16] Fixed route graph splitting and diagram mapping race condition problem RouteGraphConnectionSplitter.doSplit now always splits the connection so that part #1 is always the part that stays with the existing diagram connection and part #2 contains the entities that are moved to the newly created route graph connection resource. Part #1 is the part where the "output terminal" attached to the connection lies. This simplifies and clarifies the implementation of doSplit and does not require moving diagram mapping statements around. Also, more importantly RouteGraphConnectionSplitter and FlagUtil changes ensure that diagram mapping is *not* executed in FlagUtil.join, which was the main cause of the previous corruption. Diagram mapping is only activated once for the affected diagram(s) after everything else is done. gitlab #549 gitlab #586 Change-Id: Icf7479e8e111bf663f637d6909419267cfa4eec4 (cherry picked from commit cc78488e2230fea07726acb86db3c7dd245b39ce) --- .../diagram/connection/RouteLine.java | 7 + .../diagram/connection/segments/Segment.java | 5 + .../splitting/SplittedRouteGraph.java | 78 ++++- .../org/simantics/diagram/flag/FlagUtil.java | 34 +- .../flag/RouteGraphConnectionSplitter.java | 308 ++++++++++-------- .../graph/RouteGraphModification.java | 9 +- 6 files changed, 286 insertions(+), 155 deletions(-) diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java index 2011bfa28..225c4abfe 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java @@ -113,10 +113,17 @@ public class RouteLine implements RouteNode, Serializable { out.print(" HOR"); else out.print(" VER"); + if (hidden) + out.print(" HIDDEN"); + out.print(" @ " + position); for(RoutePoint point : points) { out.print(" ("+point.x+","+point.y+")"); } out.print(" (data=" + data + ")"); + if (nextTransient != null) + out.print(" (next transient line=" + nextTransient.getData() + ")"); + if (terminal != null) + out.print(" (terminal=" + terminal.getData() + ")"); out.println(); } diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java index b283cb4ed..4e4280c0a 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java @@ -28,4 +28,9 @@ public class Segment { public boolean isDegenerated() { return p1.getX() == p2.getX() && p1.getY() == p2.getY(); } + + @Override + public String toString() { + return String.format("(%f, %f) (%f, %f)", p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } } diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java index ae79dc019..8f7f71a29 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java @@ -1,13 +1,17 @@ package org.simantics.diagram.connection.splitting; -import gnu.trove.set.hash.THashSet; - +import java.awt.geom.Line2D; import java.awt.geom.Point2D; +import java.util.ArrayList; import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteNode; +import org.simantics.diagram.connection.RoutePoint; import org.simantics.diagram.connection.RouteTerminal; +import org.simantics.diagram.connection.segments.Segment; + +import gnu.trove.set.hash.THashSet; public class SplittedRouteGraph { public final RouteLine splitLine; @@ -91,7 +95,75 @@ public class SplittedRouteGraph { } } - /** + public static final class PickResult { + /** + * The connection route line nearest to {@link #pickPoint}. + */ + public final RouteLine nearestLine; + /** + * Original pick point in canvas coordinates. + */ + public final Point2D pickPoint; + /** + * Intersection point in canvas coordinates of {@link #nearestLine} and + * perpendicular line from {@link #pickPoint} to {@link #nearestLine}. + */ + public final Point2D intersectionPoint; + + public PickResult(RouteLine nearestLine, Point2D pickPoint, Point2D intersectionPoint) { + this.nearestLine = nearestLine; + this.pickPoint = pickPoint; + this.intersectionPoint = intersectionPoint; + } + } + + public static PickResult pickNearestLine(RouteGraph rg, double x, double y) { + Segment nearestSegment = null; + RouteLine nearestLine = null; + + ArrayList segments = new ArrayList<>(); + double minDistanceSq = Double.MAX_VALUE; + for (RouteLine line : rg.getAllLines()) { + segments.clear(); + line.collectSegments(segments); + for (Segment segment : segments) { + RoutePoint p1 = segment.p1; + RoutePoint p2 = segment.p2; + double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); + if (distanceSq < minDistanceSq) { + minDistanceSq = distanceSq; + nearestSegment = segment; + nearestLine = line; + } + } + } + + if (nearestSegment == null) + return null; + + RoutePoint p1 = nearestSegment.p1; + RoutePoint p2 = nearestSegment.p2; + Point2D p = pointToLineIntersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); + return new PickResult(nearestLine, new Point2D.Double(x, y), p); + } + + private static Point2D pointToLineIntersection(double x1, double y1, double x2, double y2, double px, double py) { + double d = Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0); + if (d == 0) { + return new Point2D.Double(x1, y1); + } else { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / d; + if (u > 1.0) { + return new Point2D.Double(x2, y2); + } else if (u <= 0.0) { + return new Point2D.Double(x1, y1); + } else { + return new Point2D.Double(x2 * u + x1 * (1.0-u), (y2 * u + y1 * (1.0- u))); + } + } + } + + /** * @param point * @param line * @return the specified point instance snapped to the specified line diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java index d38a301da..e53f4b636 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java @@ -176,6 +176,17 @@ public final class FlagUtil { * @throws DatabaseException */ public static Resource join(WriteGraph g, Resource flag, Resource otherFlag) throws DatabaseException { + return join(g, flag, otherFlag, true); + } + + /** + * @param g + * @param flag + * @param otherFlag + * @return the created DIA.ConnectionJoin instance + * @throws DatabaseException + */ + public static Resource join(WriteGraph g, Resource flag, Resource otherFlag, boolean activateDiagramMapping) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(g); StructuralResource2 STR = StructuralResource2.getInstance(g); Resource connectionJoin = g.newResource(); @@ -184,14 +195,24 @@ public final class FlagUtil { g.claim(connectionJoin, DIA.JoinsFlag, flag); g.claim(connectionJoin, DIA.JoinsFlag, otherFlag); - IActivationManager manager = g.getService(IActivationManager.class); - for(Resource diagram : OrderedSetUtils.getSubjects(g, flag)) - manager.activateOnce(g, diagram); - for(Resource diagram : OrderedSetUtils.getSubjects(g, otherFlag)) - manager.activateOnce(g, diagram); + if (activateDiagramMapping) { + activateMappingForParentDiagramsOf(g, flag, otherFlag); + } + return connectionJoin; } + public static void activateMappingForParentDiagramsOf(WriteGraph graph, Resource... elements) throws DatabaseException { + IActivationManager manager = graph.getService(IActivationManager.class); + Set diagrams = new HashSet<>(elements.length); + for (Resource e : elements) { + diagrams.addAll(OrderedSetUtils.getSubjects(graph, e)); + } + for (Resource diagram : diagrams) { + manager.activateOnce(graph, diagram); + } + } + public static void disconnectFlag(WriteGraph graph, Resource flag) throws DatabaseException { // Remove any :ConnectionJoin's this flag is joined by // if there's less than two flags joined by the join. @@ -531,5 +552,4 @@ public final class FlagUtil { return flags; } - -} +} \ No newline at end of file diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java index 3f1076b5d..5c85a9944 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java @@ -1,11 +1,10 @@ package org.simantics.diagram.flag; -import gnu.trove.map.hash.TObjectIntHashMap; - import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; @@ -24,9 +23,11 @@ import org.simantics.diagram.connection.RouteNode; import org.simantics.diagram.connection.RoutePoint; import org.simantics.diagram.connection.RouteTerminal; import org.simantics.diagram.connection.splitting.SplittedRouteGraph; +import org.simantics.diagram.connection.splitting.SplittedRouteGraph.PickResult; import org.simantics.diagram.content.ConnectionUtil; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.graph.AddElement; +import org.simantics.diagram.synchronization.graph.BasicResources; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.synchronization.graph.RouteGraphModification; import org.simantics.g2d.elementclass.FlagClass; @@ -34,6 +35,8 @@ import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.structural.stubs.StructuralResource2; +import gnu.trove.map.hash.TObjectIntHashMap; + /** * A class that handles splitting a route graph connection in two with diagram * local flags. @@ -87,34 +90,33 @@ public class RouteGraphConnectionSplitter { RouteGraphModification modis = new RouteGraphModification(ss, rg); TObjectIntHashMap idMap = modis.getIdMap(); + if (DEBUG) { + System.out.println("Split canvas position: " + splitCanvasPos); + rg.print(); + } + // Find the edge to disconnect in the graph. // Bisect the nearest route line. - RouteLine line = SplittedRouteGraph.findNearestLine(rg, splitCanvasPos); - if (DEBUG) - rg.print(); - if (line == null) + PickResult picked = SplittedRouteGraph.pickNearestLine(rg, splitCanvasPos.getX(), splitCanvasPos.getY()); + if (picked == null) return; + + RouteLine line = picked.nearestLine; + if (DEBUG) { + System.out.println("picked nearest line:"); line.print(System.out); for (RoutePoint rp : line.getPoints()) System.out.println("RP: " + rp.getX() + ", " + rp.getY()); } // Get exact intersection point on the line - double isectX = splitCanvasPos.getX(); - double isectY = splitCanvasPos.getY(); - SplittedRouteGraph srg; - if (line.isHorizontal()) { - isectY = line.getPosition(); - srg = rg.splitGraph(line, isectX); - } - else { - isectX = line.getPosition(); - srg = rg.splitGraph(line, isectY); - } + double isectX = picked.intersectionPoint.getX(); + double isectY = picked.intersectionPoint.getY(); + SplittedRouteGraph srg = rg.splitGraph(line, line.isHorizontal() ? isectX : isectY); if (DEBUG) System.out.println(srg); - + // Disconnect if(rg.isSimpleConnection()) { RouteNode na = srg.terminals1.iterator().next(); @@ -141,44 +143,106 @@ public class RouteGraphConnectionSplitter { idMap.get(srg.splitLine) )); } - - ArrayList interfaceNodes1Resources = new ArrayList(srg.interfaceNodes1.size()); - for(RouteNode n : srg.interfaceNodes1) - interfaceNodes1Resources.add(ss.getResource((Long)n.getData())); - ArrayList interfaceNodes2Resources = new ArrayList(srg.interfaceNodes2.size()); - for(RouteNode n : srg.interfaceNodes2) - interfaceNodes2Resources.add(ss.getResource((Long)n.getData())); - - ArrayList lines2Resources = new ArrayList(srg.lines2.size()); - for(RouteLine n : srg.lines2) - lines2Resources.add(ss.getResource((Long)n.getData())); - - ArrayList terminals1Resources = new ArrayList(srg.terminals1.size()); - for(RouteTerminal n : srg.terminals1) - terminals1Resources.add(ss.getResource((Long)n.getData())); - ArrayList terminals2Resources = new ArrayList(srg.terminals2.size()); - for(RouteTerminal n : srg.terminals2) - terminals2Resources.add(ss.getResource((Long)n.getData())); + ArrayList terminals1Resources = toResources(srg.terminals1); + ArrayList terminals2Resources = toResources(srg.terminals2); + + boolean mustFlip = analyzePartInputs(graph, terminals1Resources, terminals2Resources); + + ArrayList interfaceNodes1 = toResources(mustFlip ? srg.interfaceNodes2 : srg.interfaceNodes1); + ArrayList interfaceNodes2 = toResources(mustFlip ? srg.interfaceNodes1 : srg.interfaceNodes2); + + ArrayList lines2 = toResources(mustFlip ? srg.lines1 : srg.lines2); + ArrayList terminals1 = mustFlip ? terminals2Resources : terminals1Resources; + ArrayList terminals2 = mustFlip ? terminals1Resources : terminals2Resources; + doSplit(graph, connection, - interfaceNodes1Resources, - interfaceNodes2Resources, - lines2Resources, - terminals1Resources, - terminals2Resources, + interfaceNodes1, + interfaceNodes2, + lines2, + terminals1, + terminals2, line.isHorizontal(), + mustFlip, isectX, isectY); modis.addModi(new RouteGraphModification.Split( - modis.toIds(interfaceNodes1Resources), - modis.toIds(interfaceNodes2Resources), - modis.toIds(lines2Resources), - modis.toIds(terminals1Resources), - modis.toIds(terminals2Resources), + modis.toIds(interfaceNodes1), + modis.toIds(interfaceNodes2), + modis.toIds(lines2), + modis.toIds(terminals1), + modis.toIds(terminals2), line.isHorizontal(), + mustFlip, isectX, isectY )); - } - + + private ArrayList toResources(Collection nodes) throws DatabaseException { + ArrayList result = new ArrayList<>(nodes.size()); + for (RouteNode n : nodes) + result.add(ss.getResource((Long)n.getData())); + return result; + } + + /** + * @param graph + * @param terminals1 + * @param terminals2 + * @return true if inputs need to be flipped, i.e. if terminals2 + * contains the output terminals and terminals1 doesn't. + * @throws DatabaseException + */ + private boolean analyzePartInputs(ReadGraph graph, List terminals1, List terminals2) throws DatabaseException { + @SuppressWarnings("unused") + int inputs1 = 0, outputs1 = 0; + for(Resource connector : terminals1) { + if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) + ++inputs1; + else + ++outputs1; + } + @SuppressWarnings("unused") + int inputs2 = 0, outputs2 = 0; + for(Resource connector : terminals2) { + if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) + ++inputs2; + else + ++outputs2; + } + + boolean mustFlip = outputs1 == 0; + + if (DEBUG) { + System.out.println("inputs1: " + inputs1); + System.out.println("outputs1: " + outputs1); + System.out.println("inputs2: " + inputs2); + System.out.println("outputs2: " + outputs2); + System.out.println("=> type1: " + (mustFlip ? FlagClass.Type.In : FlagClass.Type.Out)); + System.out.println("=> type2: " + (mustFlip ? FlagClass.Type.Out : FlagClass.Type.In)); + System.out.println("=> must flip route graph parts to split: " + mustFlip); + } + + return mustFlip; + } + + private static String routeNodeDebugInfo(ReadGraph graph, Resource c) throws DatabaseException { + BasicResources BR = BasicResources.getInstance(graph); + String ctr = NameUtils.getSafeName(graph, c, true); + for (Resource e : graph.getObjects(c, BR.STR.Connects)) { + ctr += " --> " + NameUtils.getSafeName(graph, e); + } + for (Resource e : graph.getObjects(c, BR.DIA.AreConnected)) { + ctr += " <-> " + NameUtils.getSafeName(graph, e); + } + return ctr; + } + + /** + * Internal routine that is only public because + * {@link RouteGraphModification#runUpdates(WriteGraph)} needs to invoke it. + * + * Assumes that #1 parameters will stay with the existing connection and #2 + * parameters will go to the newly created connection. + */ public void doSplit(WriteGraph graph, Resource connection, ArrayList interfaceNodes1Resources, @@ -186,22 +250,28 @@ public class RouteGraphConnectionSplitter { ArrayList lines2Resources, ArrayList terminals1Resources, ArrayList terminals2Resources, - boolean isHorizontal, + boolean isHorizontal, + boolean invertFlagRotation, double isectX, double isectY) throws DatabaseException { + // 1 = output, 2 = input + FlagClass.Type + type1 = FlagClass.Type.Out, + type2 = FlagClass.Type.In; + if (DEBUG) { System.out.println("doSplit:"); System.out.println(NameUtils.getSafeName(graph, connection, true)); for (Resource i : interfaceNodes1Resources) - System.out.println("i1: " + NameUtils.getSafeName(graph, i, true)); + System.out.println("i1: " + routeNodeDebugInfo(graph, i)); for (Resource i : interfaceNodes2Resources) - System.out.println("i2: " + NameUtils.getSafeName(graph, i, true)); + System.out.println("i2: " + routeNodeDebugInfo(graph, i)); for (Resource l : lines2Resources) - System.out.println("l2r: " + NameUtils.getSafeName(graph, l, true)); + System.out.println("l2r: " + routeNodeDebugInfo(graph, l)); for (Resource t : terminals1Resources) - System.out.println("t1: " + NameUtils.getSafeName(graph, t, true)); + System.out.println("t1: " + routeNodeDebugInfo(graph, t)); for (Resource t : terminals2Resources) - System.out.println("t2: " + NameUtils.getSafeName(graph, t, true)); + System.out.println("t2: " + routeNodeDebugInfo(graph, t)); System.out.println("is horizontal: " + isHorizontal); System.out.println("@(x,y): " + isectX + ", " + isectY); } @@ -209,15 +279,39 @@ public class RouteGraphConnectionSplitter { ConnectionUtil cu = new ConnectionUtil(graph); Resource diagram = OrderedSetUtils.getSingleOwnerList(graph, connection, DIA.Diagram); - Resource connectionType = graph.getSingleType(connection, DIA.Connection); + Resource diagramConnectionType = graph.getSingleType(connection, DIA.Connection); Resource hasConnectionType = graph.getPossibleObject(connection, STR.HasConnectionType); - Resource newConnection = cu.newConnection(diagram, connectionType); + Resource newConnection = cu.newConnection(diagram, diagramConnectionType); if (hasConnectionType != null) graph.claim(newConnection, STR.HasConnectionType, null, hasConnectionType); // Give running name to connection increment the counter attached to the diagram. AddElement.claimFreshElementName(graph, diagram, newConnection); + String commonLabel = DiagramFlagPreferences + .getActiveFlagLabelingScheme(graph) + .generateLabel(graph, diagram); + + Point2D pos1, pos2; + double theta; + double flagDist = 3.0; + if(isHorizontal) { + theta = 0.0; + pos1 = new Point2D.Double(isectX-flagDist, isectY); + pos2 = new Point2D.Double(isectX+flagDist, isectY); + } else { + theta = Math.PI*0.5; + pos1 = new Point2D.Double(isectX, isectY-flagDist); + pos2 = new Point2D.Double(isectX, isectY+flagDist); + } + + if (invertFlagRotation) { + theta += Math.PI; + Point2D p = pos1; + pos1 = pos2; + pos2 = p; + } + // WORKAROUND for mapping problems: // If any terminal of the split connection contains a flag, make sure their STR.Joins relations are all removed // to give mapping a chance to fix them properly. @@ -237,97 +331,31 @@ public class RouteGraphConnectionSplitter { graph.claim(rn, predicate, newConnection); } - // 1 = output, 2 = input - FlagClass.Type type1, type2; - - FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph); - String commonLabel = scheme.generateLabel(graph, diagram); - // Create flags and connect both disconnected ends to them. - Point2D pos1, pos2; - double theta; - double flagDist = 3.0; - if(isHorizontal) { - theta = 0.0; - pos1 = new Point2D.Double(isectX-flagDist, isectY); - pos2 = new Point2D.Double(isectX+flagDist, isectY); - } - else { - theta = Math.PI*0.5; - pos1 = new Point2D.Double(isectX, isectY-flagDist); - pos2 = new Point2D.Double(isectX, isectY+flagDist); - } - - // Chooses flag directions - { - @SuppressWarnings("unused") - int inputs1 = 0, outputs1 = 0; - for(Resource connector : terminals1Resources) { - if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) - ++inputs1; - else - ++outputs1; - } - @SuppressWarnings("unused") - int inputs2 = 0, outputs2 = 0; - for(Resource connector : terminals2Resources) { - if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) - ++inputs2; - else - ++outputs2; - } - - if(outputs1 == 0) { - type1 = FlagClass.Type.In; - type2 = FlagClass.Type.Out; - theta += Math.PI; - } - else { - type1 = FlagClass.Type.Out; - type2 = FlagClass.Type.In; - } - if (DEBUG) { - System.out.println("inputs1: " + inputs1); - System.out.println("outputs1: " + outputs1); - System.out.println("=> type1: " + type1); - System.out.println("inputs2: " + inputs2); - System.out.println("outputs2: " + outputs2); - System.out.println("=> type2: " + type2); - } - } Resource flag1 = createFlag(graph, diagram, getFlagTransform(pos1, theta), type1, commonLabel); Resource flag2 = createFlag(graph, diagram, getFlagTransform(pos2, theta), type2, commonLabel); + if (DEBUG) { + System.out.println("LABEL FOR NEW FLAGS: " + commonLabel); System.out.println("FLAG1: " + NameUtils.getSafeName(graph, flag1, true)); System.out.println("FLAG2: " + NameUtils.getSafeName(graph, flag2, true)); } -// System.out.println("conn1: " + NameUtils.getSafeLabel(graph, type1 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector)); -// System.out.println("conn2: " + NameUtils.getSafeLabel(graph, type2 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector)); - Resource flagConnector1 = cu.newConnector(connection, - type1 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector); - Resource flagConnector2 = cu.newConnector(newConnection, - type2 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector); + Resource flagConnector1 = cu.newConnector(connection, DIA.HasArrowConnector); + Resource flagConnector2 = cu.newConnector(newConnection, DIA.HasPlainConnector); graph.claim(flag1, DIA.Flag_ConnectionPoint, flagConnector1); graph.claim(flag2, DIA.Flag_ConnectionPoint, flagConnector2); double position = isHorizontal ? isectY : isectX; - connectFlag(graph, isHorizontal, position, connection, flagConnector1, - interfaceNodes1Resources); - connectFlag(graph, isHorizontal, position, newConnection, flagConnector2, - interfaceNodes2Resources); - - FlagUtil.join(graph, flag1, flag2); - - // Move mapping relations to new connection if necessary - if(type1 == FlagClass.Type.In) { - moveStatements(graph, connection, newConnection, MOD.ElementToComponent); - moveStatements(graph, connection, newConnection, MOD.DiagramConnectionToConnection); - moveStatements(graph, connection, newConnection, MOD.DiagramConnectionToConnectionSpecial); - FlagUtil.fixBindsStatements(graph, graph.getPossibleObject(newConnection, MOD.DiagramConnectionToConnection)); - } - else - FlagUtil.fixBindsStatements(graph, graph.getPossibleObject(connection, MOD.DiagramConnectionToConnection)); + connectFlag(graph, isHorizontal, position, connection, flagConnector1, interfaceNodes1Resources); + connectFlag(graph, isHorizontal, position, newConnection, flagConnector2, interfaceNodes2Resources); + + // Join the flags without activatingn diagram mapping at this point + FlagUtil.join(graph, flag1, flag2, false); + FlagUtil.fixBindsStatements(graph, graph.getPossibleObject(connection, MOD.DiagramConnectionToConnection)); + + // Finally ensure that all the diagrams related to the operation are mapped properly in one go + FlagUtil.activateMappingForParentDiagramsOf(graph, flag1, flag2); } /** @@ -370,15 +398,6 @@ public class RouteGraphConnectionSplitter { } } - private static void moveStatements(WriteGraph graph, Resource from, Resource to, Resource relation) throws DatabaseException { - if(from.equals(to)) - return; - for(Statement stat : graph.getStatements(from, relation)) - if(stat.getSubject().equals(from)) - graph.claim(to, stat.getPredicate(), stat.getObject()); - graph.deny(from, relation); - } - private void connectFlag(WriteGraph graph, boolean isHorizontal, double position, Resource connection, Resource flagConnector, Collection interfaceNodes) throws DatabaseException { if(interfaceNodes.size() > 1) { @@ -421,6 +440,7 @@ public class RouteGraphConnectionSplitter { } public static void splitConnection(WriteGraph graph, Resource connection, double x, double y) throws DatabaseException { + // TODO: provide a proper runtimeDiagram parameter to load to support also connections attached to flags attached to diagram template flag tables RouteGraph rg = RouteGraphUtils.load(graph, null, connection); new RouteGraphConnectionSplitter(graph).split(graph, connection, rg, new Point2D.Double(x, y)); } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java index a15b99bc4..41f4c00e6 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java @@ -162,12 +162,14 @@ public class RouteGraphModification { int[] lines2; int[] terminals1; int[] terminals2; - boolean isHorizontal; + boolean isHorizontal; + boolean invertFlagRotation; double isectX; double isectY; public Split(int[] interface1, int[] interface2, int[] lines2, int[] terminals1, int[] terminals2, boolean isHorizontal, + boolean invertFlagRotation, double isectX, double isectY) { this.interface1 = interface1; this.interface2 = interface2; @@ -175,6 +177,7 @@ public class RouteGraphModification { this.terminals1 = terminals1; this.terminals2 = terminals2; this.isHorizontal = isHorizontal; + this.invertFlagRotation = invertFlagRotation; this.isectX = isectX; this.isectY = isectY; } @@ -188,6 +191,7 @@ public class RouteGraphModification { this.terminals1 = readInts(it); this.terminals2 = readInts(it); this.isHorizontal = Boolean.parseBoolean(it.next()); + this.invertFlagRotation = Boolean.parseBoolean(it.next()); this.isectX = Double.parseDouble(it.next()); this.isectY = Double.parseDouble(it.next()); } @@ -208,6 +212,8 @@ public class RouteGraphModification { b.append("$"); b.append(isHorizontal); b.append("$"); + b.append(invertFlagRotation); + b.append("$"); b.append(isectX); b.append("$"); b.append(isectY); @@ -531,6 +537,7 @@ public class RouteGraphModification { toResources(modi.terminals1), toResources(modi.terminals2), modi.isHorizontal, + modi.invertFlagRotation, modi.isectX, modi.isectY ); -- 2.43.2 From c0941146a40af9df766b514fd4238aa20ec2ff4f Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Wed, 26 Aug 2020 09:59:48 +0300 Subject: [PATCH 04/16] Add utility class org.simantics.modeling.help.HelpContexts The new class gathers headless code related to reading help context ids from the database that was previously crammed into a very illogical package org.simantics.modeling.ui.modelBrowser.handlers. gitlab #588 Change-Id: I4cbc00919d2dfef50a779ccee7f31eeb6d61eee5 --- .../modelBrowser/handlers/ContextualHelp.java | 39 ++-------- .../META-INF/MANIFEST.MF | 1 + .../org/simantics/modeling/actions/Help.java | 38 +++++----- .../simantics/modeling/help/HelpContexts.java | 73 +++++++++++++++++++ 4 files changed, 99 insertions(+), 52 deletions(-) create mode 100644 bundles/org.simantics.modeling/src/org/simantics/modeling/help/HelpContexts.java diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/ContextualHelp.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/ContextualHelp.java index 0d0ec7ce6..e7bfd35fa 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/ContextualHelp.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/modelBrowser/handlers/ContextualHelp.java @@ -20,16 +20,13 @@ import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.handlers.HandlerUtil; import org.simantics.Simantics; -import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variable; -import org.simantics.modeling.ModelingResources; -import org.simantics.modeling.PropertyVariables; +import org.simantics.modeling.help.HelpContexts; import org.simantics.ui.selection.WorkbenchSelectionUtils; -import org.simantics.utils.ui.AdaptionUtils; import org.slf4j.LoggerFactory; public class ContextualHelp extends AbstractHandler { @@ -47,7 +44,7 @@ public class ContextualHelp extends AbstractHandler { return Simantics.getSession().syncRequest(new UniqueRead() { @Override public String perform(ReadGraph graph) throws DatabaseException { - return getPossibleId(graph, resource, variable, sel); + return HelpContexts.getPossibleId(graph, resource, variable, sel); } }); } catch (DatabaseException e) { @@ -64,34 +61,12 @@ public class ContextualHelp extends AbstractHandler { return null; } + /** + * @deprecated use {@link HelpContexts#getPossibleId(ReadGraph, Resource, Variable, Object)} instead + */ + @Deprecated public static String getPossibleId(ReadGraph graph, Resource resource, Variable variable, ISelection sel) throws DatabaseException { - ModelingResources MOD = ModelingResources.getInstance(graph); - if (resource != null) { - Resource component = graph.getPossibleObject(resource, MOD.ElementToComponent); - String id = component != null ? graph.getPossibleRelatedValue2(component, MOD.contextualHelpId, Bindings.STRING) : null; - if (id != null) - return id; - id = graph.getPossibleRelatedValue2(resource, MOD.contextualHelpId, Bindings.STRING); - if (id != null) - return id; - } - - if (variable != null) { - String id = variable.getPossiblePropertyValue(graph, MOD.contextualHelpId, Bindings.STRING); - if (id != null) - return id; - } - - // TODO: consider removing this block - if (sel != null) { - PropertyVariables vars = AdaptionUtils.adaptToSingle(sel, PropertyVariables.class); - Variable var = vars != null ? vars.getConfiguration() : null; - String id = var != null ? var.getPossiblePropertyValue(graph, MOD.contextualHelpId, Bindings.STRING) : null; - if (id != null) - return id; - } - - return null; + return HelpContexts.getPossibleId(graph, resource, variable, sel); } } diff --git a/bundles/org.simantics.modeling/META-INF/MANIFEST.MF b/bundles/org.simantics.modeling/META-INF/MANIFEST.MF index 067521a89..e00a8e114 100644 --- a/bundles/org.simantics.modeling/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.modeling/META-INF/MANIFEST.MF @@ -47,6 +47,7 @@ Export-Package: org.simantics.modeling, org.simantics.modeling.adapters, org.simantics.modeling.export, org.simantics.modeling.flags, + org.simantics.modeling.help, org.simantics.modeling.mapping, org.simantics.modeling.migration, org.simantics.modeling.preferences, diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/Help.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/Help.java index ba1243108..22327aeb3 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/Help.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/actions/Help.java @@ -16,38 +16,36 @@ import org.simantics.Simantics; import org.simantics.databoard.Bindings; import org.simantics.db.Resource; import org.simantics.db.common.primitiverequest.PossibleRelatedValue2; -import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.ActionFactory; import org.simantics.modeling.ModelingResources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Help implements ActionFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(Help.class); + @Override public Runnable create(Object target) { - - if(!(target instanceof Resource)) + if (!(target instanceof Resource)) return null; - final Resource resource = (Resource)target; - - return new Runnable() { - @Override - public void run() { - - try { - ModelingResources MOD = ModelingResources.getInstance(Simantics.getSession()); - String id = Simantics.sync(new PossibleRelatedValue2(resource, MOD.contextualHelpId, Bindings.STRING)); - if(id == null) { - PlatformUI.getWorkbench().getHelpSystem().displayDynamicHelp(); - return; - } - PlatformUI.getWorkbench().getHelpSystem().displayHelp(id); - } catch (DatabaseException e) { - Logger.defaultLogError(e); + final Resource resource = (Resource) target; + + return () -> { + try { + ModelingResources MOD = ModelingResources.getInstance(Simantics.getSession()); + String id = Simantics.sync(new PossibleRelatedValue2(resource, MOD.contextualHelpId, Bindings.STRING)); + if (id == null) { + PlatformUI.getWorkbench().getHelpSystem().displayDynamicHelp(); + return; } - + PlatformUI.getWorkbench().getHelpSystem().displayHelp(id); + } catch (DatabaseException e) { + LOGGER.error("Failed to display help for resource {}", resource, e); } }; } + } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/help/HelpContexts.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/help/HelpContexts.java new file mode 100644 index 000000000..ca25cf417 --- /dev/null +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/help/HelpContexts.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2020 Association for Decentralized Information Management + * in Industry THTH ry. + * 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: + * Semantum Oy- initial API and implementation + *******************************************************************************/ +package org.simantics.modeling.help; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.modeling.ModelingResources; +import org.simantics.modeling.PropertyVariables; +import org.simantics.utils.ui.AdaptionUtils; + +/** + * @author Tuukka Lehtonen + * @since 1.46.0 + */ +public class HelpContexts { + + /** + * @param graph + * @param resource + * @param variable + * @param selection optional ISelection + * @return + * @throws DatabaseException + */ + public static String getPossibleId(ReadGraph graph, Resource resource, Variable variable, Object selection) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + if (resource != null) { + Resource component = graph.getPossibleObject(resource, MOD.ElementToComponent); + String id = component != null ? graph.getPossibleRelatedValue2(component, MOD.contextualHelpId, Bindings.STRING) : null; + if (id != null) + return id; + id = graph.getPossibleRelatedValue2(resource, MOD.contextualHelpId, Bindings.STRING); + if (id != null) + return id; + } + + if (variable != null) { + String id = variable.getPossiblePropertyValue(graph, MOD.contextualHelpId, Bindings.STRING); + if (id != null) + return id; + } + + // TODO: consider removing this block + if (selection != null) { + PropertyVariables vars = AdaptionUtils.adaptToSingle(selection, PropertyVariables.class); + Variable var = vars != null ? vars.getConfiguration() : null; + String id = var != null ? var.getPossiblePropertyValue(graph, MOD.contextualHelpId, Bindings.STRING) : null; + if (id != null) + return id; + } + + return null; + } + + public static String getPossibleId(ReadGraph graph, Variable variable, String property) throws DatabaseException { + ModelingResources MOD = ModelingResources.getInstance(graph); + Variable prop = variable != null ? variable.getPossibleProperty(graph, property) : null; + return prop != null ? prop.getPossiblePropertyValue(graph, MOD.contextualHelpId, Bindings.STRING) : null; + } + +} -- 2.43.2 From 90506d09a405fb20380a41bb7c89fb9c510f2d42 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Mon, 31 Aug 2020 14:06:16 +0300 Subject: [PATCH 05/16] Introduce DebuggerCanvasProxy to avoid compile time AWT toolkit init gitlab #593 Change-Id: Ie625f8bc7becbce47f7d035ba6f73eec0ba5566c (cherry picked from commit c8112ea7b21b56ef632d80765a7f4d113a7d3468) --- .../scl/Simantics/GraphicalDebugger.scl | 2 +- .../debug/graphical/DebuggerCanvasProxy.java | 32 +++++++++++++++++++ .../debug/graphical/GraphicalDebugger.java | 4 +-- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/DebuggerCanvasProxy.java diff --git a/bundles/org.simantics.debug.graphical/scl/Simantics/GraphicalDebugger.scl b/bundles/org.simantics.debug.graphical/scl/Simantics/GraphicalDebugger.scl index 7a23274c4..0542beb1d 100644 --- a/bundles/org.simantics.debug.graphical/scl/Simantics/GraphicalDebugger.scl +++ b/bundles/org.simantics.debug.graphical/scl/Simantics/GraphicalDebugger.scl @@ -1,6 +1,6 @@ import "Simantics/DB" -importJava "org.simantics.debug.graphical.DebuggerCanvas" where +importJava "org.simantics.debug.graphical.DebuggerCanvasProxy" where data GraphDebugger setStatementFilter :: GraphDebugger -> (Statement -> Boolean) -> () diff --git a/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/DebuggerCanvasProxy.java b/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/DebuggerCanvasProxy.java new file mode 100644 index 000000000..bf7a73887 --- /dev/null +++ b/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/DebuggerCanvasProxy.java @@ -0,0 +1,32 @@ +package org.simantics.debug.graphical; + +import org.simantics.db.Resource; +import org.simantics.scl.runtime.function.Function; + +/** + * Simantics/GraphicalDebugger SCL API to avoid undesired AWT + * toolkit initialization. + * + * @author Tuukka Lehtonen + */ +public class DebuggerCanvasProxy { + + DebuggerCanvas canvas; + + public void setStatementFilter(@SuppressWarnings("rawtypes") Function statementFilter) { + canvas.setStatementFilter(statementFilter); + } + + public void removeStatementFilter() { + canvas.removeStatementFilter(); + } + + public void addResource(Resource resource) { + canvas.addResource(resource); + } + + public DebuggerCanvasProxy(DebuggerCanvas canvas) { + this.canvas = canvas; + } + +} diff --git a/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/GraphicalDebugger.java b/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/GraphicalDebugger.java index 9091a27f9..81844e4ae 100644 --- a/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/GraphicalDebugger.java +++ b/bundles/org.simantics.debug.graphical/src/org/simantics/debug/graphical/GraphicalDebugger.java @@ -11,7 +11,7 @@ import javax.swing.JFrame; public class GraphicalDebugger { - public static DebuggerCanvas newGraphDebugger() { + public static DebuggerCanvasProxy newGraphDebugger() { final JFrame frame = new JFrame(); frame.setTitle("Graph debugger"); Toolkit tk = Toolkit.getDefaultToolkit(); @@ -53,7 +53,7 @@ public class GraphicalDebugger { }); frame.setVisible(true); - return canvas; + return new DebuggerCanvasProxy(canvas); } } -- 2.43.2 From c8efc02e56cbd70cba5cecdd7f97d3b8761d2f30 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Mon, 7 Sep 2020 16:34:21 +0300 Subject: [PATCH 06/16] Update license.html to better match current 3rd party components gitlab #598 --- license.html | 51 +++++++++++++++------------------------------------ 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/license.html b/license.html index cac8ec5bb..3d60be467 100644 --- a/license.html +++ b/license.html @@ -105,20 +105,6 @@ worth mentioning is ACE - Adaptive Communications Environment - - BSD 3-Clause License (http://www.cs.wustl.edu/~schmidt/ACE-copying.html) - -
Copyright (c) 2000-2005 INRIA, France Telecom
- - - JZlib - - BSD 3-Clause License (http://www.jcraft.com/jzlib/LICENSE.txt) - -
Copyright (c) 2000,2001,2002,2003,2004 ymnk, JCraft,Inc.
- - Batik (org.apache.batik) Apache Software License 2.0 @@ -148,7 +134,7 @@ All rights reserved. 1.2.3 - JSON (JSON_libraries) + Gson Apache Software License 2.0 @@ -169,14 +155,14 @@ All rights reserved. - JCommon, JFreeChart +JFreeChart LGPL - GNU Trove (gnu.trove2, gnu.trove3) + GNU Trove (gnu.trove3) LGPL @@ -254,7 +240,7 @@ Copyright (c) 2011-2016, Yann Collet EPL - 9.2.0 + 10.2.0 fastutil: Fast & compact type-specific collections for Java @@ -285,11 +271,12 @@ Copyright (c) 2011-2016, Yann Collet 21.0 - Aries + ANTLR v3 - Apache Software License 2.0 + BSD 3-Clause License (http://www.antlr3.org/license.html) - + 3.5.2 +
Copyright (c) 2010 Terence Parr
Apache Commons Codec @@ -341,13 +328,6 @@ Copyright (c) 2011-2016, Yann Collet 3.6.1 - Apache HttpClient 3 - - Apache Software License 2.0 - - 3.1.0 - - Apache POI Apache Software License 2.0 @@ -355,14 +335,6 @@ Copyright (c) 2011-2016, Yann Collet 3.15 - ANTLR v3 - - BSD 3-Clause License (http://www.antlr3.org/license.html) - - 3.5.2 -
Copyright (c) 2010 Terence Parr
- - Apache Lucene Core Apache Software License 2.0 @@ -384,6 +356,13 @@ Copyright (c) 2011-2016, Yann Collet 2.6.0, only needed by Apache POI + Aries + + Apache Software License 2.0 + + + + curvesapi BSD 3-Clause License (https://github.com/virtuald/curvesapi/blob/master/license.txt), Apache Software License 2.0 -- 2.43.2 From 6c6f34dda4dfa90bc403e1c525a84e17c17c0cde Mon Sep 17 00:00:00 2001 From: Reino Ruusu Date: Thu, 10 Sep 2020 17:18:34 +0300 Subject: [PATCH 07/16] Added org.apache.commons.csv to target definition gitlab #602 Change-Id: Ib4de0682ad5996d5ebe2dde0998951ff25644cab --- releng/org.simantics.sdk.build.p2.site/pom.xml | 5 +++++ .../org.simantics.sdk.build.targetdefinition.target | 2 ++ .../org.simantics.sdk.build.targetdefinition.tpd | 2 ++ .../simantics.target | 2 ++ 4 files changed, 11 insertions(+) diff --git a/releng/org.simantics.sdk.build.p2.site/pom.xml b/releng/org.simantics.sdk.build.p2.site/pom.xml index 9534f71fb..4d84e674f 100644 --- a/releng/org.simantics.sdk.build.p2.site/pom.xml +++ b/releng/org.simantics.sdk.build.p2.site/pom.xml @@ -42,6 +42,7 @@ 0.5.4 4.4 1.20 + 1.6 2.6 1.2 1.4 @@ -270,6 +271,10 @@ org.apache.commons.compress + + org.apache.commons:commons-csv:${commons-csv.version} + true + commons-lang:commons-lang:${commons-lang.version} true diff --git a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target index 708e508ed..6500ce550 100644 --- a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target +++ b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.target @@ -281,6 +281,8 @@ + + diff --git a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd index 6a0428e20..7494c7981 100644 --- a/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd +++ b/releng/org.simantics.sdk.build.targetdefinition/org.simantics.sdk.build.targetdefinition.tpd @@ -282,6 +282,8 @@ location "http://www.simantics.org/download/master/external-components/maven/" { org.apache.commons.collections4.source org.apache.commons.compress org.apache.commons.compress.source + org.apache.commons.csv + org.apache.commons.csv.source org.apache.commons.io org.apache.commons.io.source org.apache.commons.lang diff --git a/releng/org.simantics.sdk.build.targetdefinition/simantics.target b/releng/org.simantics.sdk.build.targetdefinition/simantics.target index 10ff40e65..2e8f54c2d 100644 --- a/releng/org.simantics.sdk.build.targetdefinition/simantics.target +++ b/releng/org.simantics.sdk.build.targetdefinition/simantics.target @@ -281,6 +281,8 @@ + + -- 2.43.2 From 99395a7780ef1626d3ae97183ef4ae717f32b215 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Thu, 17 Sep 2020 14:45:47 +0300 Subject: [PATCH 08/16] Fix CopyAdvisorUtil.copy2 to copy IsRelatedTo-statements left out before This implementation works so that it copies any IsRelatedTo statements where both the subject and object have been copied during the normal (old) copy process. The statements are marked as pending during the copy and post-processed after everything else has been copied. gitlab #607 Change-Id: I9170a448c127e0c7de6eae4260db5799ad7644bb --- .../graph/CopyAdvisorUtil.java | 80 ++++++++++++++++--- .../mapping/ComponentCopyAdvisor.java | 2 - 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java index ab28463e8..bbf9a8e5b 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java @@ -11,8 +11,6 @@ *******************************************************************************/ package org.simantics.diagram.synchronization.graph; -import gnu.trove.map.hash.THashMap; - import java.util.Map; import java.util.Set; import java.util.function.BiFunction; @@ -46,6 +44,10 @@ import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.graph.db.TransferableGraphs; import org.simantics.graph.representation.TransferableGraph1; import org.simantics.layer0.Layer0; +import org.simantics.utils.datastructures.BinaryFunction; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; /** * This class contains utility methods for the basic cut/copy operations @@ -82,6 +84,15 @@ public class CopyAdvisorUtil { public static final boolean DEBUG_COPY = DebugPolicy.DEBUG_COPY_PASTE; + private static class Statement4 { + public final Statement stm; + public final Resource inverse; + public Statement4(Statement stm, Resource inverse) { + this.stm = stm; + this.inverse = inverse; + } + } + /** * @param context a synchronization context instance, such as * {@link GraphToDiagramSynchronizer} @@ -364,8 +375,10 @@ public class CopyAdvisorUtil { * @throws DatabaseException */ public static Resource copy2(WriteGraph graph, Resource source, - BiFunction advisor) throws DatabaseException { - return copy2(graph, source, 0, advisor, new THashMap()); + BiFunction advisor) + throws DatabaseException + { + return copy2(graph, source, advisor, new THashMap<>()); } /** @@ -380,13 +393,52 @@ public class CopyAdvisorUtil { * @throws DatabaseException */ public static Resource copy2(WriteGraph graph, Resource source, - BiFunction advisor, Map copyMap) - throws DatabaseException { - return copy2(graph, source, 0, advisor, copyMap); + BiFunction advisor, + Map copyMap) + throws DatabaseException + { + Set pendingStatements = new THashSet<>(); + Resource result = copy2(graph, source, 0, advisor, copyMap, pendingStatements); + postProcessStatements(graph, copyMap, pendingStatements); + return result; + } + + /** + * Post-process pending statement + * + * Rule: If both the subject and object of a pending source statement have + * been copied, then the pending statement should also be copied. + */ + private static void postProcessStatements( + WriteGraph graph, + Map copyMap, + Set pendingStatements) + throws DatabaseException + { + if (pendingStatements.isEmpty()) + return; + + if (DEBUG_COPY) + System.out.println("post processing " + pendingStatements.size() + " pending statements"); + for (Statement4 srcStm : pendingStatements) { + // At this point, it is certain that srcStm subject has been copied + // but test it anyway. + Resource subjectCopy = (Resource) copyMap.get(srcStm.stm.getSubject()); + Resource objectCopy = (Resource) copyMap.get(srcStm.stm.getObject()); + if (subjectCopy == null || objectCopy == null) { + if (DEBUG_COPY) + System.out.println("skipping pending statement: " + NameUtils.toString(graph, srcStm.stm)); + continue; + } + if (DEBUG_COPY) + System.out.println("copying pending statement: " + NameUtils.toString(graph, srcStm.stm)); + graph.claim(subjectCopy, srcStm.stm.getPredicate(), srcStm.inverse, objectCopy); + } } private static Resource copy2(final WriteGraph graph, final Resource source, final int level, - BiFunction advisor, Map copyMap) + BiFunction advisor, Map copyMap, + Set pendingSourceStatements) throws DatabaseException { if (DEBUG_COPY) System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")"); @@ -497,12 +549,18 @@ public class CopyAdvisorUtil { if (DEBUG_COPY) System.out.println("[" + level + "]\t\tcopy whole object"); - Resource clone = copy2(graph, obj, level + 1, advisor, copyMap); + Resource clone = copy2(graph, obj, level + 1, advisor, copyMap, pendingSourceStatements); graph.claim(copy, relation, inverse, clone); } } else { - if (DEBUG_COPY) - System.out.println("[" + level + "]\t\tskipping statement"); + if (graph.isSubrelationOf(relation, L0.IsRelatedTo)) { + if (DEBUG_COPY) + System.out.println("[" + level + "]\t\tmarking statement as pending for post-processing"); + pendingSourceStatements.add(new Statement4(stm, inverse)); + } else { + if (DEBUG_COPY) + System.out.println("[" + level + "]\t\tskipping weak statement"); + } } } } diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/ComponentCopyAdvisor.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/ComponentCopyAdvisor.java index 3e6e9c1b9..021ecc436 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/ComponentCopyAdvisor.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/ComponentCopyAdvisor.java @@ -32,7 +32,6 @@ import org.simantics.modeling.ModelingUtils; import org.simantics.modeling.services.ComponentNamingUtil; import org.simantics.modeling.services.NamingException; import org.simantics.project.IProject; -import org.simantics.structural.stubs.StructuralResource2; import gnu.trove.map.hash.THashMap; @@ -58,7 +57,6 @@ public class ComponentCopyAdvisor extends GraphCopyAdvisor { @Override public Object copy(ISynchronizationContext context, WriteGraph graph, Resource source, Resource sourceContainer, Resource targetContainer, Map map) throws DatabaseException { - StructuralResource2 STR = StructuralResource2.getInstance(graph); Resource copy = CopyAdvisorUtil.copy2(graph, source, null, map); Layer0 L0 = Layer0.getInstance(graph); -- 2.43.2 From 39b62de8d2593275dfae8726772a636593e72e0e Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Thu, 24 Sep 2020 10:35:51 +0300 Subject: [PATCH 09/16] Remove apparent LOGGER.info used for transient debugging gitlab #608 --- .../db/layer0/scl/AbstractExpressionCompilationRequest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java index 1bcd91877..16f88a340 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java @@ -179,7 +179,6 @@ implements Read> { b2.append('\n'); } SCLDatabaseException exception = new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors()); - LOGGER.info(exception.getMessage(), exception); throw exception; } catch(Throwable e) { // Should not happen! -- 2.43.2 From fac3394a0015e8f1b642bb59f97c814ea5d7ff1f Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Thu, 24 Sep 2020 12:11:18 +0300 Subject: [PATCH 10/16] Replace file extension properly in FixExportedOntology gitlab #609 --- .../org/simantics/graph/refactoring/FixExportedOntology.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bundles/org.simantics.graph/src/org/simantics/graph/refactoring/FixExportedOntology.java b/bundles/org.simantics.graph/src/org/simantics/graph/refactoring/FixExportedOntology.java index 17aa3ed26..33291cdf1 100644 --- a/bundles/org.simantics.graph/src/org/simantics/graph/refactoring/FixExportedOntology.java +++ b/bundles/org.simantics.graph/src/org/simantics/graph/refactoring/FixExportedOntology.java @@ -41,8 +41,9 @@ public class FixExportedOntology { private static Path replaceExtension(Path p, String newExtension) { String newName = p.getFileName().toString(); - if (newName.contains(".")) - newName = newName.split("\\.")[0]; + int lastDot = newName.lastIndexOf('.'); + if (lastDot > -1) + newName = newName.substring(0, lastDot); return p.resolveSibling(newName + newExtension); } -- 2.43.2 From 86617be247efb3b904b3180d0569049aff232d75 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Mon, 28 Sep 2020 15:29:08 +0300 Subject: [PATCH 11/16] Fix SymbolLibraryComposite DnD to cope also with GroupProxySymbolItems gitlab #613 Change-Id: Ia45674fb6fe99f4dc958096d35688441f363e80e (cherry picked from commit 1b93154e988c98b4a2be6a1492b6eabc8b0f6471) --- .../ui/SymbolLibraryComposite.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java index af58be479..31c091fd4 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java @@ -88,7 +88,6 @@ import org.simantics.diagram.internal.Activator; import org.simantics.diagram.symbolcontribution.CompositeSymbolGroup; import org.simantics.diagram.symbolcontribution.IIdentifiedObject; import org.simantics.diagram.symbolcontribution.ISymbolProvider; -import org.simantics.diagram.symbolcontribution.IdentifiedObject; import org.simantics.diagram.symbolcontribution.SymbolProviderFactory; import org.simantics.diagram.symbollibrary.IModifiableSymbolGroup; import org.simantics.diagram.symbollibrary.ISymbolGroup; @@ -1118,24 +1117,22 @@ public class SymbolLibraryComposite extends Composite { json.append(" \"res\" : ["); int pos = 0; for(int i=0;i 0) json.append(","); - Object r = res[i]; - if(r instanceof IdentifiedObject) { - Object id = ((IdentifiedObject) r).getId(); - if(id instanceof IAdaptable) { - Object resource = ((IAdaptable) id).getAdapter(Resource.class); - if(resource != null) { - long rid = ((Resource)resource).getResourceId(); - json.append(Long.toString(rid)); - pos++; - } - } - } + if(pos > 0) json.append(","); + Object r = res[i]; + if(r instanceof IAdaptable) { + Resource resource = ((IAdaptable) r).getAdapter(Resource.class); + if(resource != null) { + long rid = resource.getResourceId(); + json.append(Long.toString(rid)); + pos++; + } + } } json.append("] }"); - StringSelection text = new StringSelection(json.toString()); - PlaintextTransfer plainText = new PlaintextTransfer(json.toString()); + String jsonText = json.toString(); + StringSelection text = new StringSelection(jsonText); + PlaintextTransfer plainText = new PlaintextTransfer(jsonText); return new MultiTransferable(local, text, plainText); -- 2.43.2 From 215cb2930ed3fb362b2951d1da3080abec976972 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Wed, 30 Sep 2020 08:41:03 +0300 Subject: [PATCH 12/16] Add isDisposed checking to avoid unexpected NPE In this case ctx.getContentContext() would return null when the canvas context was already disposed. gitlab #614 (cherry picked from commit 930da66f9b2d7d1acba3e5dc805a323933abb780) --- .../src/org/simantics/g2d/gallery/GalleryViewer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java index 5bbe9f26e..d09ecf4a6 100644 --- a/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java +++ b/bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java @@ -631,8 +631,10 @@ public class GalleryViewer extends ContentViewer { ctx.getThreadAccess().asyncExec(() -> { //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")"); // Update scene graph and repaint. - el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el); - ctx.getContentContext().setDirty(); + if (!ctx.isDisposed()) { + el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el); + ctx.getContentContext().setDirty(); + } }); break; } -- 2.43.2 From ccb253462b54a0e036f0906ec8ac11a9021a968c Mon Sep 17 00:00:00 2001 From: Antti Villberg Date: Tue, 6 Oct 2020 17:05:24 +0300 Subject: [PATCH 13/16] NodeStructureRequest and NodeValueRequest fire ExternalRead twice gitlab #617 Change-Id: Id424c9254e1d80fdd26665fcbbbc9080d0878d71 --- .../layer0/variable/NodeStructureRequest.java | 321 +++++++++--------- .../db/layer0/variable/NodeValueRequest.java | 12 +- 2 files changed, 168 insertions(+), 165 deletions(-) diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java index 5d21ca56b..707fdbd0e 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java @@ -7,174 +7,177 @@ import java.util.Map; import org.simantics.databoard.util.ObjectUtils; import org.simantics.db.ReadGraph; import org.simantics.db.common.request.ParametrizedPrimitiveRead; -import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variables.NodeStructure; import org.simantics.db.procedure.Listener; import org.simantics.simulator.variable.exceptions.NodeManagerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import gnu.trove.map.hash.THashMap; @SuppressWarnings("rawtypes") class NodeStructureRequest extends ParametrizedPrimitiveRead implements VariableNodeReadRunnable { - private Listener listener = null; - private NodeStructure value = Variables.PENDING_NODE_STRUCTURE; - private boolean wasRun = false; - - static class Probe implements Runnable { - - private VariableNode node; - public NodeStructure result; - - public Probe(VariableNode node) { - this.node = node; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - try { - result = NodeStructureRequest.get(node); - node.support.structureCache.put(node.node, result, 1000000000L); - } catch (NodeManagerException e) { - e.printStackTrace(); - } - } - - } - - public NodeStructureRequest(VariableNode node) { - super(node); - } - - @SuppressWarnings("unchecked") - @Override - public void register(ReadGraph graph, final Listener procedure) { - - if(procedure.isDisposed()) { - - // We are not listening - NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); - - if(result != null) { - // Return cached value immediately - procedure.execute(result); - } else { - NodeStructureRequest.Probe probe = new Probe(parameter); - parameter.support.manager.getRealm().asyncExec(probe); - if(probe.result != null) { - procedure.execute(probe.result); - } else { - procedure.execute(Variables.PENDING_NODE_STRUCTURE); - } - } - - return; - - } - - // We need to listen - listener = procedure; - // Register listening - parameter.support.manager.addNodeListener(parameter.node, this); - synchronized(this) { - if(wasRun) { - procedure.execute(value); - } else { - NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); - if(result != null) { - procedure.execute(result); - } else { - procedure.execute(Variables.PENDING_NODE_STRUCTURE); - } - } - } - - } - - static class NodeListener implements VariableNodeReadRunnable { - - private VariableNode node; - private NodeStructureRequest request; - - public NodeListener(VariableNode node, NodeStructureRequest request) { - this.node = node; - this.request = request; - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - node.support.manager.addNodeListener(node.node, request); - } - - } - - @SuppressWarnings("unchecked") - @Override - public void unregistered() { - parameter.support.manager.removeNodeListener(parameter.node, this); - parameter.support.structureCache.removeListening(parameter.node); - listener = null; - } - - @SuppressWarnings("unchecked") - public static NodeStructure get(VariableNode parameter) throws NodeManagerException { - List children = parameter.support.manager.getChildren(parameter.node); - List properties = parameter.support.manager.getProperties(parameter.node); - Map childMap = Collections.emptyMap(); - Map propertyMap = childMap; - if(!children.isEmpty()) { - childMap = new THashMap<>(children.size()); - for(Object o : children) { - String name = parameter.support.manager.getName(o); - childMap.put(name, o); - } - } - if(!properties.isEmpty()) { - propertyMap = new THashMap<>(properties.size()); - for(Object o : properties) { - String name = parameter.support.manager.getName(o); - propertyMap.put(name, o); - } - } - return new NodeStructure(childMap, propertyMap); - } - - @SuppressWarnings("unchecked") - @Override - public synchronized void run() { - try { - // Cache this value with infinite cache time since we are listening - NodeStructure newValue = get(parameter); - if (wasRun && ObjectUtils.objectEquals(value, newValue)) { - //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node); - return; - } - value = newValue; - parameter.support.structureCache.put(parameter.node, value); - } catch (Throwable e) { - // Must catch everything to prevent DB client from getting stuck. - Logger.defaultLogError(e); - // Invoke the exception method of the listener - Listener listener = this.listener; - if (listener != null) listener.exception(new DatabaseException("External data access error", e)); - wasRun = true; - return; - } - - // Must always invoke an existing listener, regardless of earlier errors. - Listener listener = this.listener; - if (listener != null) { - listener.execute(value); - } - wasRun = true; - } - - @Override - public String toString() { - return "NodeStructureRequest.run @ " + System.identityHashCode(this); - } + private static final Logger LOGGER = LoggerFactory.getLogger(NodeStructureRequest.class); + + private Listener listener = null; + private NodeStructure value = Variables.PENDING_NODE_STRUCTURE; + private boolean wasRun = false; + + static class Probe implements Runnable { + + private VariableNode node; + public NodeStructure result; + + public Probe(VariableNode node) { + this.node = node; + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + try { + result = NodeStructureRequest.get(node); + node.support.structureCache.put(node.node, result, 1000000000L); + } catch (NodeManagerException e) { + e.printStackTrace(); + } + } + + } + + public NodeStructureRequest(VariableNode node) { + super(node); + } + + @SuppressWarnings("unchecked") + @Override + public void register(ReadGraph graph, final Listener procedure) { + + if(procedure.isDisposed()) { + + // We are not listening + NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); + + if(result != null) { + // Return cached value immediately + procedure.execute(result); + } else { + NodeStructureRequest.Probe probe = new Probe(parameter); + parameter.support.manager.getRealm().asyncExec(probe); + if(probe.result != null) { + procedure.execute(probe.result); + } else { + procedure.execute(Variables.PENDING_NODE_STRUCTURE); + } + } + + return; + + } + + // We need to listen + listener = procedure; + // Register listening + parameter.support.manager.addNodeListener(parameter.node, this); + synchronized(this) { + if(!wasRun) { + NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node); + if(result != null) { + procedure.execute(result); + } else { + procedure.execute(Variables.PENDING_NODE_STRUCTURE); + } + } + } + + } + + static class NodeListener implements VariableNodeReadRunnable { + + private VariableNode node; + private NodeStructureRequest request; + + public NodeListener(VariableNode node, NodeStructureRequest request) { + this.node = node; + this.request = request; + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + node.support.manager.addNodeListener(node.node, request); + } + + } + + @SuppressWarnings("unchecked") + @Override + public void unregistered() { + parameter.support.manager.removeNodeListener(parameter.node, this); + parameter.support.structureCache.removeListening(parameter.node); + listener = null; + } + + @SuppressWarnings("unchecked") + public static NodeStructure get(VariableNode parameter) throws NodeManagerException { + List children = parameter.support.manager.getChildren(parameter.node); + List properties = parameter.support.manager.getProperties(parameter.node); + Map childMap = Collections.emptyMap(); + Map propertyMap = childMap; + if(!children.isEmpty()) { + childMap = new THashMap<>(children.size()); + for(Object o : children) { + String name = parameter.support.manager.getName(o); + childMap.put(name, o); + } + } + if(!properties.isEmpty()) { + propertyMap = new THashMap<>(properties.size()); + for(Object o : properties) { + String name = parameter.support.manager.getName(o); + propertyMap.put(name, o); + } + } + return new NodeStructure(childMap, propertyMap); + } + + @SuppressWarnings("unchecked") + @Override + public synchronized void run() { + try { + // Cache this value with infinite cache time since we are listening + NodeStructure newValue = get(parameter); + if (wasRun && ObjectUtils.objectEquals(value, newValue)) { + //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node); + return; + } + value = newValue; + parameter.support.structureCache.put(parameter.node, value); + } catch (Throwable e) { + // Must catch everything to prevent DB client from getting stuck. + LOGGER.error("Error while computing node structure", e); + // Invoke the exception method of the listener + Listener listener = this.listener; + if (listener != null) { + listener.exception(new DatabaseException("External data access error", e)); + wasRun = true; + } + return; + } + + // Must always invoke an existing listener, regardless of earlier errors. + Listener listener = this.listener; + if (listener != null) { + listener.execute(value); + wasRun = true; + } + } + + @Override + public String toString() { + return "NodeStructureRequest.run @ " + System.identityHashCode(this); + } } \ No newline at end of file diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java index 140ee8fa0..b44f16b30 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java @@ -110,9 +110,7 @@ class NodeValueRequest extends ParametrizedPrimitiveRead listener = this.listener; - if (listener != null) listener.exception(new DatabaseException("External data access error", e)); - wasRun = true; + if (listener != null) { + listener.exception(new DatabaseException("External data access error", e)); + wasRun = true; + } return; } // Must always invoke an existing listener, regardless of earlier errors. @@ -228,8 +228,8 @@ class NodeValueRequest extends ParametrizedPrimitiveRead Date: Tue, 6 Oct 2020 16:51:28 +0300 Subject: [PATCH 14/16] Guard graph SCL module compilation gitlab #616 Change-Id: I882a5414884f1838710ae02c80e153399dc38b9f --- .../scl/GraphModuleSourceRepository.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java index 8eed51aca..a70844cd3 100644 --- a/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java +++ b/bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java @@ -12,6 +12,8 @@ import org.simantics.db.common.request.WriteRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.SyncListener; import org.simantics.db.request.Read; +import org.simantics.db.request.ReadExt; +import org.simantics.db.request.RequestFlags; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingUtils; import org.simantics.modeling.internal.Activator; @@ -137,6 +139,29 @@ public enum GraphModuleSourceRepository implements ModuleSourceRepository { } } + static class PossibleResourceIU extends UnaryRead implements ReadExt { + + public PossibleResourceIU(String parameter) { + super(parameter); + } + + @Override + public Resource perform(ReadGraph graph) throws DatabaseException { + return graph.getPossibleResource(parameter); + } + + @Override + public boolean isImmutable(ReadGraph graph) throws DatabaseException { + return false; + } + + @Override + public int getType() { + return RequestFlags.IMMEDIATE_UPDATE; + } + + } + static class ReadModuleSource extends UnaryRead { public ReadModuleSource(String moduleName) { super(moduleName); @@ -144,7 +169,7 @@ public enum GraphModuleSourceRepository implements ModuleSourceRepository { @Override public ModuleSource perform(ReadGraph graph) throws DatabaseException { - Resource moduleResource = graph.getPossibleResource(parameter); + Resource moduleResource = graph.syncRequest(new PossibleResourceIU(parameter)); if(moduleResource == null) return null; Layer0 L0 = Layer0.getInstance(graph); -- 2.43.2 From 883a1447e1da4fb8365b85817e4d04588a0eb11d Mon Sep 17 00:00:00 2001 From: Jussi Koskela Date: Tue, 15 Sep 2020 15:30:31 +0300 Subject: [PATCH 15/16] Apply profile style only for added / removed items gitlab #605 Change-Id: Ic94aed406c05afef5fc1caf92d1efd39571fcd57 --- .../scenegraph/profile/common/ObserverGroupListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ObserverGroupListener.java b/bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ObserverGroupListener.java index 31295b9b6..10e2abe67 100644 --- a/bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ObserverGroupListener.java +++ b/bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ObserverGroupListener.java @@ -44,7 +44,7 @@ public class ObserverGroupListener implements SetListener { public void add(Resource item) { //System.out.println("Add to group(" + this + "): " + item); items.put(item, item); - observer.update(); + observer.update(style, item); } @Override @@ -52,7 +52,7 @@ public class ObserverGroupListener implements SetListener { // new Exception().printStackTrace(); //System.out.println("Remove from group(" + this + "): " + item); items.remove(item); - observer.update(); + observer.update(style, item); } @Override -- 2.43.2 From ae2e31aa5eb35410e5b2ce222d42421154f3fecc Mon Sep 17 00:00:00 2001 From: Jussi Koskela Date: Wed, 2 Sep 2020 12:17:31 +0300 Subject: [PATCH 16/16] Fixed multiple issues causing dangling references to discarded queries gitlab #594 Change-Id: Iaa6b12f60d7dfa2bbcbc9614ef837973885586cc --- .../simantics/db/impl/query/CacheEntry.java | 1 + .../db/impl/query/CacheEntryBase.java | 26 ++++++- .../db/impl/query/ExternalReadEntry.java | 10 +-- .../db/impl/query/QueryCollectorImpl.java | 1 + .../db/impl/query/QueryIdentityHash.java | 6 +- .../db/impl/query/QueryIdentityHashSet.java | 20 ++++- .../db/impl/query/QueryListening.java | 2 +- .../server/request/DocumentRequest.java | 75 ++++++++++--------- .../document/server/request/NodesRequest.java | 66 ++++++++-------- 9 files changed, 134 insertions(+), 73 deletions(-) diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntry.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntry.java index edb087025..a650f32ce 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntry.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntry.java @@ -42,6 +42,7 @@ public abstract class CacheEntry { abstract Query getQuery(); abstract CacheEntry pruneFirstParents(); + abstract void pruneParentSet(); abstract void removeParent(CacheEntry entry); abstract void addParent(CacheEntry entry); abstract boolean hasParents(); diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java index 56da11ace..98e7f0d00 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java @@ -278,7 +278,31 @@ public abstract class CacheEntryBase extends CacheEntry { } } - + + @Override + void pruneParentSet() { + // First parent is discarded => look for more parents + if(p2OrParents instanceof QueryIdentityHashSet) { + + QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents; + set.removeDiscardedReally(); + if(set.isEmpty()) p2OrParents = null; + + } else if(p2OrParents instanceof CacheEntry) { + + CacheEntry entry = (CacheEntry)p2OrParents; + if(entry.isDiscarded()) { + // Second entry is also discarded => all empty + p2OrParents = null; + } + + } else { + + // Nothing left + + } + } + @Override final public void removeParent(CacheEntry entry) { diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/ExternalReadEntry.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/ExternalReadEntry.java index 6253744bb..2980b9b15 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/ExternalReadEntry.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/ExternalReadEntry.java @@ -28,7 +28,7 @@ final public class ExternalReadEntry extends CacheEntryBase final LinkedList items = new LinkedList(); protected ExternalRead id; - protected ReadGraphImpl graph; + protected QueryProcessor processor; protected boolean registered = false; @Override @@ -49,7 +49,7 @@ final public class ExternalReadEntry extends CacheEntryBase public void discard() { id.unregistered(); id = null; - graph = null; + processor = null; super.discard(); } @@ -65,7 +65,7 @@ final public class ExternalReadEntry extends CacheEntryBase public ExternalReadEntry(ExternalRead request, ReadGraphImpl graph) { assert request != null; this.id = request; - this.graph = graph; + this.processor = graph.processor; } @Override @@ -213,7 +213,7 @@ final public class ExternalReadEntry extends CacheEntryBase synchronized(items) { items.addLast(result); - graph.processor.updatePrimitive(id); + processor.updatePrimitive(id); // TODO: implement flags/logic in ExternalRead to state that all but the latest request result can be evaporated // In some cases where data is produced really fast this might be necessary but currently this queueing will do. } @@ -229,7 +229,7 @@ final public class ExternalReadEntry extends CacheEntryBase @Override public boolean isDisposed() { - return registered && (isDiscarded() || !graph.processor.isBound(this)); + return registered && (isDiscarded() || !processor.isBound(this)); } @Override diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl.java index 0b6f6ef83..e6dc252c4 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl.java @@ -148,6 +148,7 @@ class QueryCollectorImpl implements QueryProcessor.QueryCollector { } else { + entry.pruneParentSet(); support.setLevel(entry, parent.getLevel() + 1); } diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHash.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHash.java index ffbf4b2a9..2db67c679 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHash.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHash.java @@ -229,7 +229,11 @@ abstract public class QueryIdentityHash extends THash { // TODO Auto-generated method stub return null; } - + + @Override + void pruneParentSet() { + } + }; /** diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHashSet.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHashSet.java index 3a7eb6182..ac2602fcb 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHashSet.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHashSet.java @@ -147,7 +147,25 @@ final public class QueryIdentityHashSet extends QueryIdentityHash implements Ite } } - + + final public void removeDiscardedReally() { + + tempDisableAutoCompaction(); + try { + + for(int i=0;i<_set.length;i++) { + CacheEntry entry = _set[i]; + if(entry != null && REMOVED != entry) { + if(entry.isDiscarded()) removeAt(i); + } + } + + } finally { + reenableAutoCompaction(false); + } + + } + /** * Creates an iterator over the values of the set. The iterator * supports element deletion. diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java index 91200f1fc..e0b39d651 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java @@ -415,7 +415,7 @@ public class QueryListening { @Override public void run() { - ListenerEntry entry = addedEntries.get(base); + ListenerEntry entry = addedEntries.remove(base); if(entry != null) entry.setLastKnown(result); } diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java index 8acc2f0d1..5b76cd344 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java @@ -1,6 +1,7 @@ package org.simantics.document.server.request; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -10,7 +11,7 @@ import java.util.Set; import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; -import org.simantics.db.common.request.AsyncReadRequest; +import org.simantics.db.common.request.UnaryAsyncRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.VariableRead; import org.simantics.db.layer0.variable.Variable; @@ -28,49 +29,55 @@ public class DocumentRequest extends VariableRead> { super(var); } - @Override - public List perform(ReadGraph graph) throws DatabaseException { - - long s = System.nanoTime(); - - Set nodes = graph.syncRequest(new NodesRequest(variable), TransientCacheAsyncListener.>instance()); - HashSet rs = new HashSet(); // result - if(nodes.isEmpty()) { - return Collections.emptyList(); - } + static class CollectNodesRequest extends UnaryAsyncRead, Collection> { - if(PROFILE) { - long dura = System.nanoTime()-s; - System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); + public CollectNodesRequest(Collection nodes) { + super(nodes); } - graph.syncRequest(new AsyncReadRequest() { + @Override + public void perform(AsyncReadGraph graph, AsyncProcedure> procedure) { + HashSet rs = new HashSet(); // result - @Override - public void run(AsyncReadGraph graph) throws DatabaseException { + for(Variable node : parameter) { + graph.asyncRequest(new NodeRequest(node), new AsyncProcedure () { - for(Variable node : nodes) { - graph.asyncRequest(new NodeRequest(node), new AsyncProcedure () { - - @Override - public void execute(AsyncReadGraph graph, JSONObject result) { - synchronized (rs) { - rs.add(result); - } + @Override + public void execute(AsyncReadGraph graph, JSONObject result) { + synchronized(rs) { + rs.add(result); } + } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - } + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + } + + }); - }); - - } - } - - }); + procedure.execute(graph, rs); + + } + + } + + @Override + public List perform(ReadGraph graph) throws DatabaseException { + + long s = System.nanoTime(); + + Set nodes = graph.syncRequest(new NodesRequest(variable), TransientCacheAsyncListener.>instance()); + if(nodes.isEmpty()) { + return Collections.emptyList(); + } + + if(PROFILE) { + long dura = System.nanoTime()-s; + System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph)); + } + Collection rs = graph.syncRequest(new CollectNodesRequest(nodes)); if(PROFILE) { long dura = System.nanoTime()-s; diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java index 99526368e..133264e29 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java @@ -2,11 +2,12 @@ package org.simantics.document.server.request; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; -import org.simantics.db.common.request.AsyncReadRequest; +import org.simantics.db.common.request.UnaryAsyncRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.VariableChildren; import org.simantics.db.layer0.request.VariableRead; @@ -16,52 +17,57 @@ import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.threads.logger.ITask; import org.simantics.utils.threads.logger.ThreadLogger; -import gnu.trove.set.hash.THashSet; - public class NodesRequest extends VariableRead> { public NodesRequest(Variable var) { super(var); } - @Override - public Set perform(ReadGraph graph) throws DatabaseException { + static class CollectNodesRequest2 extends UnaryAsyncRead, Set> { - ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null; + public CollectNodesRequest2(Collection nodes) { + super(nodes); + } - StructuralResource2.getInstance(graph); - if(variable == null) - return Collections.emptySet(); + @Override + public void perform(AsyncReadGraph graph, AsyncProcedure> procedure) { + HashSet rs = new HashSet(); // result - Set nodes = new THashSet(); + for(Variable node : parameter) { + graph.asyncRequest(new NodesRequest2(node), new AsyncProcedure> () { - Collection children = graph.syncRequest(new VariableChildren(variable)); + @Override + public void execute(AsyncReadGraph graph, Set result) { + synchronized(rs) { + rs.addAll(result); + } + } - graph.syncRequest(new AsyncReadRequest() { + @Override + public void exception(AsyncReadGraph graph, Throwable throwable) { + } - @Override - public void run(AsyncReadGraph graph) throws DatabaseException { + }); - for(Variable child : children) { - graph.asyncRequest(new NodesRequest2(child), new AsyncProcedure>() { + } + procedure.execute(graph, rs); - @Override - public void execute(AsyncReadGraph graph, Set result) { - synchronized(nodes) { - nodes.addAll(result); - } - } + } - @Override - public void exception(AsyncReadGraph graph, Throwable throwable) { - } - - }); - } + } - } + @Override + public Set perform(ReadGraph graph) throws DatabaseException { + + ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null; + + StructuralResource2.getInstance(graph); + if(variable == null) + return Collections.emptySet(); + + Collection children = graph.syncRequest(new VariableChildren(variable)); - }); + Set nodes = graph.syncRequest(new CollectNodesRequest2(children)); if(DocumentRequest.PROFILE) task.finish(); -- 2.43.2