try {
File modelFile = getProperty(MigrationStateKeys.MODEL_FILE);
TimeLogger.log(MigrationStateImpl.class, "reading TG into memory from " + modelFile);
- TransferableGraph1 tg = TransferableGraphFileReader.read(modelFile);
+ TransferableGraph1 tg = TransferableGraphFileReader.read(modelFile, false);
TimeLogger.log(MigrationStateImpl.class, "read TG into memory from " + modelFile);
setProperty(MigrationStateKeys.CURRENT_TG, tg);
return (T)tg;
protected boolean parseAsBlock() {
return false;
}
+
+ /*
+ * Override this to provide location information in compilation error situations.
+ */
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ return toString();
+ }
@SuppressWarnings("unchecked")
private Function1<EvaluationContext, Object> eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException {
else
throw e;
} catch (SCLExpressionCompilationException e) {
- LOGGER.error("Could not compile {}", evaluator.getExpressionText(), e);
StringBuilder b = new StringBuilder();
b.append("Couldn't compile '");
b.append(evaluator.getExpressionText());
+ b.append("' in ");
+ b.append(getContextDescription(graph));
b.append("':\n");
StringBuilder b2 = new StringBuilder();
for(CompilationError error : e.getErrors()) {
b2.append(error.description);
b2.append('\n');
}
- System.err.println(b.toString() + b2.toString());
- throw new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors());
+ 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!
LOGGER.error("This error should never happen", e);
public boolean isNear(double x2, double y2, double tolerance) {
return isHorizontal
? Math.abs(y2-position) <= tolerance
- && points.get(0).x <= x2
- && x2 <= points.get(points.size()-1).x
+ && points.get(0).x <= x2 - tolerance
+ && x2 <= points.get(points.size()-1).x + tolerance
: Math.abs(x2-position) <= tolerance
- && points.get(0).y <= y2
- && y2 <= points.get(points.size()-1).y;
+ && points.get(0).y <= y2 - tolerance
+ && y2 <= points.get(points.size()-1).y + tolerance;
}
public void print(PrintStream out) {
import org.simantics.db.procedure.AsyncProcedure;
import org.simantics.diagram.adapter.RouteGraphUtils.BackendConnection;
import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
import org.simantics.diagram.connection.rendering.ConnectionStyle;
import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;
import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.handler.TerminalTopology;
import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.utils.TopologicalSelectionExpander;
import org.simantics.layer0.Layer0;
import org.simantics.modeling.ModelingResources;
import org.simantics.db.exception.DatabaseException;
import org.simantics.diagram.connection.ConnectionVisuals;
import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
import org.simantics.diagram.connection.RouteLine;
import org.simantics.diagram.connection.RouteNode;
import org.simantics.diagram.connection.RouteTerminal;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
import org.simantics.g2d.element.handler.TerminalLayout;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.elementclass.FlagClass.Type;
import org.simantics.layer0.Layer0;
import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;
import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.DatabaseException;
import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
import org.simantics.diagram.connection.RouteLine;
import org.simantics.diagram.connection.RouteTerminal;
import org.simantics.diagram.connection.delta.RouteGraphDelta;
import org.simantics.g2d.elementclass.BranchPoint.Direction;
import org.simantics.g2d.elementclass.FlagClass;
import org.simantics.g2d.elementclass.FlagHandler;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.participant.RenderingQualityInteractor;
import org.simantics.g2d.participant.TransformUtil;
import org.simantics.g2d.utils.geom.DirectionSet;
import java.awt.Composite;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayDeque;
import org.simantics.db.common.request.WriteRequest;
import org.simantics.db.exception.DatabaseException;
import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
import org.simantics.diagram.connection.RouteLine;
import org.simantics.diagram.connection.RoutePoint;
import org.simantics.diagram.connection.RouteTerminal;
import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle;
import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;
+import org.simantics.diagram.connection.segments.Segment;
import org.simantics.diagram.synchronization.ISynchronizationContext;
import org.simantics.diagram.synchronization.SynchronizationHints;
import org.simantics.diagram.synchronization.graph.RouteGraphConnection;
import org.simantics.g2d.element.impl.Element;
import org.simantics.g2d.elementclass.FlagClass;
import org.simantics.g2d.elementclass.FlagHandler;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.participant.TransformUtil;
import org.simantics.g2d.utils.geom.DirectionSet;
import org.simantics.scenegraph.g2d.G2DParentNode;
}
private static RouteGraphTarget pickNearestRouteGraphConnection(ArrayList<IElement> elements, double x, double y, double pd) {
- // Find the nearest distance at which we get hits.
- double hi = pd + 1;
- double lo = hi * .01;
- double limit = 0.5;
- while (true) {
- double delta = (hi - lo);
- if (delta <= limit)
- break;
-
- pd = (lo + hi) * .5;
+ Segment nearestSegment = null;
+ RouteLine nearestLine = null;
+ IElement nearestConnection = null;
- boolean hit = false;
- for (IElement connection : elements) {
- RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
- RouteLine line = rgn.getRouteGraph().pickLine(x, y, pd);
- if (line != null) {
- hit = true;
- break;
+ double minDistanceSq = Double.MAX_VALUE;
+
+ for (IElement connection : elements) {
+ RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+ for (RouteLine line : rgn.getRouteGraph().getAllLines()) {
+ ArrayList<Segment> segments = new ArrayList<Segment>();
+ 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 && distanceSq < Math.pow(pd + rgn.getSelectionStrokeWidth() / 2, 2)) {
+ minDistanceSq = distanceSq;
+ nearestSegment = segment;
+ nearestLine = line;
+ nearestConnection = connection;
+ }
}
}
-
- if (hit)
- hi = pd;
- else
- lo = pd;
}
- // Now that the nearest hitting distance is found, find the nearest intersection.
- RouteGraphTarget nearestTarget = null;
- double nearest = Double.MAX_VALUE;
- for (IElement connection : elements) {
- RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
- RouteLine line = rgn.getRouteGraph().pickLine(x, y, pd);
- if (line == null)
- continue;
-
- Point2D intersection = intersectionPoint(x, y, line);
- if (intersection == null)
- continue;
-
- double dx = intersection.getX() - x;
- double dy = intersection.getY() - y;
- double dist = Math.sqrt(dx*dx + dy*dy);
- if (dist < nearest) {
- nearest = dist;
- nearestTarget = new RouteGraphTarget(connection, rgn, line, new Point2D.Double(x, y), intersection);
+ if (nearestSegment != null) {
+ RoutePoint p1 = nearestSegment.p1;
+ RoutePoint p2 = nearestSegment.p2;
+ double d = Math.pow(p2.getX() - p1.getX(), 2.0) + Math.pow(p2.getY() - p1.getY(), 2.0);
+ Point2D p;
+ if (d == 0) {
+ p = new Point2D.Double(p1.getX(), p1.getY());
+ } else {
+ double u = ((x - p1.getX()) * (p2.getX() - p1.getX()) + (y - p1.getY()) * (p2.getY() - p1.getY())) / d;
+ if (u > 1.0) {
+ p = new Point2D.Double(p2.getX(), p2.getY());
+ } else if (u <= 0.0) {
+ p = new Point2D.Double(p1.getX(), p1.getY());
+ } else {
+ p = new Point2D.Double(p2.getX() * u + p1.getX() * (1.0-u), (p2.getY() * u + p1.getY() * (1.0- u)));
+ }
}
+ return new RouteGraphTarget(nearestConnection, nearestConnection.getHint(RouteGraphConnectionClass.KEY_RG_NODE), nearestLine, new Point2D.Double(x, y), p);
+ } else {
+ return null;
}
-
- return nearestTarget;
}
static Point2D intersectionPoint(double x, double y, RouteLine line) {
import org.simantics.databoard.Bindings;
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
+import org.simantics.db.Statement;
import org.simantics.db.common.procedure.adapter.TransientCacheListener;
import org.simantics.db.common.request.IndexRoot;
import org.simantics.db.common.request.PossibleTypedParent;
import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.scl.AbstractExpressionCompilationContext;
import org.simantics.db.layer0.scl.AbstractExpressionCompilationRequest;
public static Function1<Object, Object> validate(ReadGraph graph, Variable context) throws DatabaseException {
return graph.syncRequest(new ServerSCLValueValidationRequest(graph, context), TransientCacheListener.instance());
}
+
+ @Override
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ Statement possibleOwner = graph.getPossibleStatement(literal, L0.IsOwnedBy);
+ if(possibleOwner != null) {
+ String uri = graph.getPossibleURI(possibleOwner.getObject());
+ if(uri != null) {
+ String propertyName = NameUtils.getSafeName(graph, graph.getInverse(possibleOwner.getPredicate()));
+ return uri + "#" + propertyName;
+ }
+ }
+ return super.getContextDescription(graph);
+ }
public static class ServerSCLValueValidationRequest extends ServerSCLValueRequest {
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
+import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import org.simantics.diagram.connection.RouteLine;
+import org.simantics.diagram.connection.RoutePoint;
+import org.simantics.diagram.connection.segments.Segment;
import org.simantics.g2d.canvas.ICanvasContext;
import org.simantics.g2d.connection.handler.ConnectionHandler;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
import org.simantics.g2d.elementclass.BranchPoint;
import org.simantics.g2d.elementclass.MonitorHandler;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
import org.simantics.scenegraph.utils.TransformedRectangle;
+import org.simantics.utils.datastructures.Pair;
/**
*
});
}
};
+
+ /*
+ * First use the default sorting if available, then sort successive connections by their distance to cursor.
+ */
+ public static PickSorter connectionSorter(PickSorter sorter, double x, double y) {
+ return new PickSorter() {
+
+ private double getDistanceSqToRouteGraphConnection(RouteGraphNode rgn, double x, double y) {
+ double minDistanceSq = Double.MAX_VALUE;
+ for (RouteLine line : rgn.getRouteGraph().getAllLines()) {
+ ArrayList<Segment> segments = new ArrayList<Segment>();
+ 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;
+ }
+ }
+ }
+ return minDistanceSq;
+ }
+
+ @Override
+ public void sort(List<IElement> elements) {
+ if (sorter != null)
+ sorter.sort(elements);
+
+ List<Pair<Double, IElement>> connections = new ArrayList<>(elements.size());
+ int tail = 0;
+ for (int i = 0; i < elements.size(); i++) {
+ IElement element = elements.get(i);
+ RouteGraphNode rgn = element.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+ if (rgn != null) {
+ double distanceSq = getDistanceSqToRouteGraphConnection(rgn, x, y);
+ connections.add(new Pair<Double, IElement>(distanceSq, element));
+ }
+
+ if (rgn == null || i == elements.size() - 1) {
+ Collections.sort(connections, new Comparator<Pair<Double, IElement>>() {
+ @Override
+ public int compare(Pair<Double, IElement> connection1, Pair<Double, IElement> connection2) {
+ return Double.compare(connection2.first, connection1.first);
+ }
+ });
+ for (Pair<Double, IElement> connection : connections) {
+ elements.set(tail, connection.second);
+ tail++;
+ }
+ connections.clear();
+ tail = i + 1;
+ }
+ }
+ }
+ };
+ }
}
}
import org.simantics.g2d.element.ElementClassProviders;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.IElementClassProvider;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.participant.KeyUtil;
import org.simantics.g2d.participant.MouseUtil;
import org.simantics.g2d.participant.TransformUtil;
import org.simantics.g2d.scenegraph.SceneGraphConstants;
import org.simantics.g2d.utils.CanvasUtils;
import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
import org.simantics.scenegraph.g2d.events.Event;
import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
import org.simantics.scenegraph.g2d.events.KeyEvent;
import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
import org.simantics.scenegraph.g2d.events.command.Commands;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
import org.simantics.utils.ObjectUtils;
import org.simantics.utils.datastructures.context.IContext;
import org.simantics.utils.datastructures.context.IContextListener;
+import org.simantics.utils.datastructures.hints.HintListenerAdapter;
import org.simantics.utils.datastructures.hints.IHintContext.Key;
import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
+import org.simantics.utils.datastructures.hints.IHintObservable;
import org.simantics.utils.threads.ThreadUtils;
/**
super.addedToContext(ctx);
hoverStrategy = new DefaultHoverStrategy((TerminalHoverStrategy) getHint(TerminalPainter.TERMINAL_HOVER_STRATEGY));
setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, hoverStrategy);
+
+ getContext().getSceneGraph().setGlobalProperty(G2DSceneGraph.PICK_DISTANCE, getPickDistance());
+ getHintStack().addKeyHintListener(KEY_PICK_DISTANCE, new HintListenerAdapter() {
+ @Override
+ public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+ getContext().getSceneGraph().setGlobalProperty(G2DSceneGraph.PICK_DISTANCE, getPickDistance());
+ }
+ });
}
@EventHandler(priority = 0)
return null;
double pd = getPickDistance();
- Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2+1, pd*2+1);
+ Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2, pd*2);
Shape canvasShape = GeometryUtils.transformShape(controlPickRect, inverse);
return canvasShape;
}
assertDependencies();
- // Pick Terminal
- double pickDist = getPickDistance();
- Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
- Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+ Shape canvasPickRect = getCanvasPickShape(me.controlPosition);
int selectionId = me.mouseId;
PickRequest req = new PickRequest(canvasPickRect).context(getContext());
req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
- req.pickSorter = pickSorter;
+ req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY());
+
//req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST;
List<IElement> pickables = new ArrayList<IElement>();
pickContext.pick(diagram, req, pickables);
if (getToolMode() != Hints.POINTERTOOL) return false;
if (me.clickCount < 2) return false;
- // Pick Terminal
- double pickDist = getPickDistance();
- Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
- Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+ Shape canvasPickRect = getCanvasPickShape(me.controlPosition);
int selectionId = me.mouseId;
PickRequest req = new PickRequest(canvasPickRect).context(getContext());
req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
+
+ req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY());
List<IElement> pick = new ArrayList<IElement>();
pickContext.pick(diagram, req, pick);
assertDependencies();
Point2D curCanvasPos = util.controlToCanvas(me.controlPosition, curCanvasDragPos);
- PickRequest req = new PickRequest(me.startCanvasPos).context(getContext());
+ Shape canvasPickRect = getCanvasPickShape(me.controlPosition);
+ PickRequest req = new PickRequest(canvasPickRect).context(getContext());
req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
+ req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY());
List<IElement> picks = new ArrayList<IElement>();
pickContext.pick(diagram, req, picks);
- //System.out.println(picks);
- if (picks.isEmpty()) {
- // Widen the area of searching if nothing is found with point picking
- double pickDist = getPickDistance();
- Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
- Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
- req = new PickRequest(canvasPickRect).context(getContext());
- req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
- pickContext.pick(diagram, req, picks);
- //System.out.println("2nd try: " + picks);
- }
-
Set<IElement> sel = selection.getSelection(me.mouseId);
IElement topMostPick = picks.isEmpty() ? null : picks.get(picks.size() - 1);
Set<IElement> elementsToDrag = new HashSet<IElement>();
getContext().add(tm);
return !onlyConnections;
}
+ } else {
+ // forward MouseDragBegin to closest RouteGraphNode
+ for (int i = picks.size() - 1; i >= 0; i--) {
+ RouteGraphNode rgn = picks.get(i).getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+ if (rgn != null) {
+ rgn.handleDrag(me);
+ break;
+ }
+ }
}
}
}
* Contributors:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
-package org.simantics.diagram.connection;
+package org.simantics.g2d.elementclass;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.Collections;
+import org.simantics.diagram.connection.RouteGraph;
import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;
import org.simantics.g2d.connection.ConnectionEntity;
import org.simantics.g2d.connection.handler.ConnectionHandler;
@Override
public boolean pickTest(IElement e, Shape s, PickPolicy policy) {
+ RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
+ if (rgn == null) {
+ return false;
+ }
RouteGraph rg = getRouteGraph(e);
if (rg == null)
return false;
if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION))
tolerance = getTolerance(e);
else
- tolerance = (bounds.getHeight()+bounds.getHeight()) * 0.25;
+ tolerance = Math.max((bounds.getHeight()+bounds.getHeight()) * 0.25, rgn.getSelectionStrokeWidth() / 2);
Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance);
return node != null;
}
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
import org.simantics.databoard.Bindings;
import org.simantics.databoard.Datatypes;
+import org.simantics.databoard.binding.Binding;
import org.simantics.databoard.binding.error.RuntimeDatatypeConstructionException;
import org.simantics.databoard.binding.mutable.Variant;
import org.simantics.databoard.container.DataContainer;
/**
* It is recommended to use {@link #read(File)} and {@link #read(InputStream)}
* for reading to ensure proper resource handling.
+ *
+ * <p>
+ * It is important to use the correct setting for
+ * {@link #sharedValueContext(boolean)} which depends on how the TG was
+ * serialized to begin with. See {@link #DEFAULT_SHARED_VALUE_CONTEXT} for more
+ * information on this.
*/
final public class TransferableGraphFileReader extends ByteFileReader {
+ /**
+ * Serializing TransferableGraph1 structures using the default {@link Binding}
+ * will use shared context for serializing the values Variant array. Thus all TG
+ * files produced by the graph compiler use a shared value context which means
+ * this class must be used with {@link #sharedValueContext(boolean)} set to
+ * true. As an example, <code>true</code> must be used if the corresponding TG
+ * file is written e.g. like this:
+ * <pre>
+ * DataContainers.writeFile(location
+ * new DataContainer(format,
+ * version,
+ * metadata,
+ * new Variant(TransferableGraph1.BINDING, tg)));
+ * </pre>
+ *
+ * <p>
+ * On the other hand, any TG files serialized using more optimized methods like
+ * <code>ModelTransferableGraphSource</code> do not use a shared value context
+ * when writing the file. This means those files cannot be read safely using
+ * standard {@link Binding} at all, and when using this class,
+ * {@link #sharedValueContext(boolean)} must be set to false to prevent the
+ * import from corrupting datatype values because the referable parts of
+ * datatypes may get bound to the wrong existing types if the data is read using
+ * shared context.
+ *
+ * <p>
+ * <code>true</code> is the default setting.
+ */
+ public static final boolean DEFAULT_SHARED_VALUE_CONTEXT = true;
+
private static final Logger LOGGER = LoggerFactory.getLogger(TransferableGraphFileReader.class);
InputStream in = new InputStream() {
final private static int SIZE = 1<<18;
final private static int HEADER = headerSize();
final private int header;
+ private boolean shareValueContext = true;
/**
* Reads a {@link DataContainer} containing a {@link TransferableGraph1}
* @throws IOException
*/
public static TransferableGraph1 read(File file) throws IOException {
+ return read(file, DEFAULT_SHARED_VALUE_CONTEXT);
+ }
+
+ /**
+ * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
+ * structure from the specified InputStream. Note that this implementation does
+ * not close the specified <code>input</code> stream, it is expected to be
+ * closed by the caller.
+ *
+ * @param input the input stream to read from
+ * @return the TG contained by the stream
+ * @throws IOException
+ */
+ public static TransferableGraph1 read(InputStream input) throws IOException {
+ return read(input, DEFAULT_SHARED_VALUE_CONTEXT);
+ }
+
+ /**
+ * Reads a {@link DataContainer} containing a {@link TransferableGraph1}
+ * structure from the specified {@link File}.
+ *
+ * @param file the file to read from
+ * @return the TG contained by the file
+ * @throws IOException
+ */
+ public static TransferableGraph1 read(File file, boolean sharedValueContext) throws IOException {
try (TransferableGraphFileReader reader = new TransferableGraphFileReader(file)) {
- return reader.readTG();
+ return reader.sharedValueContext(sharedValueContext).readTG();
}
}
* @return the TG contained by the stream
* @throws IOException
*/
- public static TransferableGraph1 read(InputStream input) throws IOException {
+ public static TransferableGraph1 read(InputStream input, boolean sharedValueContext) throws IOException {
try (TransferableGraphFileReader reader = new TransferableGraphFileReader(input)) {
- return reader.readTG();
+ return reader.sharedValueContext(sharedValueContext).readTG();
}
}
this.header = HEADER;
}
+ public TransferableGraphFileReader sharedValueContext(boolean share) {
+ this.shareValueContext = share;
+ return this;
+ }
+
private static int headerSize() {
try {
return Bindings.getSerializerUnchecked(Datatype.class).serialize(Datatypes.getDatatypeUnchecked(TransferableGraph1.class)).length;
throw new Error("Failed to determine TransferableGraph1 header size. ", e);
}
}
-
+
public TransferableGraph1 readTG() throws IOException {
if(getSize() == 0) return null;
for(int i=0;i<valueLength;i++) {
int resource = safeInt();
- //idcontext.clear();
+ if (!shareValueContext)
+ idcontext.clear();
Variant value = (Variant)variantSerializer
.deserialize((DataInput)dis, idcontext);
values[i] = new Value(resource, value);
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.variable.Variable;
import org.simantics.diagram.adapter.RouteGraphUtils;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
import org.simantics.diagram.connection.RouteTerminal;
import org.simantics.diagram.elements.TextNode;
import org.simantics.diagram.profile.StyleBase;
import org.simantics.diagram.synchronization.graph.BasicResources;
import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.g2d.utils.Alignment;
import org.simantics.layer0.Layer0;
import org.simantics.modeling.ModelingResources;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.variable.Variable;
import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
import org.simantics.diagram.connection.RouteTerminal;
import org.simantics.diagram.content.ConnectionUtil;
import org.simantics.diagram.handler.Paster;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.handler.SelectionOutline;
import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
import org.simantics.modeling.ModelingResources;
import org.simantics.scenegraph.INode;
import org.simantics.scenegraph.ParentNode;
import org.simantics.scl.runtime.function.Function4;
import org.simantics.structural.stubs.StructuralResource2;
import org.simantics.structural2.utils.StructuralUtils;
-import org.simantics.ui.SimanticsUI;
import org.simantics.utils.datastructures.Pair;
import org.simantics.utils.ui.dialogs.ShowMessage;
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.platform.ui</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Platform UI Components
+Bundle-SymbolicName: org.simantics.platform.ui
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: Semantum Oy
+Automatic-Module-Name: org.simantics.platform.ui
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.ui.console;bundle-version="3.7.0",
+ org.eclipse.ui;bundle-version="3.109.0",
+ org.eclipse.core.jobs;bundle-version="3.9.0",
+ org.eclipse.jface.text;bundle-version="3.12.0",
+ org.eclipse.core.runtime;bundle-version="3.13.0",
+ org.simantics.ui,
+ org.slf4j.api,
+ ch.qos.logback.core,
+ ch.qos.logback.classic
+Bundle-ActivationPolicy: lazy
+Bundle-Activator: org.simantics.platform.ui.internal.Activator
+Export-Package: org.simantics.platform.ui
--- /dev/null
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .\r
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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.platform.ui;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+public class SimanticsConsole extends MessageConsole {
+
+ // We don't want to constantly lose data, but this is still only half MB
+ private static int HIGH_WATERMARK = 2<<18;
+ // 75%
+ private static int LOW_WATERMARK = 3 * (HIGH_WATERMARK>>2);
+
+ private ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ private Color black;
+ private Color red;
+ private Color green;
+ private Color yellow;
+ private Color blue;
+ private Color magenta;
+ private Color cyan;
+ private Color white;
+
+ private MessageConsoleStream stream;
+
+ public SimanticsConsole() {
+ super("Simantics Console", "Simantics Console", null, true);
+ }
+
+ @Override
+ protected void init() {
+ Display d = Display.getDefault();
+
+ black = tuneColor(d, SWT.COLOR_BLACK);
+ red = tuneColor(d, SWT.COLOR_RED);
+ green = tuneColor(d, SWT.COLOR_GREEN);
+ yellow = tuneColor(d, SWT.COLOR_YELLOW);
+ blue = tuneColor(d, SWT.COLOR_BLUE);
+ magenta = tuneColor(d, SWT.COLOR_MAGENTA);
+ cyan = tuneColor(d, SWT.COLOR_CYAN);
+ white = tuneColor(d, SWT.COLOR_WHITE);
+
+ setBackground(black);
+ stream = newMessageStream();
+ stream.setFontStyle(SWT.NORMAL);
+ stream.setColor(white);
+
+ setWaterMarks(LOW_WATERMARK, HIGH_WATERMARK);
+
+ }
+
+ @Override
+ protected void dispose() {
+ super.dispose();
+ executor.shutdown();
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // nothing to do
+ }
+
+ black.dispose();
+ red.dispose();
+ green.dispose();
+ green.dispose();
+ yellow.dispose();
+ blue.dispose();
+ magenta.dispose();
+ cyan.dispose();
+ white.dispose();
+ }
+
+ private Color tuneColor(Display d, int color) {
+ Color c = d.getSystemColor(color);
+ return new Color(d, (int)(c.getRed() * 0.75), (int)(c.getGreen() * 0.75), (int)(c.getBlue() * 0.75));
+ }
+
+ public static SimanticsConsole show() {
+
+ if(!Thread.currentThread().equals(Display.getDefault().getThread())) {
+ Display.getDefault().asyncExec(() -> show());
+ return null;
+ }
+
+ for (IConsole c : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) {
+ if (c instanceof SimanticsConsole) {
+ SimanticsConsole sc = (SimanticsConsole) c;
+ sc.activate();
+ return sc;
+ }
+ }
+
+ SimanticsConsole sc = new SimanticsConsole();
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new SimanticsConsole[] {sc});
+ return sc;
+
+ }
+
+ public static SimanticsConsole findConsole() {
+
+ for (IConsole c : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) {
+ if (c instanceof SimanticsConsole) {
+ return (SimanticsConsole) c;
+ }
+ }
+
+ SimanticsConsole sc = new SimanticsConsole();
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new SimanticsConsole[] {sc});
+ return sc;
+
+ }
+
+ public void write(String data) {
+ executor.submit(() -> {
+ Pattern p = Pattern.compile("(\\x1b\\[(?<cmd>.*?(m)))");
+ Matcher m = p.matcher(data);
+ int pos = 0;
+ while (m.find()) {
+ if (m.start() > pos) {
+ stream.print(data.substring(pos, m.start()));
+ }
+ pos = m.end();
+
+ String cmd = m.group("cmd");
+ if (cmd.endsWith("m")) {
+ // Stream style can be set only in UI thread
+ Display.getDefault().syncExec(() -> {
+ int fontStyle = stream.getFontStyle();
+ Color color = stream.getColor();
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // nothing to do
+ }
+ stream = newMessageStream();
+
+ String attributes[] = cmd.substring(0, cmd.length() - 1).split(";");
+ for (String attribute : attributes) {
+ switch (Integer.parseInt(attribute)) {
+ case 0: fontStyle = SWT.NORMAL; break;
+ case 1: fontStyle = SWT.BOLD; break;
+ case 4: break; // underline
+ case 5: break; // blink
+ case 7: break; // reverse video
+ case 8: break; // nondisplayed
+ case 30: color = black; break;
+ case 31: color = red; break;
+ case 32: color = green; break;
+ case 33: color = yellow; break;
+ case 34: color = blue; break;
+ case 35: color = magenta; break;
+ case 36: color = cyan; break;
+ case 37: color = white; break;
+ // background colors not supported
+ default:break;
+ }
+ }
+ stream.setColor(color);
+ stream.setFontStyle(fontStyle);
+ });
+ }
+ }
+ if (pos < data.length()) {
+ stream.print(data.substring(pos));
+ }
+ stream.println();
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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.platform.ui.internal;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.simantics.UnhandledExceptionService;
+import org.simantics.platform.ui.SimanticsConsole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator extends AbstractUIPlugin {
+
+ public Activator() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+
+ super.start(context);
+
+ SimanticsConsole console = SimanticsConsole.findConsole();
+ if(console != null) {
+ ServiceReference<?> ref = context.getServiceReference(UnhandledExceptionService.class.getName());
+ if (ref != null) {
+ UnhandledExceptionService service = (UnhandledExceptionService) context.getService(ref);
+ service.registerHandler(t -> {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ console.write(sw.toString());
+ });
+ }
+ ConsoleAppender ca = new ConsoleAppender(console);
+ ch.qos.logback.classic.Logger logbackLogger =
+ (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ logbackLogger.addAppender(ca);
+ ca.start();
+ }
+
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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.platform.ui.internal;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.simantics.platform.ui.SimanticsConsole;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+public class ConsoleAppender extends AppenderBase<ILoggingEvent> {
+
+ final private SimanticsConsole console;
+
+ final private DateFormat formatter;
+
+ ConsoleAppender(SimanticsConsole console) {
+ assert(console != null);
+ this.console = console;
+ formatter = new SimpleDateFormat("dd.LL.yyyy HH:mm:ss.SSS");
+ formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ @Override
+ protected void append(ILoggingEvent e) {
+ StringBuilder b = new StringBuilder();
+ b.append('[');
+ b.append(formatter.format(new Date(e.getTimeStamp())));
+ b.append("]: ");
+ b.append(e.getMessage());
+ console.write(b.toString());
+ }
+
+}
\ No newline at end of file
private static final long serialVersionUID = -7066146333849901429L;
public static final String IGNORE_FOCUS = "ignoreFocus";
+ public static final String PICK_DISTANCE = "pickDistance";
protected transient Container rootPane = null;
// TODO: swing dependency in here might not be a good idea
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Constructor;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.simantics.scenegraph.ISelectionPainterNode;
import org.simantics.scenegraph.g2d.G2DNode;
import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
import org.simantics.scenegraph.g2d.IG2DNode;
import org.simantics.scenegraph.g2d.events.EventTypes;
import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
import org.simantics.scenegraph.g2d.events.command.Commands;
import org.simantics.scenegraph.g2d.nodes.GridNode;
import org.simantics.scenegraph.g2d.nodes.LinkNode;
+import org.simantics.scenegraph.g2d.nodes.NavigationNode;
import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Action;
import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Pick;
import org.simantics.scenegraph.utils.GeometryUtils;
import org.simantics.scenegraph.utils.InitValueSupport;
import org.simantics.scenegraph.utils.NodeUtil;
-import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import gnu.trove.map.hash.THashMap;
return null;
}
- private double getSelectionStrokeWidth() {
+ public double getSelectionStrokeWidth() {
if (selectionStroke instanceof BasicStroke) {
BasicStroke bs = (BasicStroke) selectionStroke;
return bs.getLineWidth();
@Override
protected boolean mouseDragged(MouseDragBegin e) {
+ // Consume event if drag is possible.
+ // PointerInteractor will call handleDrag with the MouseDragBegin event for the route line that is closest to the cursor.
+ return currentAction != null;
+ }
+
+ public boolean handleDrag(MouseDragBegin e) {
if (dragAction != null && !e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.button == MouseEvent.LEFT_BUTTON) {
currentAction = dragAction;
dragAction = null;
}
}
if (newBranchPointPosition != null) {
- RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
+ RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance());
if (line != null) {
newBranchPointPosition.setLocation(mouseX, mouseY);
SplittedRouteGraph.snapToLine(newBranchPointPosition, line);
return false;
}
//System.out.println("move action");
- dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, pickTolerance, moveFilter, getSnapAdvisor());
+ dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, scaledPickTolerance(), moveFilter, getSnapAdvisor());
//System.out.println("DRAG ACTION: " + dragAction);
}
return false;
}
+ private double scaledPickTolerance() {
+ NavigationNode nn = NodeUtil.findNearestParentNode(this, NavigationNode.class);
+ double scale = 1.0;
+ if (nn != null) {
+ scale = GeometryUtils.getScale(nn.getTransform());
+ }
+ double pickDistance = 0;
+ G2DSceneGraph sg = NodeUtil.getRootNode(nn != null ? nn : this);
+ if (sg != null) {
+ pickDistance = sg.getGlobalProperty(G2DSceneGraph.PICK_DISTANCE, pickDistance);
+ }
+ return Math.max(getSelectionStrokeWidth() / 2.0, pickDistance / scale);
+ }
+
/**
* Checks the selections data node in the scene graph for any links
* @return
return false;
if (!e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.keyCode == KeyEvent.VK_S) {
- Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
+ Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
return splitTarget(target);
}
else if (!e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK | MouseEvent.CTRL_MASK) && (e.keyCode == KeyEvent.VK_R || e.keyCode == KeyEvent.VK_D)) {
- Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES);
+ Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES);
return deleteTarget(target);
}
else if (e.keyCode == KeyEvent.VK_ESCAPE) {
}
else if (e.keyCode == KeyEvent.VK_ALT) {
// Begin connection branching visualization.
- RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
+ RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance());
if (branchable && line != null) {
newBranchPointPosition = new Point2D.Double(mouseX, mouseY);
SplittedRouteGraph.snapToLine(newBranchPointPosition, line);
package org.simantics.structural.synchronization.utils;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import gnu.trove.map.hash.THashMap;
import gnu.trove.procedure.TObjectObjectProcedure;
import gnu.trove.procedure.TObjectProcedure;
import gnu.trove.set.hash.THashSet;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.function.Consumer;
-
/**
* The entry point to the mapping structure between Simantics database and a
* designated solver. It is used to synchronize changes from Simantics to the
*/
abstract public class MappingBase<T extends ComponentBase<T>> {
+ private final transient Logger LOGGER = LoggerFactory.getLogger(getClass());
+
abstract public T getConfiguration();
/**
public Map<String, T> getConfigurationBySolverName() {
Map<String, T> result = configurationBySolverName;
- if (result == null)
- result = configurationBySolverName = createConfigurationBySolverName(getConfiguration());
+ if (result == null) {
+ T configuration = getConfiguration();
+ if (configuration != null)
+ result = configurationBySolverName = createConfigurationBySolverName(configuration);
+ else
+ result = Collections.emptyMap();
+ }
return result;
}
private void browseConfigurationBySolverName(
THashMap<String, T> configurationBySolverName,
T configuration) {
- configurationBySolverName.put(configuration.solverComponentName, configuration);
+ if (configuration.solverComponentName != null) {
+ configurationBySolverName.put(configuration.solverComponentName, configuration);
+ } else if (configuration.componentId != 0) {
+ LOGGER.warn("configuration.solverComponentName is null! configuration uid is {} and component id {}", configuration.getUid(), configuration.componentId);
+ }
for(T child : configuration.getChildren()) {
browseConfigurationBySolverName(configurationBySolverName, child);
child.parent = configuration;
if(result == null) {
result = componentFactory.create(uid);
configurationByUid.put(uid, result);
+ configurationBySolverName = null; // forces recalculation
}
else {
if(result.getParent() == null)
public void remove(final Solver solver, T component) {
if(configurationByUid != null)
configurationByUid.remove(component.uid);
- if (configurationBySolverName != null)
+ if (configurationBySolverName != null && component.solverComponentName != null)
configurationBySolverName.remove(component.solverComponentName);
if(component.getChildMap() != null)
component.getChildMap().forEachValue(new TObjectProcedure<T>() {
import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
import org.simantics.scl.compiler.types.Type;
import org.simantics.structural2.scl.AbstractCompileStructuralValueRequest.CompilationContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Compiles an SCL expression that is attached to a literal
*/
public abstract class AbstractCompileStructuralValueRequest extends AbstractExpressionCompilationRequest<CompilationContext, Object> {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCompileStructuralValueRequest.class);
+
protected final Resource relation;
public static class CompilationContext extends AbstractExpressionCompilationContext {
@Override
abstract protected String getExpressionText(ReadGraph graph) throws DatabaseException;
+
+ /*
+ * @throws DatabaseException is an index root could not be found
+ */
abstract protected Resource getIndexRoot(ReadGraph graph) throws DatabaseException;
+ /*
+ * Does not throw if the graph is valid.
+ *
+ * @return null if component type was not availble
+ *
+ */
abstract protected Resource getComponentType(ReadGraph graph) throws DatabaseException;
+
+ protected Resource getPossibleIndexRoot(ReadGraph graph) throws DatabaseException {
+ try {
+ return getIndexRoot(graph);
+ } catch (DatabaseException e) {
+ LOGGER.error("Error while resolving index root during structural value compilation", e);
+ return null;
+ }
+ }
@Override
protected Type getExpectedType(ReadGraph graph, CompilationContext context)
return null;
}
+ @Override
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ Resource componentType = getComponentType(graph);
+ if(componentType != null) {
+ String uri = graph.getPossibleURI(componentType);
+ if(uri != null)
+ return uri;
+ }
+ // OK, no component type - report index root then
+ Resource indexRoot = getPossibleIndexRoot(graph);
+ if(indexRoot != null) {
+ String uri = graph.getPossibleURI(componentType);
+ if(uri != null)
+ return uri;
+ }
+ // OK, nothing - report default
+ return super.getContextDescription(graph);
+ }
+
}
return true;
}
+ @Override
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ if(component != null) {
+ String uri = graph.getPossibleURI(component);
+ if(uri != null) {
+ String propertyName = NameUtils.getSafeName(graph, relation);
+ return uri + "#" + propertyName;
+ }
+ }
+ return super.getContextDescription(graph);
+ }
+
}
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
+import org.simantics.internal.UnhandledExceptionServiceImpl;
import org.simantics.ui.workbench.WorkbenchShutdownServiceImpl;
/**
public void start(BundleContext context) throws Exception {
WorkbenchShutdownServiceImpl.initialize(context);
+ UnhandledExceptionServiceImpl.initialize(context);
super.start(context);
public void stop(BundleContext context) throws Exception {
WorkbenchShutdownServiceImpl.close();
+ UnhandledExceptionServiceImpl.close();
super.stop(context);
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.statushandlers.AbstractStatusHandler;
+import org.eclipse.ui.statushandlers.StatusAdapter;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.simantics.CancelStartupException;
import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy;
import org.simantics.SimanticsPlatform.RecoveryPolicy;
import org.simantics.TimingProgressMonitor;
+import org.simantics.UnhandledExceptionService;
import org.simantics.application.arguments.IArguments;
import org.simantics.application.arguments.SimanticsArguments;
import org.simantics.db.common.Indexing;
import org.simantics.ui.jobs.SessionGarbageCollectorJob;
import org.simantics.ui.workbench.PerspectiveBarsActivator;
import org.simantics.ui.workbench.PerspectiveContextActivator;
+import org.simantics.ui.workbench.WorkbenchShutdownService;
import org.simantics.utils.logging.TimeLogger;
import org.simantics.utils.ui.dialogs.ShowError;
import org.simantics.utils.ui.dialogs.ShowMessage;
* location is not being shown
*/
public String getWorkspaceLocation() {
- // read command line, which has priority
- IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class);
- String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null;
- if (location != null) {
- return location;
- }
- // read the preference
- if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) {
- return Platform.getLocation().toOSString();
- }
- return null;
+ // read command line, which has priority
+ IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class);
+ String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null;
+ if (location != null) {
+ return location;
+ }
+ // read the preference
+ if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) {
+ return Platform.getLocation().toOSString();
+ }
+ return null;
}
/**
*
* @see org.eclipse.ui.application.WorkbenchAdvisor#getWorkbenchErrorHandler()
*/
+
+ private AbstractStatusHandler workbenchErrorHandler = new AbstractStatusHandler() {
+
+ @Override
+ public void handle(StatusAdapter statusAdapter, int style) {
+ if (ideWorkbenchErrorHandler == null) {
+ ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler(
+ getWorkbenchConfigurer());
+ }
+ ideWorkbenchErrorHandler.handle(statusAdapter, style);
+
+ BundleContext context = Activator.getDefault().getBundle().getBundleContext();
+ ServiceReference<?> ref = context.getServiceReference(UnhandledExceptionService.class.getName());
+ UnhandledExceptionService unhandled = (UnhandledExceptionService)context.getService(ref);
+ Throwable t = statusAdapter.getStatus().getException();
+ if(t != null)
+ unhandled.handle(t);
+
+ }
+ };
+
@Override
public AbstractStatusHandler getWorkbenchErrorHandler() {
- if (ideWorkbenchErrorHandler == null) {
- ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler(
- getWorkbenchConfigurer());
- }
- return ideWorkbenchErrorHandler;
+ return workbenchErrorHandler;
}
/* (non-Javadoc)
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Antti Villberg
+ */
+public interface UnhandledExceptionService {
+
+ void registerHandler(Consumer<Throwable> handler);
+ void handle(Throwable t);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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.internal;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.simantics.UnhandledExceptionService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Antti Villberg
+ */
+public class UnhandledExceptionServiceImpl implements UnhandledExceptionService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(UnhandledExceptionServiceImpl.class);
+ private static ServiceRegistration<?> service = null;
+
+ private final List<Consumer<Throwable>> handlers = new ArrayList<>();
+
+ /**
+ * Invoked by the bundle activator to initialize the cache service.
+ *
+ * @param context the bundle context to register the service with
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public synchronized static void initialize(BundleContext context) {
+ if (service == null) {
+ service = context.registerService(UnhandledExceptionService.class.getName(), new UnhandledExceptionServiceImpl(), new Hashtable());
+ }
+ }
+
+ /**
+ * Invoked by the bundle activator to close the cache service.
+ */
+ public synchronized static void close() {
+ if (service != null) {
+ service.unregister();
+ service = null;
+ }
+ }
+
+ @Override
+ public synchronized void registerHandler(Consumer<Throwable> handler) {
+ handlers.add(handler);
+ }
+
+ @Override
+ public synchronized void handle(Throwable t) {
+ for (Consumer<Throwable> handler : handlers) {
+ try {
+ handler.accept(t);
+ } catch (Exception e) {
+ handleException(handler, e);
+ } catch (LinkageError e) {
+ handleException(handler, e);
+ } catch (AssertionError e) {
+ handleException(handler, e);
+ }
+ }
+ }
+
+ protected void handleException(Object source, Throwable t) {
+ LOGGER.error("{}: Unhandled exception handler {} caused unexpected exception", getClass().getSimpleName(), source, t);
+ }
+
+}
<module>org.simantics.msvc.runtime.x86_64</module>
<module>org.simantics.nativemem</module>
<module>org.simantics.objmap2</module>
+ <module>org.simantics.platform.ui</module>
<module>org.simantics.platform.ui.ontology</module>
<module>org.simantics.project</module>
<module>org.simantics.project.ontology</module>
version="0.0.0"
unpack="false"/>
+ <plugin
+ id="org.simantics.platform.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
</feature>