+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DistrictNetworkHoverInfoStyle {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkHoverInfoStyle.class);
+
+ private static final String ACTIONS_MODULE = "Actions";
+ private static final String HOVER_CONTRIBUTION = "hoverContribution";
+
+ public static class StyleResult {
+ Point2D origin;
+ List<Tuple3> labels;
+
+ public StyleResult(Point2D origin, List<Tuple3> labels) {
+ this.origin = origin;
+ this.labels = labels;
+ }
+
+ public Point2D getOrigin() {
+ return origin;
+ }
+
+ public List<Tuple3> getLabels() {
+ return labels;
+ }
+ }
+
+ public static StyleResult doCalculateStyleResult(ReadGraph graph, Resource runtimeDiagram, Resource mapElement) throws DatabaseException {
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);
+
+ String variableURI = graph.getPossibleRelatedValue(runtimeDiagram, DIA.RuntimeDiagram_HasVariable,
+ Bindings.STRING);
+ Variable activeVariable = org.simantics.db.layer0.variable.Variables.getPossibleVariable(graph, variableURI);
+ if (activeVariable == null)
+ return null;
+
+ Resource module = DistrictNetworkUtil.getMappedComponentCached(graph, mapElement);
+ if (module == null)
+ return null;
+
+ Resource moduleType = graph.getPossibleType(module, STR.Component);
+ if (moduleType == null)
+ return null;
+
+ Function1<Variable, List<Tuple3>> function = getUCTextGridFunctionCached(graph, moduleType);
+ if (function == null)
+ return null;
+
+ List<Tuple3> result;
+ try {
+ Variable variable = Variables.getVariable(graph, module);
+ Variable moduleVariable = Variables.possibleActiveVariable(graph, variable);
+ if (moduleVariable == null)
+ moduleVariable = variable;
+
+ result = Simantics.applySCLRead(graph, function, moduleVariable);
+ } catch (PendingVariableException | MissingVariableValueException e) {
+ result = Collections.singletonList(new Tuple3("<pending>", "", ""));
+ } catch (MissingVariableException e) {
+ // the requested variable is missing from the UC
+ String message = e.getMessage();
+ LOGGER.warn("Missing variable for calculating style with function {} {}", function, message);
+ result = Collections.singletonList(new Tuple3("<" + message + ">", "", ""));
+ }
+
+ Point2D point = calculatePoint(graph, mapElement);
+
+ return new StyleResult(point, result);
+ }
+
+ public static Point2D calculatePoint(ReadGraph graph, Resource mapElement) throws DatabaseException {
+ Point2D point;
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+ if (graph.isInstanceOf(mapElement, DN.Vertex)) {
+ double[] coords = graph.getRelatedValue(mapElement, DIA.HasLocation);
+ point = DistrictNetworkNodeUtils.calculatePoint2D(new Point2D.Double(coords[0], coords[1]), null);
+ } else if (graph.isInstanceOf(mapElement, DN.Edge)) {
+ Resource v1 = graph.getSingleObject(mapElement, DN.HasStartVertex);
+ double[] coords1 = graph.getRelatedValue(v1, DIA.HasLocation);
+ Resource v2 = graph.getSingleObject(mapElement, DN.HasEndVertex);
+ double[] coords2 = graph.getRelatedValue(v2, DIA.HasLocation);
+ point = DistrictNetworkNodeUtils.calculatePoint2D(
+ new Point2D.Double((coords1[0] + coords2[0]) / 2, (coords1[1] + coords2[1]) / 2), null);
+ } else {
+ return null;
+ }
+ return point;
+ }
+
+ public static Point2D calculatePoint(INode node, int zoomLevel, Point2D result) {
+ if (node instanceof DistrictNetworkVertexNode) {
+ DistrictNetworkVertexNode vertex = (DistrictNetworkVertexNode) node;
+ return DistrictNetworkNodeUtils.calculatePoint2D(vertex.getVertex().getPoint(), result);
+ } else if (node instanceof DistrictNetworkEdgeNode) {
+ DistrictNetworkEdgeNode edge = (DistrictNetworkEdgeNode) node;
+ Point2D cp = edge.getCenterPoint(zoomLevel);
+ if (result == null)
+ result = new Point2D.Double();
+ result.setLocation(cp);
+ return result;
+ }
+ return null;
+ }
+
+ private static Function1<Variable, List<Tuple3>> getUCTextGridFunctionCached(ReadGraph graph, Resource componentType)
+ throws DatabaseException {
+ return graph.syncRequest(new UCTextGridFunctionRequest(componentType), TransientCacheListener.instance());
+ }
+
+ private static final class UCTextGridFunctionRequest extends ResourceRead<Function1<Variable, List<Tuple3>>> {
+ public UCTextGridFunctionRequest(Resource resource) {
+ super(resource);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Function1<Variable, List<Tuple3>> perform(ReadGraph graph) throws DatabaseException {
+ Resource actionsModule = CommonDBUtils.getPossibleChild(graph, resource, ACTIONS_MODULE);
+ if (actionsModule == null || !graph.isInstanceOf(actionsModule, Layer0.getInstance(graph).SCLModule))
+ return null;
+
+ String uri = graph.getURI(actionsModule);
+ SCLContext sclContext = SCLContext.getCurrent();
+ Object oldGraph = sclContext.get("graph");
+ try {
+ sclContext.put("graph", graph);
+ return (Function1<Variable, List<Tuple3>>) SCLOsgi.MODULE_REPOSITORY.getValue(uri, HOVER_CONTRIBUTION);
+ } catch (ValueNotFound e1) {
+ return null;
+ } finally {
+ sclContext.put("graph", oldGraph);
+ }
+ }
+ }