/*******************************************************************************
* 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);
}
}
}