/******************************************************************************* * 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: *
    *
  1. The diagram itself
  2. *
  3. The model
  4. *
  5. The project
  6. *
* * @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); } }