/*******************************************************************************
* Copyright (c) 2007, 2010 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:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
package org.simantics.diagram.synchronization.graph;
import gnu.trove.set.hash.THashSet;
import java.awt.geom.AffineTransform;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.simantics.Simantics;
import org.simantics.databoard.Bindings;
import org.simantics.databoard.binding.Binding;
import org.simantics.datatypes.literal.Vec2d;
import org.simantics.db.AsyncReadGraph;
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
import org.simantics.db.Statement;
import org.simantics.db.WriteGraph;
import org.simantics.db.common.CommentMetadata;
import org.simantics.db.common.primitiverequest.OrderedSet;
import org.simantics.db.common.request.IndexRoot;
import org.simantics.db.common.request.Queries;
import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.common.utils.OrderedSetUtils;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.exception.DoesNotContainValueException;
import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
import org.simantics.db.exception.NoSingleResultException;
import org.simantics.db.exception.ServiceException;
import org.simantics.db.exception.ValidationException;
import org.simantics.db.layer0.request.PossibleModel;
import org.simantics.db.procedure.AsyncProcedure;
import org.simantics.diagram.connection.ConnectionSegmentEnd;
import org.simantics.diagram.connection.ConnectionVisuals;
import org.simantics.diagram.content.ConnectionUtil;
import org.simantics.diagram.content.DesignatedTerminal;
import org.simantics.diagram.content.ElementContext;
import org.simantics.diagram.content.ResourceTerminal;
import org.simantics.diagram.content.TerminalMap;
import org.simantics.diagram.internal.DebugPolicy;
import org.simantics.diagram.query.DiagramRequests;
import org.simantics.diagram.stubs.DiagramResource;
import org.simantics.diagram.stubs.G2DResource;
import org.simantics.g2d.connection.EdgeVisualsConfigurer;
import org.simantics.g2d.diagram.IDiagram;
import org.simantics.g2d.diagram.handler.DataElementMap;
import org.simantics.g2d.diagram.handler.Topology.Terminal;
import org.simantics.g2d.element.ElementHints;
import org.simantics.g2d.element.IElement;
import org.simantics.g2d.element.handler.EdgeVisuals;
import org.simantics.g2d.element.handler.EdgeVisuals.ArrowType;
import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
import org.simantics.g2d.element.handler.FillColor;
import org.simantics.g2d.element.handler.TerminalTopology;
import org.simantics.g2d.elementclass.FlagClass;
import org.simantics.g2d.elementclass.FlagClass.Type;
import org.simantics.g2d.page.DiagramDesc;
import org.simantics.g2d.routing.IRouter2;
import org.simantics.g2d.svg.LineCap;
import org.simantics.g2d.svg.LineJoin;
import org.simantics.g2d.utils.Alignment;
import org.simantics.layer0.Layer0;
import org.simantics.layer0.utils.binaryPredicates.OrderedSetElementsPredicate;
import org.simantics.modeling.ModelingResources;
import org.simantics.scl.commands.Commands;
import org.simantics.structural.stubs.StructuralResource2;
import org.simantics.structural2.modelingRules.CPTerminal;
import org.simantics.structural2.modelingRules.IAttachmentRelationMap;
import org.simantics.structural2.modelingRules.IConnectionPoint;
import org.simantics.structural2.modelingRules.IModelingRules;
import org.simantics.ui.selection.WorkbenchSelectionElement;
import org.simantics.utils.page.MarginUtils.Margin;
import org.simantics.utils.page.MarginUtils.Margins;
import org.simantics.utils.page.PageCentering;
import org.simantics.utils.page.PageDesc;
import org.simantics.utils.page.PageOrientation;
import org.simantics.utils.ui.ErrorLogger;
/**
* @author Tuukka Lehtonen
*/
public final class DiagramGraphUtil {
public static double[] validateAffineTransform(Resource resource, double[] matrix) {
if (matrix != null) {
if (matrix.length < 4) {
ErrorLogger.defaultLogError("resource " + resource + " matrix too small for AffineTransform: " + Arrays.toString(matrix), new Exception("trace"));
return null;
}
// Validate scale/rotation part
if (DebugPolicy.DEBUG_TRANSFORM_LOAD) {
double det = new AffineTransform(matrix).getDeterminant();
double detabs = Math.abs(det);
if (detabs < DebugPolicy.DETERMINANT_LIMIT_LOW)
ErrorLogger.defaultLogWarning("resource " + resource + " transform determinant absolute value is close to zero: " + detabs + "(transform=" + Arrays.toString(matrix) + ")", new Exception("trace"));
if (detabs > DebugPolicy.DETERMINANT_LIMIT_HIGH)
ErrorLogger.defaultLogWarning("resource " + resource + " transform determinant absolute value is suspiciously large: " + detabs + "(transform=" + Arrays.toString(matrix) + ")", new Exception("trace"));
}
if (matrix.length > 5) {
// Validate translation
double xabs = Math.abs(matrix[4]);
double yabs = Math.abs(matrix[5]);
double limit = DebugPolicy.TRANSLATION_LIMIT_HIGH;
boolean largeX = xabs > limit;
boolean largeY = yabs > limit;
if (largeX || largeY)
ErrorLogger.defaultLogWarning("resource " + resource + " transform translation is suspiciously large: " + Arrays.toString(matrix), new Exception("trace"));
return matrix;
}
}
return matrix;
}
public static AffineTransform getAffineTransform(ReadGraph graph, Resource resource) throws DatabaseException {
G2DResource g2d = G2DResource.getInstance(graph);
return getAffineTransform(graph, resource, g2d.HasTransform, true);
}
public static Vec2d getOffset(ReadGraph graph, Resource resource) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
Vec2d offset = graph.getPossibleRelatedValue(resource, DIA.Element_profileMonitorOffset, Vec2d.BINDING);
if(offset != null) return offset;
else return new Vec2d(0, 0);
}
public static boolean getProfileMonitorsHidden(ReadGraph graph, Resource resource) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
Boolean value = graph.getPossibleRelatedValue(resource, DIA.Element_hideProfileMonitors, Bindings.BOOLEAN);
if(value == null) value = false;
return value;
}
public static boolean getProfileMonitorsUp(ReadGraph graph, Resource resource) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
Boolean value = graph.getPossibleRelatedValue(resource, DIA.Element_upProfileMonitors, Bindings.BOOLEAN);
if(value == null) value = true;
return value;
}
public static double getProfileMonitorSpacing(ReadGraph graph, Resource resource) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
Double value = graph.getPossibleRelatedValue(resource, DIA.Element_profileMonitorSpacing, Bindings.DOUBLE);
if(value == null) value = 0.0;
return value;
}
public static AffineTransform getDynamicAffineTransform(ReadGraph graph, Resource runtime, Resource element) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
return getDynamicAffineTransform(graph, runtime, element, DIA.HasDynamicTransform, true);
}
/**
* @param graph
* @param element
* @return
* @throws DatabaseException
*/
public static AffineTransform getWorldTransform(ReadGraph graph, Resource element) throws DatabaseException {
ModelingResources MOD = ModelingResources.getInstance(graph);
AffineTransform result = DiagramGraphUtil.getAffineTransform(graph, element);
while (true) {
Resource parentComponent = graph.getPossibleObject(element, MOD.HasParentComponent);
if (parentComponent == null)
return result;
element = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
if (element == null)
return result;
AffineTransform tr = DiagramGraphUtil.getAffineTransform(graph, element);
tr.setToTranslation(tr.getTranslateX(), tr.getTranslateY());
result.preConcatenate(tr);
}
}
/**
* @param graph
* @param runtime
* @param element
* @return
* @throws DatabaseException
*/
public static AffineTransform getDynamicWorldTransform(ReadGraph graph, Resource runtime, Resource element) throws DatabaseException {
ModelingResources MOD = ModelingResources.getInstance(graph);
AffineTransform result = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, element);
while (true) {
Resource parentComponent = graph.getPossibleObject(element, MOD.HasParentComponent);
if (parentComponent == null)
return result;
element = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
if (element == null)
return result;
AffineTransform tr = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, element);
tr.setToTranslation(tr.getTranslateX(), tr.getTranslateY());
result.preConcatenate(tr);
}
}
/**
* @param graph
* @param resource
* @param relation
* @param invalidAsIdentity true to return invalid transforms as identity
* transforms, false
to return null
* @return
* @throws DatabaseException
*/
public static AffineTransform getAffineTransform(ReadGraph graph, Resource resource, Resource relation, boolean invalidAsIdentity) throws DatabaseException {
double mat[] = getPossibleRelatedDoubleArray(graph, resource, relation);
mat = validateAffineTransform(resource, mat);
return mat != null ? new AffineTransform(mat) :
invalidAsIdentity ? new AffineTransform() : null;
}
public static AffineTransform getDynamicAffineTransform(ReadGraph graph, Resource runtime, Resource element, Resource relation, boolean invalidAsIdentity) throws DatabaseException {
double mat[] = graph.getPossibleRelatedValue2(element, relation, new ElementContext(runtime, element), Bindings.DOUBLE_ARRAY);
mat = validateAffineTransform(element, mat);
return mat != null ? new AffineTransform(mat) :
invalidAsIdentity ? new AffineTransform() : null;
}
public static double[] getPossibleRelatedDoubleArray(ReadGraph graph, Resource resource, Resource relation) throws DatabaseException {
Resource res = graph.getPossibleObject(resource, relation);
if (res == null)
return null;
return graph.getValue(res, Bindings.getBindingUnchecked(double[].class));
}
public static AffineTransform getTransform(ReadGraph graph, Resource resource) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
double[] matrix = graph.getPossibleRelatedValue(resource, DIA.HasTransform, Bindings.DOUBLE_ARRAY);
if (matrix == null)
return new AffineTransform();
if (matrix.length < 4)
return new AffineTransform();
return new AffineTransform(matrix);
}
public static void setTransform(WriteGraph graph, Resource resource, AffineTransform at) throws DatabaseException {
double[] matrix = new double[6];
at.getMatrix(matrix);
changeTransform(graph, resource, matrix);
}
public static void setTransform(WriteGraph graph, Resource resource, double[] matrix) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
G2DResource G2D = G2DResource.getInstance(graph);
setRelatedValue(graph, resource, DIA.HasTransform, G2D.Transform, matrix, Bindings.DOUBLE_ARRAY);
}
public static void changeTransform(WriteGraph graph, Resource resource, AffineTransform at) throws DatabaseException {
double[] matrix = new double[6];
at.getMatrix(matrix);
changeTransform(graph, resource, matrix);
}
public static void changeTransform(WriteGraph graph, Resource resource, double[] matrix) throws DatabaseException {
Commands.get(graph, "Simantics/Diagram/setTransform")
.execute(graph, graph.syncRequest(new IndexRoot(resource)), resource, matrix);
}
public static void setRelatedValue(WriteGraph graph, Resource resource, Resource relation, Resource valueType, Object arrayValue, Binding binding) throws DatabaseException {
Statement stm = graph.getPossibleStatement(resource, relation);
if (stm == null) {
addRelatedValue(graph, resource, relation, valueType, arrayValue, binding);
} else {
// statement might be asserted, check this before overwriting
if (!stm.getSubject().equals(resource)) {
// Asserted, just add a new related value
addRelatedValue(graph, resource, relation, valueType, arrayValue, binding);
} else {
//Object old = graph.getValue2(p);
//if (!Arrays.equals(old, arrayValue))
graph.claimValue(stm.getObject(), arrayValue, binding);
}
}
}
public static Resource addRelatedValue(WriteGraph graph, Resource resource, Resource relation, Resource valueType, Object arrayValue, Binding binding) throws DatabaseException {
Resource d = graph.newResource();
Layer0 b = Layer0.getInstance(graph);
graph.claim(d, b.InstanceOf, null, valueType);
graph.claimValue(d, arrayValue);
graph.claim(resource, relation, d);
return d;
}
public static T getPossibleRelatedValue(ReadGraph graph, Resource r, Resource relation, Class valueClass, T defaultValue) throws DatabaseException {
Resource object = graph.getPossibleObject(r, relation);
if (object == null)
return defaultValue;
T t = graph.getPossibleValue(object, Bindings.getBindingUnchecked(valueClass));
if (t != null && valueClass.isArray()) {
if (defaultValue != null) {
int defaultValueLength = Array.getLength(defaultValue);
int valueLength = Array.getLength(t);
if (valueLength < defaultValueLength)
return defaultValue;
}
}
return t == null ? defaultValue : t;
}
public static Resource getConnectionPointOfTerminal(ReadGraph g, Terminal forTerminal) throws DatabaseException {
if (forTerminal instanceof ResourceTerminal)
return getConnectionPointOfTerminal(g, ((ResourceTerminal) forTerminal).getResource());
return null;
}
public static Resource tryGetBindingRelation(ReadGraph g, Terminal forTerminal) throws DatabaseException {
if (forTerminal instanceof ResourceTerminal)
return getPossibleConnectionPointOfTerminal(g, ((ResourceTerminal) forTerminal).getResource());
return null;
}
public static LineJoin toLineJoin(G2DResource g2d, Resource lineJoin) {
if (lineJoin != null) {
if (lineJoin.equals(g2d.LineJoin_BevelJoin))
return LineJoin.bevel;
if (lineJoin.equals(g2d.LineJoin_RoundJoin))
return LineJoin.round;
}
return LineJoin.miter;
}
public static LineCap toLineCap(G2DResource g2d, Resource lineCap) {
if (lineCap != null) {
if (lineCap.equals(g2d.LineCap_ButtCap))
return LineCap.butt;
if (lineCap.equals(g2d.LineCap_RoundCap))
return LineCap.round;
}
return LineCap.square;
}
public static Resource toLineJoin(G2DResource g2d, LineJoin lineJoin) {
if (lineJoin != null) {
if (lineJoin.equals(LineJoin.bevel))
return g2d.LineJoin_BevelJoin;
if (lineJoin.equals(LineJoin.round))
return g2d.LineJoin_RoundJoin;
}
return g2d.LineJoin_MiterJoin;
}
public static Resource toLineCap(G2DResource g2d, LineCap lineCap) {
if (lineCap != null) {
if (lineCap.equals(LineCap.butt))
return g2d.LineCap_ButtCap;
if (lineCap.equals(LineCap.round))
return g2d.LineCap_RoundCap;
}
return g2d.LineCap_SquareCap;
}
public static Alignment toAlignment(Resource align, G2DResource g2d, Alignment defaultValue) {
if (align == null)
return defaultValue;
if (align.equals(g2d.Alignment_Leading))
return Alignment.LEADING;
if (align.equals(g2d.Alignment_Trailing))
return Alignment.TRAILING;
if (align.equals(g2d.Alignment_Center))
return Alignment.CENTER;
return defaultValue;
}
public static Alignment toVerticalAlignment(Resource align, G2DResource g2d, Alignment defaultValue) {
if (align == null)
return defaultValue;
if (align.equals(g2d.Alignment_Leading))
return Alignment.LEADING;
if (align.equals(g2d.Alignment_Trailing))
return Alignment.TRAILING;
if (align.equals(g2d.Alignment_Center))
return Alignment.CENTER;
if (align.equals(g2d.Alignment_Baseline))
return Alignment.BASELINE;
return defaultValue;
}
public static Resource toFlagTypeResource(DiagramResource dr, FlagClass.Type type) {
switch (type) {
case In: return dr.FlagType_InputFlag;
case Out: return dr.FlagType_OutputFlag;
default: throw new IllegalArgumentException("unsupported flag type: " + type);
}
}
public static FlagClass.Type toFlagType(DiagramResource dr, Resource type) {
return toFlagType(dr, type, Type.In);
}
public static FlagClass.Type toFlagType(DiagramResource dr, Resource type, FlagClass.Type defaultValue) {
if (type != null) {
if (dr.FlagType_InputFlag.equals(type))
return Type.In;
if (dr.FlagType_OutputFlag.equals(type))
return Type.Out;
}
return defaultValue;
}
public static void tag(WriteGraph g, Resource object, Resource tag, boolean set) throws DatabaseException {
if (set)
g.claim(object, tag, tag, object);
else
g.deny(object, tag, tag, object);
}
/**
* @param graph
* @param diagram the diagram from which to look for a page description
* property
* @return if the diagram does not have a page desc definition,
* defaultValue
is returned
* @throws DatabaseException
*/
public static PageDesc getPageDesc(ReadGraph graph, Resource diagram, PageDesc defaultValue) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(graph);
Resource pd = graph.getPossibleObject(diagram, dr.HasPageDescription);
if (pd == null)
return defaultValue;
return readPageDesc(graph, pd);
}
/**
* @param graph
* @param diagram
* the diagram from which to look for a page description property
* @return PageDesc for the specified diagram
* @throws DatabaseException
* if DIA.HasPageDescription can't be read
*/
public static PageDesc getPageDesc(ReadGraph graph, Resource diagram) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(graph);
Resource pd = graph.getSingleObject(diagram, dr.HasPageDescription);
return readPageDesc(graph, pd);
}
public static PageDesc readPageDesc(ReadGraph graph, Resource pageDesc) throws DatabaseException {
Layer0 l0 = Layer0.getInstance(graph);
DiagramResource dr = DiagramResource.getInstance(graph);
Resource orientation = graph.getSingleObject(pageDesc, dr.PageDescription_Orientation);
double[] size = graph.getRelatedValue(pageDesc, dr.PageDescription_Size, Bindings.getBindingUnchecked(double[].class));
Resource margins = graph.getSingleObject(pageDesc, dr.PageDescription_Margins);
Margins m = readMargins(graph, margins);
//PageDesc pd = PageDesc.getDescription(toOrientation(orientation, dr), size[0], size[1]);
String name = graph.getPossibleRelatedValue(pageDesc, l0.HasName);
if (name == null)
name = "";
PageDesc pd = new PageDesc(name, toOrientation(orientation, dr), PageCentering.TopLeftAtOrigin, size[0], size[1], m);
return pd;
}
public static Margins readMargins(ReadGraph graph, Resource margins) throws NoSingleResultException, DoesNotContainValueException, ServiceException {
DiagramResource dr = DiagramResource.getInstance(graph);
double t = graph.getRelatedValue(margins, dr.PageDescription_Margins_Top);
double b = graph.getRelatedValue(margins, dr.PageDescription_Margins_Bottom);
double l = graph.getRelatedValue(margins, dr.PageDescription_Margins_Left);
double r = graph.getRelatedValue(margins, dr.PageDescription_Margins_Right);
Margin mt = new Margin(0, 0, t);
Margin mb = new Margin(0, 0, b);
Margin ml = new Margin(0, 0, l);
Margin mr = new Margin(0, 0, r);
return new Margins(mt, mb, ml, mr);
}
public static void setPageDesc(WriteGraph graph, Resource diagram, String pageDescRepr) throws DatabaseException {
setPageDesc(graph, diagram, PageDesc.fromRepr(pageDescRepr));
}
public static void setPageDesc(WriteGraph graph, Resource diagram, PageDesc pageDesc) throws DatabaseException {
Layer0 b = Layer0.getInstance(graph);
G2DResource g2d = G2DResource.getInstance(graph);
DiagramResource dr = DiagramResource.getInstance(graph);
Resource pd = graph.getPossibleObject(diagram, dr.HasPageDescription);
if(pd != null && graph.isImmutable(pd)) {
graph.deny(diagram, dr.HasPageDescription);
pd = null;
}
if (pd == null) {
pd = graph.newResource();
graph.claim(pd, b.InstanceOf, null, dr.PageDescription);
graph.claim(diagram, dr.HasPageDescription, pd);
}
graph.deny(pd, dr.PageDescription_Size);
Resource pageSize = graph.newResource();
graph.claim(pageSize, b.InstanceOf, null, g2d.Point2D);
graph.claimValue(pageSize, new double[] { pageDesc.getWidth(), pageDesc.getHeight() });
graph.claim(pd, dr.PageDescription_Size, pageSize);
graph.deny(pd, dr.PageDescription_Orientation);
graph.claim(pd, dr.PageDescription_Orientation, toOrientationResource(pageDesc.getOrientation(), dr));
Resource margins = graph.getPossibleObject(pd, dr.PageDescription_Margins);
if (margins == null) {
margins = graph.newResource();
graph.claim(margins, b.InstanceOf, null, dr.Margins);
graph.claim(pd, dr.PageDescription_Margins, margins);
}
setMargins(graph,pageDesc.getMargins(),margins, dr);
graph.claimLiteral(pd, b.HasName,pageDesc.getText());
}
private static PageOrientation toOrientation(Resource orientation, DiagramResource dr) {
if (orientation != null) {
if (orientation.equals(dr.Orientation_Portrait))
return PageOrientation.Portrait;
if (orientation.equals(dr.Orientation_Landscape))
return PageOrientation.Landscape;
}
return PageOrientation.Portrait;
}
public static Resource toOrientationResource(PageOrientation orientation, DiagramResource dr) {
if (PageOrientation.Portrait.equals(orientation))
return dr.Orientation_Portrait;
if (PageOrientation.Landscape.equals(orientation))
return dr.Orientation_Landscape;
return dr.Orientation_Portrait;
}
private static void setMargins(WriteGraph g, Margins margins, Resource marginsR,DiagramResource dr) throws ServiceException, ManyObjectsForFunctionalRelationException {
g.claimLiteral(marginsR, dr.PageDescription_Margins_Top,margins.top.diagramAbsolute);
g.claimLiteral(marginsR, dr.PageDescription_Margins_Bottom,margins.bottom.diagramAbsolute);
g.claimLiteral(marginsR, dr.PageDescription_Margins_Left,margins.left.diagramAbsolute);
g.claimLiteral(marginsR, dr.PageDescription_Margins_Right,margins.right.diagramAbsolute);
}
public static Double getGridSize(ReadGraph graph, Resource diagram, Double defaultValue) throws ManyObjectsForFunctionalRelationException, ServiceException {
DiagramResource dr = DiagramResource.getInstance(graph);
Double gridSize = graph.getPossibleRelatedValue(diagram, dr.HasGridSize);
return gridSize == null ? defaultValue : gridSize;
}
public static void setGridSize(WriteGraph graph, Resource diagram, double gridSize) throws ManyObjectsForFunctionalRelationException, ServiceException {
DiagramResource dr = DiagramResource.getInstance(graph);
graph.claimLiteral(diagram, dr.HasGridSize, gridSize);
}
public static boolean isPageBordersVisible(ReadGraph graph, Resource diagram) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(graph);
return graph.hasStatement(diagram, dr.DisplayPageSize);
}
public static boolean isMarginsVisible(ReadGraph graph, Resource diagram) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(graph);
return graph.hasStatement(diagram, dr.DisplayMargins);
}
public static void setPageBordersVisible(WriteGraph graph, Resource diagram, boolean visible) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(graph);
tag(graph, diagram, dr.DisplayPageSize, visible);
}
public static void setMarginsVisible(WriteGraph graph, Resource diagram, boolean visible) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(graph);
tag(graph, diagram, dr.DisplayMargins, visible);
}
public static void setDiagramDesc(WriteGraph graph, Resource diagram, DiagramDesc desc) throws DatabaseException {
DiagramGraphUtil.setPageDesc(graph, diagram, desc.getPageDesc());
DiagramGraphUtil.setGridSize(graph, diagram, desc.getGridSize());
DiagramGraphUtil.setPageBordersVisible(graph, diagram, desc.isPageBordersVisible());
DiagramGraphUtil.setMarginsVisible(graph, diagram, desc.isMarginsVisible());
// Add comment to change set.
CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
graph.addMetadata(cm.add("Set diagram description for diagram resource " + diagram));
}
/**
* Potentially returns the connection connected to the element with the
* given connectionRelation.
*/
public static Resource getRelatedConnection(ReadGraph g, Resource element, Resource connectionRelation) throws DatabaseException {
StructuralResource2 sr = StructuralResource2.getInstance(g);
for(Resource connector : g.getObjects(element, connectionRelation))
for(Resource connection : g.getObjects(connector, sr.Connects))
if(!connection.equals(element))
return connection;
return null;
}
/**
* Returns the connection type of a potential connection connected to the
* element with the given connectionRelation.
*/
public static Resource getRelatedConnectionType(ReadGraph g, Resource element, Resource connectionRelation) throws DatabaseException {
StructuralResource2 sr = StructuralResource2.getInstance(g);
for(Resource connector : g.getObjects(element, connectionRelation))
for(Resource connection : g.getObjects(connector, sr.Connects))
if(!connection.equals(element))
for(Resource connectionType : g.getObjects(connection, sr.HasConnectionType))
return connectionType;
return null;
}
/**
* Returns a flag that is joined to this flag with a ConnectionJoin.
*/
public static Resource getJoinedFlag(ReadGraph g, Resource flag) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(g);
for(Resource join : g.getObjects(flag, dr.FlagIsJoinedBy))
for(Resource otherFlag : g.getObjects(join, dr.JoinsFlag))
if(!otherFlag.equals(flag))
return otherFlag;
return null;
}
public static Resource getConnectionTypeForFlag(ReadGraph g, Resource flag) throws DatabaseException {
DiagramResource dr = DiagramResource.getInstance(g);
Resource connectionType = getRelatedConnectionType(g, flag, dr.Flag_ConnectionPoint);
if(connectionType != null)
return connectionType;
Resource otherFlag = getJoinedFlag(g, flag);
if(otherFlag == null)
return null;
return getRelatedConnectionType(g, otherFlag, dr.Flag_ConnectionPoint);
}
/**
* Checks if the two specified diagram elements exist on the same diagram.
*/
public static boolean onSameDiagram(ReadGraph graph, Resource element1, Resource element2) throws DatabaseException {
return !Collections.disjoint(
OrderedSetElementsPredicate.INSTANCE.getSubjects(graph, element1),
OrderedSetElementsPredicate.INSTANCE.getSubjects(graph, element2));
}
/**
* Checks whether a diagram element has a DIAGRAM.Routing
tag
* that is adaptable to {@link IRouter2}.
*
* @param graph
* @param element
* @param procedure
*/
public static void getPossibleRouter(AsyncReadGraph graph, final Resource element, final AsyncProcedure procedure) {
DiagramResource dr = graph.getService(DiagramResource.class);
graph.forPossibleStatement(element, dr.Routing, new AsyncProcedure() {
@Override
public void exception(AsyncReadGraph graph, Throwable throwable) {
procedure.exception(graph, throwable);
}
@Override
public void execute(AsyncReadGraph graph, Statement result) {
if (result != null)
graph.forPossibleAdapted(result.getPredicate(), IRouter2.class, procedure);
else
procedure.execute(graph, null);
}
});
}
/**
* @param graph
* @param modelingRules
* @param connection
* @param diagram
* @param edge
* @param firstTerminal
* @param secondTerminal
* @throws DatabaseException
*/
public static void loadConnectionVisuals(ReadGraph graph, IModelingRules modelingRules, Resource connection,
IDiagram diagram, IElement edge, DesignatedTerminal firstTerminal, DesignatedTerminal secondTerminal)
throws DatabaseException {
List evs = edge.getElementClass().getItemsByClass(EdgeVisuals.class);
if (evs.isEmpty())
return;
IAttachmentRelationMap attachmentRelations = modelingRules.getAttachmentRelations(graph, connection);
IConnectionPoint firstCp = ConnectionUtil.toConnectionPoint(graph, firstTerminal);
IConnectionPoint secondCp = ConnectionUtil.toConnectionPoint(graph, secondTerminal);
Resource firstAttachment = null;
Resource secondAttachment = null;
if (firstCp instanceof CPTerminal)
firstAttachment = attachmentRelations.get(graph, (CPTerminal) firstCp);
if (secondCp instanceof CPTerminal)
secondAttachment = attachmentRelations.get(graph, (CPTerminal) secondCp);
if (DebugPolicy.DEBUG_CONNECTION_VISUALS_LOAD) {
System.out.println("first attachment relation : " + NameUtils.getSafeName(graph, firstAttachment));
System.out.println("second attachment relation : " + NameUtils.getSafeName(graph, secondAttachment));
}
// 1. Configure edge ends
loadEdgeEnds(graph, modelingRules, connection, edge, firstAttachment, secondAttachment);
// 2. Configure edge line style
loadLineStyle(graph, modelingRules, connection, edge);
}
public static void loadEdgeEnds(ReadGraph graph, IModelingRules modelingRules, Resource connection, IElement edge,
Resource firstAttachment, Resource secondAttachment) throws DatabaseException {
EdgeVisualsConfigurer startConfig = (firstAttachment != null) ? graph.syncRequest(Queries.adapt(
firstAttachment, EdgeVisualsConfigurer.class, true)) : null;
EdgeVisualsConfigurer endConfig = (secondAttachment != null) ? graph.syncRequest(Queries.adapt(
secondAttachment, EdgeVisualsConfigurer.class, true)) : null;
for (EdgeVisuals ev : edge.getElementClass().getItemsByClass(EdgeVisuals.class)) {
if (startConfig != null)
startConfig.configure(edge, ev, EdgeVisuals.BEGIN);
else
ev.setArrowType(edge, EdgeEnd.Begin, ArrowType.None);
if (endConfig != null)
endConfig.configure(edge, ev, EdgeVisuals.END);
else
ev.setArrowType(edge, EdgeEnd.End, ArrowType.None);
}
}
public static void loadLineStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection, IElement edge)
throws DatabaseException {
Resource connectionType = modelingRules.getConnectionType(graph, connection);
if (connectionType != null) {
loadLineStyleFromConnectionType(graph, modelingRules, connectionType, edge);
}
}
public static void loadLineStyleFromConnectionType(ReadGraph graph, IModelingRules modelingRules, Resource connectionType, IElement edge)
throws DatabaseException {
edge.setHint(ElementHints.KEY_CONNECTION_TYPE, connectionType);
if (DebugPolicy.DEBUG_CONNECTION_VISUALS_LOAD)
System.out.println("Connection type : " + NameUtils.getSafeName(graph, connectionType));
// Load standard visual aspects of the specified edge
ConnectionVisuals cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType));
if (cv.color != null) {
for (FillColor fc : edge.getElementClass().getItemsByClass(FillColor.class)) {
fc.setFillColor(edge, cv.toColor());
}
}
for (EdgeVisuals ev : edge.getElementClass().getItemsByClass(EdgeVisuals.class)) {
if (cv.stroke != null)
ev.setStroke(edge, cv.stroke);
if (cv.strokeType != null)
ev.setStrokeType(edge, cv.strokeType);
}
}
/**
* @param graph
* @param connectionPart
* @return
* @throws DatabaseException
*/
public static ConnectionSegmentEnd resolveConnectionSegmentEnd(ReadGraph graph, Resource connectionPart)
throws DatabaseException {
BasicResources br = BasicResources.getInstance(graph);
if (graph.isInstanceOf(connectionPart, br.DIA.BranchPoint))
return ConnectionSegmentEnd.BRANCH;
if (graph.isInstanceOf(connectionPart, br.DIA.Connector))
return ConnectionSegmentEnd.CONNECTOR;
return null;
}
/**
* @param graph
* @param diagram
* @param segmentEnd
* @param endType
* @return
* @throws DatabaseException
*/
public static DesignatedTerminal findDesignatedTerminal(ReadGraph graph, IDiagram diagram, Resource segmentEnd, ConnectionSegmentEnd endType)
throws DatabaseException {
if (DebugPolicy.DEBUG_TERMINAL_SEARCH)
System.out.println("findDesignatedTerminal: " + NameUtils.getSafeName(graph, segmentEnd) + " : " + endType);
BasicResources br = BasicResources.getInstance(graph);
DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
switch (endType) {
case CONNECTOR: {
List ts = new ArrayList();
for (Statement stm : graph.getStatements(segmentEnd, br.STR.Connects)) {
// Ignore the Is Connector Of relation that goes to the
// owner :Connection
if (graph.isSubrelationOf(stm.getPredicate(), br.DIA.IsConnectorOf))
continue;
Resource connectionRelation = graph.getInverse(stm.getPredicate());
if (DebugPolicy.DEBUG_TERMINAL_SEARCH)
System.out.println("CONNECTION RELATION: " + NameUtils.getSafeName(graph, connectionRelation));
Resource elementResource = stm.getObject();
if (DebugPolicy.DEBUG_TERMINAL_SEARCH)
System.out.println("ELEMENT RESOURCE: " + NameUtils.getSafeName(graph, elementResource));
IElement e = dem.getElement(diagram, elementResource);
if (e == null) {
return null;
// throw new ValidationException("connector "
// + NameUtils.getSafeName(graph, segmentEnd)
// + " is connected to an entity that has not (yet) been loaded as an IElement: "
// + NameUtils.getSafeName(graph, elementResource));
}
TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);
ts.clear();
tt.getTerminals(e, ts);
for (Terminal t : ts) {
if (t instanceof ResourceTerminal) {
ResourceTerminal rt = (ResourceTerminal) t;
Resource binds = DiagramGraphUtil.getConnectionPointOfTerminal(graph, rt.getResource());
if (DebugPolicy.DEBUG_TERMINAL_SEARCH) {
System.out.println("connection relation: "
+ NameUtils.getSafeName(graph, connectionRelation) + " " + connectionRelation.getResourceId());
System.out.println(" terminal: " + NameUtils.getSafeName(graph, rt.getResource()) + " " + rt.getResource().getResourceId());
System.out.println(" binds: " + NameUtils.getSafeName(graph, binds) + " " + binds.getResourceId());
}
if (graph.isSubrelationOf(connectionRelation, binds)) {
return new DesignatedTerminal(e, t);
}
}
}
throw new ValidationException("connector "
+ NameUtils.getSafeName(graph, segmentEnd)
+ " is connected using a relation that is not its own: "
+ NameUtils.getSafeName(graph, connectionRelation));
}
// throw new
// ValidationException("connector " +
// NameUtils.getSafeName(g, segmentEnd) +
// " is not connected to anything");
return null;
}
case BRANCH: {
List ts = new ArrayList();
IElement e = dem.getElement(diagram, segmentEnd);
if (e == null) {
// throw new ValidationException("branch point "
// + NameUtils.getSafeName(graph, segmentEnd)
// + " has not (yet) been loaded as an IElement");
return null;
}
TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);
tt.getTerminals(e, ts);
if (ts.size() != 1)
throw new IllegalStateException("branch point element has " + ts.size()
+ " terminals, expected 1");
return new DesignatedTerminal(e, ts.get(0));
}
default:
throw new IllegalArgumentException("unexpected connection segment end: " + endType);
}
}
/**
* @param graph
* @param diagram
* @param terminalStm the statement that goes from the connection
* connector to the node with inverse relation of the terminal
* relation.
* @return
* @throws DatabaseException
*/
public static DesignatedTerminal getDesignatedTerminalForConnector(ReadGraph graph, IDiagram diagram, Resource elementResource, Resource terminalRelation, Resource connector)
throws DatabaseException {
if (DebugPolicy.DEBUG_TERMINAL_SEARCH)
System.out.println("getDesignatedTerminalForConnector: ("
+ NameUtils.getSafeName(graph, elementResource) + ", "
+ NameUtils.getSafeName(graph, terminalRelation) + ", "
+ NameUtils.getSafeName(graph, connector) + ")"
);
DataElementMap dem = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
IElement e = dem.getElement(diagram, elementResource);
if (e == null)
return null;
TerminalTopology tt = e.getElementClass().getSingleItem(TerminalTopology.class);
List ts = new ArrayList();
tt.getTerminals(e, ts);
for (Terminal t : ts) {
if (t instanceof ResourceTerminal) {
ResourceTerminal rt = (ResourceTerminal) t;
Resource binds = DiagramGraphUtil.getConnectionPointOfTerminal(graph, rt.getResource());
if (DebugPolicy.DEBUG_TERMINAL_SEARCH) {
System.out.println("connection relation: "
+ NameUtils.getSafeName(graph, terminalRelation, true));
System.out.println(" terminal: " + NameUtils.getSafeName(graph, rt.getResource(), true));
System.out.println(" binds: " + NameUtils.getSafeName(graph, binds, true));
}
if (graph.isSubrelationOf(terminalRelation, binds)) {
return new DesignatedTerminal(e, t);
}
}
}
throw new ValidationException("terminal connection statement ("
+ NameUtils.getSafeName(graph, elementResource) + ", "
+ NameUtils.getSafeName(graph, terminalRelation) + ", "
+ NameUtils.getSafeName(graph, connector) + ")"
+ " is using using a terminal relation that is not its own: "
+ NameUtils.getSafeName(graph, terminalRelation));
}
/**
* @param graph
* @return
* @throws DatabaseException
*/
public static TerminalMap getElementTerminals(ReadGraph graph, Resource element)
throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
Resource elementType = graph.getPossibleType(element, DIA.Element);
return elementType != null ? getElementTypeTerminals(graph, elementType) : TerminalMap.EMPTY;
}
private static final boolean DEBUG_GET_ELEMENT_TYPE_TERMINALS = false;
/**
* @param graph
* @param elementType
* @return
* @throws DatabaseException
*/
public static TerminalMap getElementTypeTerminals(ReadGraph graph, Resource elementType) throws DatabaseException {
StructuralResource2 STR = StructuralResource2.getInstance(graph);
DiagramResource DIA = DiagramResource.getInstance(graph);
if (DEBUG_GET_ELEMENT_TYPE_TERMINALS)
System.out.println("getElementTypeTerminals: " + NameUtils.getSafeName(graph, elementType));
Resource definedBy = graph.getSingleObject(elementType, STR.IsDefinedBy);
Collection parts = OrderedSetUtils.toList(graph, definedBy);
if (DEBUG_GET_ELEMENT_TYPE_TERMINALS)
System.out.println("\tdefining part count: " + parts.size());
TerminalMap result = null;
for (Resource part : parts) {
if (DEBUG_GET_ELEMENT_TYPE_TERMINALS)
System.out.println("\t\tpart: " + NameUtils.getSafeName(graph, part));
if (graph.isInstanceOf(part, DIA.Terminal)) {
Resource binds = DiagramGraphUtil.getConnectionPointOfTerminal(graph, part);
if (result == null)
result = new TerminalMap(parts.size());
if (DEBUG_GET_ELEMENT_TYPE_TERMINALS)
System.out.println("\t\t\tFOUND TERMINAL <-> BINDING RELATION: " + NameUtils.getSafeName(graph, part) + " <-> " + NameUtils.getSafeName(graph, binds));
result.put(part, binds);
}
}
return result != null ? result : TerminalMap.EMPTY;
}
/**
* Get the value of a specified on/off diagram preference setting (=tag)
* where the tag may be stored in:
*
* - The diagram itself
* - The model
* - The project
*
*
* @param graph database access
* @param diagram the diagram to look for the tag in
* @param preference the tag relation of the boolean preference to check for
* @return value of the preference
* @throws DatabaseException
*/
public static boolean getDiagramTagPreference(ReadGraph graph, Resource diagram, Resource preference) throws DatabaseException {
boolean result = graph.hasStatement(diagram, preference);
if (!result) {
Resource model = graph.sync(new PossibleModel(diagram));
if (model != null)
result = graph.hasStatement(model, preference);
if (!result)
result = graph.hasStatement(Simantics.getProjectResource(), preference);
}
return result;
}
public static void rotateConnection(WriteGraph graph, Resource r,
double cx, double cy, boolean clockwise) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
for(Resource node : graph.getObjects(r, DIA.HasInteriorRouteNode))
if(graph.isInstanceOf(node, DIA.RouteLine)) {
boolean isHorizontal = (Boolean)graph.getRelatedValue(node, DIA.IsHorizontal);
double position = (Double)graph.getRelatedValue(node, DIA.HasPosition);
if(isHorizontal) {
position -= cy;
if(clockwise)
position = -position;
position += cx;
}
else {
position -= cx;
if(!clockwise)
position = -position;
position += cy;
}
isHorizontal = !isHorizontal;
graph.claimLiteral(node, DIA.IsHorizontal, isHorizontal);
graph.claimLiteral(node, DIA.HasPosition, position);
}
}
public static void flipConnection(WriteGraph graph, Resource r,
boolean xAxis, double c) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
for(Resource node : graph.getObjects(r, DIA.HasInteriorRouteNode))
if(graph.isInstanceOf(node, DIA.RouteLine)) {
boolean isHorizontal = (Boolean)graph.getRelatedValue(node, DIA.IsHorizontal);
if(isHorizontal == xAxis) {
double position = (Double)graph.getRelatedValue(node, DIA.HasPosition);
position = 2*c-position;
graph.claimLiteral(node, DIA.HasPosition, position);
}
}
}
/*public static void addConnectionPoint(WriteGraph g, Resource symbol, Resource terminal,
Resource diagramConnectionRelation) throws DatabaseException {
Layer0 L0 = Layer0.getInstance(g);
StructuralResource2 STR = StructuralResource2.getInstance(g);
DiagramResource DIA = DiagramResource.getInstance(g);
Resource variable = g.newResource();
g.claim(variable, L0.InstanceOf, null, STR.ConnectionVariable);
g.claim(terminal, DIA.HasConnectionVariable, variable);
g.claim(variable, STR.Binds, diagramConnectionRelation);
g.claim(variable, STR.IsParameterOf, symbol);
}
public static Resource getConnectionPointOfTerminal(ReadGraph g, Resource forTerminal) throws DatabaseException {
return g.getSingleObject(
g.getSingleObject(forTerminal, DiagramResource.getInstance(g).HasConnectionVariable),
StructuralResource2.getInstance(g).Binds);
}
public static Resource getPossibleConnectionPointOfTerminal(ReadGraph g, Resource forTerminal) throws DatabaseException {
Resource connectionVariable = g.getPossibleObject(forTerminal, DiagramResource.getInstance(g).HasConnectionVariable);
return (connectionVariable == null) ? null : g.getPossibleObject(connectionVariable, StructuralResource2.getInstance(g).Binds);
}*/
public static void addConnectionPoint(WriteGraph g, Resource symbol, Resource terminal,
Resource diagramConnectionRelation) throws DatabaseException {
Layer0 L0 = Layer0.getInstance(g);
DiagramResource DIA = DiagramResource.getInstance(g);
g.claim(terminal, DIA.HasConnectionPoint, diagramConnectionRelation);
g.claim(diagramConnectionRelation, L0.HasDomain, symbol);
}
public static Resource getConnectionPointOfTerminal(ReadGraph g, Resource forTerminal) throws DatabaseException {
return g.getSingleObject(forTerminal, DiagramResource.getInstance(g).HasConnectionPoint);
}
public static Resource getPossibleConnectionPointOfTerminal(ReadGraph g, Resource forTerminal) throws DatabaseException {
return g.getPossibleObject(forTerminal, DiagramResource.getInstance(g).HasConnectionPoint);
}
public static Collection getTerminals(ReadGraph g, Resource symbol) throws DatabaseException {
// Layer0 L0 = Layer0.getInstance(g);
StructuralResource2 STR = StructuralResource2.getInstance(g);
DiagramResource DIA = DiagramResource.getInstance(g);
List terminals = null;
for (Resource definedBy : g.getObjects(symbol, STR.IsDefinedBy)) {
Collection elements = g.syncRequest( new OrderedSet(definedBy) );
if (terminals == null)
terminals = new ArrayList( elements.size() );
for (Resource element : elements)
if (g.isInstanceOf(element, DIA.Terminal))
terminals.add(element);
}
// Collection definedBy = g.getObjects(symbol, STR.IsDefinedBy);
// if (!definedBy.isEmpty()) {
// Collection relations = g.getObjects(symbol, L0.DomainOf);
// terminals = new ArrayList(relations.size());
// for(Resource relation : relations) {
// for (Resource element : g.getObjects(relation, DIA.HasConnectionPoint_Inverse)) {
// Collection owners = OrderedSetUtils.getOwnerLists(g, element, DIA.Diagram);
// if (!Collections.disjoint(definedBy, owners))
// terminals.add(element);
// }
// }
// }
return terminals == null ? Collections.emptyList() : terminals;
}
/**
* Determines the connection type of the given diagram connection. Uses the modeling rules
* specified in the diagram the connection belongs to.
*/
public static Resource determineConnectionType(ReadGraph graph, Resource diagramConnection) throws DatabaseException {
Layer0 L0 = Layer0.getInstance(graph);
Resource diagram = graph.getPossibleObject(diagramConnection, L0.PartOf);
if (diagram == null)
// Invalid diagram connection resource, not a part of any diagram.
return null;
IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagram, null));
if (modelingRules == null)
return null;
return determineConnectionType(graph, diagramConnection, modelingRules);
}
/**
* Determines the connection type of the given diagram connection assuming the given modeling rules.
*/
public static Resource determineConnectionType(ReadGraph graph, Resource diagramConnection, IModelingRules modelingRules) throws DatabaseException {
Set cps = new THashSet();
DiagramRequests.expandConnections(graph, diagramConnection, new THashSet(), cps);
return modelingRules.computeConnectionType(graph, cps);
}
public static void defaultSymbolDropHandler(WriteGraph graph, List drop) throws DatabaseException {
System.err.println("dropped " + drop);
}
}