1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.diagram.style;
14 import java.awt.geom.AffineTransform;
15 import java.awt.geom.Point2D;
16 import java.awt.geom.Rectangle2D;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.List;
25 import org.simantics.db.ReadGraph;
26 import org.simantics.db.Resource;
27 import org.simantics.db.common.procedure.adapter.TransientCacheListener;
28 import org.simantics.db.exception.DatabaseException;
29 import org.simantics.db.layer0.variable.Variable;
30 import org.simantics.db.layer0.variable.Variables;
31 import org.simantics.diagram.connection.RouteGraph;
32 import org.simantics.diagram.elements.DecorationSVGNode;
33 import org.simantics.diagram.elements.SVGNode;
34 import org.simantics.diagram.handler.Paster;
35 import org.simantics.diagram.handler.Paster.RouteLine;
36 import org.simantics.diagram.profile.StyleBase;
37 import org.simantics.diagram.stubs.DiagramResource;
38 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
39 import org.simantics.issues.Severity;
40 import org.simantics.issues.common.IssueResourcesContexts;
41 import org.simantics.issues.common.ListModelIssuesBySeverity;
42 import org.simantics.modeling.ModelingResources;
43 import org.simantics.modeling.ui.Activator;
44 import org.simantics.modeling.ui.diagram.style.IssueDecorationStyle.IssueResult;
45 import org.simantics.scenegraph.INode;
46 import org.simantics.scenegraph.g2d.nodes.ConnectionNode;
47 import org.simantics.scenegraph.g2d.nodes.Decoration;
48 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
49 import org.simantics.scenegraph.profile.EvaluationContext;
50 import org.simantics.scenegraph.profile.common.ProfileVariables;
51 import org.simantics.scenegraph.utils.NodeUtil;
52 import org.simantics.structural.stubs.StructuralResource2;
53 import org.simantics.utils.datastructures.map.Tuple;
57 * @author Tuukka Lehtonen
59 public class IssueDecorationStyle extends StyleBase<IssueResult> {
61 private static final String DECORATION_NODE_NAME = "issueDecorations"; //$NON-NLS-1$
63 private List<Resource> getContexts(ReadGraph graph, Resource element) throws DatabaseException {
65 ModelingResources MOD = ModelingResources.getInstance(graph);
66 List<Resource> result = new ArrayList<Resource>(3);
68 Resource config = graph.getPossibleObject(element, MOD.ElementToComponent);
69 if (config != null && result.indexOf(config) == -1) result.add(config);
70 config = graph.getPossibleObject(element, MOD.DiagramConnectionToConnection);
71 if (config != null && result.indexOf(config) == -1) result.add(config);
72 // For diagram reference element support
73 config = graph.getPossibleObject(element, MOD.HasParentComponent);
74 if (config != null && result.indexOf(config) == -1) result.add(config);
80 public IssueResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {
82 Resource model = Variables.getModel(graph, configuration);
86 List<Resource> contexts = getContexts(graph, element);
87 Map<Severity, List<Resource>> issuesBySeverity = graph.syncRequest(new ListModelIssuesBySeverity(model, true, true, Severity.NOTE),
88 TransientCacheListener.<Map<Severity, List<Resource>>>instance());
90 for (Severity severity : Severity.values()) {
91 List<Resource> issues = issuesBySeverity.get(severity);
93 Set<Resource> issueContexts = graph.syncRequest(new IssueResourcesContexts(issues));
94 if (!Collections.disjoint(issueContexts, contexts)) {
95 return new IssueResult(severity, getIdentifier(graph, runtimeDiagram, element));
103 private static Object getIdentifier(ReadGraph graph, Resource runtimeDiagram, Resource element) throws DatabaseException {
104 DiagramResource DIA = DiagramResource.getInstance(graph);
105 StructuralResource2 STR = StructuralResource2.getInstance(graph);
106 if (graph.isInstanceOf(element, DIA.RouteGraphConnection)) {
107 Collection<Resource> connectors = graph.getObjects(element, DIA.HasConnector);
108 Collection<Resource> routeNodes = graph.getObjects(element, DIA.HasInteriorRouteNode);
110 // This is needed to make this query result change every time the underlying element changes visually.
111 Set<Object> identifier = new HashSet<Object>(connectors.size() + routeNodes.size());
113 for (Resource connector : connectors) {
114 for (Resource connectedTo : graph.getObjects(connector, STR.Connects)) {
115 if (!connectedTo.equals(element)) {
116 AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, connectedTo, DIA.HasDynamicTransform, false);
121 for (Resource routeLine : routeNodes) {
122 RouteLine rl = Paster.readRouteLine(graph, routeLine);
127 return DiagramGraphUtil.getAffineTransform(graph, element);
132 public void applyStyleForNode(EvaluationContext observer, INode node, IssueResult result) {
133 if (result == null) {
134 ProfileVariables.denyChild(node, "", DECORATION_NODE_NAME); //$NON-NLS-1$
138 SVGNode svgNode = ProfileVariables.claimChild(node, "", DECORATION_NODE_NAME, DecorationSVGNode.class, observer); //$NON-NLS-1$
140 svgNode.setZIndex( Integer.MAX_VALUE );
141 svgNode.setTransform(getDecorationPosition(node));
143 String svgData = svgDataForSeverity(result.getSeverity());
145 svgNode.setData(svgData);
149 * Returns position of the decoration.
150 * By default decoration is placed to the top left corner. Override this method to change the position.
155 protected AffineTransform getDecorationPosition(INode node) {
156 Rectangle2D bounds = NodeUtil.getLocalBounds(node, Decoration.class);
158 if (node instanceof ConnectionNode) {
159 for (INode child : ((ConnectionNode)node).getSortedNodes()) {
160 if (child instanceof RouteGraphNode) {
161 RouteGraphNode rgn = (RouteGraphNode) child;
162 RouteGraph rg = rgn.getRouteGraph();
163 Point2D nearest = rg.findNearestPoint(bounds.getCenterX(), bounds.getCenterY());
164 if (nearest != null) {
165 return AffineTransform.getTranslateInstance(nearest.getX(), nearest.getY());
170 double tx = bounds.getX();
171 double ty = bounds.getY();
172 return AffineTransform.getTranslateInstance(tx, ty);
175 protected String svgDataForSeverity(Severity s) {
177 case FATAL: return Activator.FATAL_SVG_TEXT;
178 case ERROR: return Activator.ERROR_SVG_TEXT;
179 case WARNING: return Activator.WARNING_SVG_TEXT;
180 case INFO: return Activator.INFO_SVG_TEXT;
181 case NOTE: return Activator.NOTE_SVG_TEXT;
182 default: return null;
187 protected void cleanupStyleForNode(INode node) {
188 ProfileVariables.denyChild(node, "", DECORATION_NODE_NAME); //$NON-NLS-1$
192 public String toString() {
193 return "Issue decoration"; //$NON-NLS-1$
197 * This is needed to keep the issue decoration up-to-date when its parent
200 public static class IssueResult extends Tuple {
201 public IssueResult(Severity severity, Object identifier) {
202 super(severity, identifier);
204 public Severity getSeverity() {
205 return (Severity) getField(0);