/******************************************************************************* * Copyright (c) 2012 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.modeling.flags; import gnu.trove.set.hash.THashSet; import java.util.Collections; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.databoard.annotations.Optional; import org.simantics.databoard.util.Bean; 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.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.content.ConnectionUtil; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.utils.StructuralUtils; import org.simantics.utils.ObjectUtils; /** * @author Hannu Niemistö * @author Tuukka Lehtonen */ public class LiftFlag { private static final boolean DEBUG = false; public static class LiftedConnectionPoint extends Bean { @Optional public Resource component; @Optional public Resource componentType; @Optional public Resource connectionPoint; /** * Read through STR.HasAttachmentRelation from configuration connection * point. */ @Optional public Resource attachmentRelation; } /** * Creates a connection point for the flag. Returns null * if the operation succeeded, otherwise returns an error message. */ public static String liftFlag(WriteGraph g, Resource flag) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); StructuralResource2 STR = StructuralResource2.getInstance(g); DiagramResource DIA = DiagramResource.getInstance(g); ModelingResources MOD = ModelingResources.getInstance(g); // Check preconditions if (g.hasStatement(flag, STR.IsJoinedBy)) return "Flag is already connected to other flag."; if (g.hasStatement(flag, DIA.IsLiftedAs)) return "Flag is already lifted."; // Find configuration connection Resource connection = findConfigurationConnection(g, flag); if (connection == null) return "Couldn't find configuration connection."; // Find component and connection point LiftedConnectionPoint lcp = calculateLiftedConnectionPointForConnection(g, connection); // Validate calculated result if (lcp.component == null) return "Didn't find a component where the flag is connected to."; if (lcp.componentType == null) return "Didn't find an enclosing user component."; // Generate default name if (DEBUG) System.out.println("found user component : " + NameUtils.getSafeName(g, lcp.componentType, true)); String newName = generateTerminalName(g, lcp); newName = NameUtils.findFreshName(g, newName, lcp.componentType); // Create the connection point Resource connectionPoint = g.newResource(); Resource connectionPointInv = g.newResource(); for (Resource superrelation : g.getObjects(lcp.connectionPoint, L0.SubrelationOf)) { g.claim(connectionPoint, L0.SubrelationOf, null, superrelation); g.claim(connectionPointInv, L0.SubrelationOf, null, g.getInverse(superrelation)); } for (Resource type : g.getObjects(lcp.connectionPoint, L0.InstanceOf)) { g.claim(connectionPoint, L0.InstanceOf, null, type); } g.claim(connectionPoint, L0.InverseOf, connectionPointInv); g.claimLiteral(connectionPoint, L0.HasName, newName, Bindings.STRING); g.claim(connectionPoint, L0.ConsistsOf, connectionPointInv); g.claimLiteral(connectionPointInv, L0.HasName, "Inverse", Bindings.STRING); g.claim(connectionPoint, L0.HasDomain, lcp.componentType); // Copy custom connection point terminal definitions from referenced // lifted connection point. Assertions from types may also bring some // definitions in already. for (Statement terminalStm : g.getStatements(lcp.connectionPoint, MOD.ConnectionRelationToTerminal)) { if (!terminalStm.isAsserted(lcp.connectionPoint)) { g.claim(connectionPoint, MOD.ConnectionRelationToTerminal, terminalStm.getObject()); } } StructuralUtils.addConnectionPoint(g, lcp.componentType, connectionPoint); g.claim(flag, DIA.IsLiftedAs, connectionPoint); // This is now somewhat redundant, because statement is also added // in mapping. But maybe flag is lifted when mapping is not active? g.claim(connection, STR.Binds, connectionPoint); // See platform issue https://www.simantics.org/redmine/issues/3482 if (lcp.attachmentRelation != null) g.claim(connectionPoint, STR.HasAttachmentRelation, lcp.attachmentRelation); g.claim(lcp.componentType, L0.ConsistsOf, connectionPoint); return null; } /** * @param g * @param element * @return * @throws DatabaseException */ public static Resource findConfigurationConnection(ReadGraph g, Resource element) throws DatabaseException { StructuralResource2 STR = StructuralResource2.getInstance(g); ModelingResources MOD = ModelingResources.getInstance(g); for (Resource connector : g.getObjects(element, STR.IsConnectedTo)) { Resource diagramConnection = ConnectionUtil.tryGetConnection(g, connector); if (diagramConnection != null) { Resource connection = g.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection); if (connection != null) return connection; } Resource mappedConnection = ConnectionUtil.tryGetMappedConnection(g, connector); if(mappedConnection != null) return mappedConnection; } return null; } /** * @param g * @param lcp * @return * @throws DatabaseException */ public static String generateTerminalName(ReadGraph g, LiftedConnectionPoint lcp) throws DatabaseException { String componentName = NameUtils.getSafeName(g, lcp.component); String cpName = NameUtils.getSafeName(g, lcp.connectionPoint); StringBuilder sb = new StringBuilder(); // NOTE: NameUtils.getSafeName never returns null so part of this logic below is a bit useless. if (componentName == null) { if (cpName == null) sb.append("ConnectionPoint"); else sb.append("Lifted").append(cpName); } else { if (cpName == null) sb.append(componentName).append("Lifted"); else sb.append(componentName).append("_").append(cpName); } return sb.toString(); } /** * @param graph * @param flag * @return * @throws DatabaseException */ public static LiftedConnectionPoint calculateLiftedConnectionPointForFlag(ReadGraph graph, Resource flag) throws DatabaseException { Resource connection = findConfigurationConnection(graph, flag); return connection == null ? new LiftedConnectionPoint() : calculateLiftedConnectionPointForConnection(graph, connection); } /** * @param graph * @param connection * @return * @throws DatabaseException */ public static LiftedConnectionPoint calculateLiftedConnectionPointForConnection(ReadGraph graph, Resource connection) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DiagramResource DIA = DiagramResource.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); if (DEBUG) System.out.println("calculateLiftedConnectionPoint from connection: " + NameUtils.getSafeName(graph, connection, true)); LiftedConnectionPoint result = new LiftedConnectionPoint(); findOutputTerminal: for (Statement stat : graph.getStatements(connection, STR.Connects)) { result.component = stat.getObject(); result.connectionPoint = graph.getInverse(stat.getPredicate()); result.attachmentRelation = graph.getPossibleObject(result.connectionPoint, STR.HasAttachmentRelation); if (DEBUG) System.out.println(" connection point " + NameUtils.getSafeName(graph, result.connectionPoint, true) + " connects component " + NameUtils.getSafeName(graph, result.component, true) + " with attachment " + NameUtils.getSafeName(graph, result.attachmentRelation)); // This code tries to find component and connector behind signals Resource connector = graph.getPossibleObject(result.component, MOD.ComponentToConnector); if (connector != null) { if (DEBUG) System.out.println("connector: " + NameUtils.getSafeName(graph, connector, true)); for (Statement s : graph.getStatements(connector, STR.Connects)) { Resource element = s.getObject(); Resource diagramRelation = graph.getInverse(s.getPredicate()); Resource componentCandidate = graph.getPossibleObject(element, MOD.ElementToComponent); Resource cpCandidate = graph.getPossibleObject(diagramRelation, MOD.DiagramConnectionRelationToConnectionRelation); if (DEBUG) { System.out.println("element: " + NameUtils.getSafeName(graph, element, true)); System.out.println("diagram connection relation: " + NameUtils.getSafeName(graph, diagramRelation, true)); System.out.println("component candidate: " + NameUtils.getSafeName(graph, componentCandidate, true)); System.out.println("connection point candidate: " + NameUtils.getSafeName(graph, cpCandidate, true)); } if (componentCandidate != null && cpCandidate != null) { result.component = componentCandidate; result.connectionPoint = cpCandidate; result.attachmentRelation = graph.getPossibleObject(cpCandidate, STR.HasAttachmentRelation); if (DEBUG) System.out.println("attachmentRelation: " + NameUtils.getSafeName(graph, result.attachmentRelation)); if (result.attachmentRelation != null && graph.isSubrelationOf(result.attachmentRelation, DIA.HasTailConnector)) // Found an output terminal, this is the one we want. break findOutputTerminal; } } } } // Find component type Resource componentType = null; for (Resource curComponent = result.component; true;) { componentType = graph.getPossibleObject(curComponent, STR.Defines); if (componentType != null) { result.componentType = componentType; break; } curComponent = graph.getPossibleObject(curComponent, L0.PartOf); if (curComponent == null) break; } return result; } /** * @param graph * @param componentType * @param connectionPoint * @param proper * @return null if connection point is valid * @throws DatabaseException */ public static String isConnectionPointValid(ReadGraph graph, Resource componentType, Resource connectionPoint, LiftedConnectionPoint proper) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DiagramResource DIA = DiagramResource.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); Resource existingHasAttachment = graph.getPossibleObject(connectionPoint, STR.HasAttachmentRelation); if (!ObjectUtils.objectEquals(existingHasAttachment, proper.attachmentRelation)) return "Wrong connection point (" + connectionPoint + ") attachment relation: is " + existingHasAttachment + ", should be " + proper.attachmentRelation; { Set existingTypes = new THashSet(graph.getObjects(connectionPoint, L0.InstanceOf)); Set properTypes = new THashSet(proper.connectionPoint != null ? graph.getObjects(proper.connectionPoint, L0.InstanceOf) : Collections.emptySet()); if (!existingTypes.equals(properTypes)) return "Incorrect connection point relation (" + connectionPoint + ") types (existing " + existingTypes + " vs. proper " + properTypes + ")"; } Resource diagramConnectionPoint = graph.getPossibleObject(connectionPoint, MOD.ConnectionRelationToDiagramConnectionRelation); if (diagramConnectionPoint != null) { Set existingTypes = new THashSet(graph.getObjects(diagramConnectionPoint, L0.InstanceOf)); Set properTypes = new THashSet(proper.connectionPoint != null ? graph.getObjects(proper.connectionPoint, MOD.ImpliesDiagramConnectionRelationType) : Collections.emptySet()); if (!existingTypes.equals(properTypes)) return "Incorrect diagram connection point relation (" + diagramConnectionPoint + ") types (existing " + existingTypes + " vs. proper " + properTypes + ")"; Set properTerminalTypes = new THashSet(graph.getObjects(connectionPoint, MOD.ConnectionRelationToTerminal)); for (Resource terminal : graph.getObjects(diagramConnectionPoint, DIA.HasConnectionPoint_Inverse)) { Set existingTerminalTypes = new THashSet(graph.getObjects(terminal, L0.InstanceOf)); if (!existingTerminalTypes.equals(properTerminalTypes)) return "Incorrect diagram connection point relation types (existing " + existingTypes + " vs. proper " + properTypes + ")"; } } return null; } /** * @param graph * @param componentType * @param connectionPoint * @param proper * @return null if connection point was properly validated * @throws DatabaseException */ public static String validateConnectionPoint(WriteGraph graph, Resource componentType, Resource connectionPoint, LiftedConnectionPoint proper) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); DiagramResource DIA = DiagramResource.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); // Fix HasAttachmentRelation of connectionPoint Resource existingHasAttachment = graph.getPossibleObject(connectionPoint, STR.HasAttachmentRelation); if (!ObjectUtils.objectEquals(existingHasAttachment, proper.attachmentRelation)) { graph.deny(connectionPoint, STR.HasAttachmentRelation); if (proper.attachmentRelation != null) graph.claim(connectionPoint, STR.HasAttachmentRelation, proper.attachmentRelation); } // Fix InstanceOf's of connectionPoint fixDirectTypes(graph, connectionPoint, proper.connectionPoint != null ? new THashSet( graph.getObjects(proper.connectionPoint, L0.InstanceOf) ) : Collections.emptySet()); // Fix InstanceOf's of connection point's diagram connection relation Resource diagramConnectionPoint = graph.getPossibleObject(connectionPoint, MOD.ConnectionRelationToDiagramConnectionRelation); if (diagramConnectionPoint != null) { fixDirectTypes(graph, diagramConnectionPoint, proper.connectionPoint != null ? new THashSet( graph.getObjects(proper.connectionPoint, MOD.ImpliesDiagramConnectionRelationType) ) : Collections.emptySet()); // Fix InstanceOf's of connection point's diagram connection relation's symbol terminal Set properTerminalTypes = new THashSet(graph.getObjects(connectionPoint, MOD.ConnectionRelationToTerminal)); for (Resource terminal : graph.getObjects(diagramConnectionPoint, DIA.HasConnectionPoint_Inverse)) { fixDirectTypes(graph, terminal, properTerminalTypes); } } return null; } /** * @param graph * @param r * @param properTypes * @throws DatabaseException */ private static void fixDirectTypes(WriteGraph graph, Resource r, Set properTypes) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Set existingTypes = new THashSet( graph.getObjects(r, L0.InstanceOf) ); if (!existingTypes.equals(properTypes)) { Set addTypes = new THashSet(properTypes); properTypes.removeAll(existingTypes); existingTypes.removeAll(properTypes); for (Resource remove : existingTypes) graph.deny(r, L0.InstanceOf, remove); for (Resource add : addTypes) graph.claim(r, L0.InstanceOf, null, add); } } }