/******************************************************************************* * 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.structural2.modelingRules; import gnu.trove.set.hash.THashSet; import java.util.ArrayList; import java.util.Collection; import java.util.Set; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.layer0.Layer0; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.utils.StructuralUtils; import org.simantics.utils.strings.AlphanumComparator; public class StandardModelingRules extends AbstractModelingRules { Layer0 L0; StructuralResource2 STR; public StandardModelingRules(ReadGraph g) { L0 = Layer0.getInstance(g); STR = StructuralResource2.getInstance(g); } private ConnectionJudgement judgeConnection(ReadGraph g, Collection connectionPoints, boolean allowExisting) throws DatabaseException { // TODO: not a 100% sure if this is safe, will some critical cases break // in other applications than Apros? Anyway, this by default allows // any connections without classifiable connection points. if (connectionPoints.isEmpty()) { ConnectionJudgement judgement = new ConnectionJudgement(ConnectionJudgementType.LEGAL); judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE; return judgement; } // Check that connecting to all of the terminals is allowed if(!allowExisting) { for(IConnectionPoint cp : connectionPoints) { if(cp instanceof CPTerminal) { CPTerminal terminal = (CPTerminal)cp; if(g.isInstanceOf(terminal.relation, L0.FunctionalRelation)) { if(terminal.component == null) continue; if (g.hasStatement(terminal.component, terminal.relation)) { if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println(terminal.toString(g) + " failed FunctionalRelation test -> ILLEGAL"); return ConnectionJudgement.ILLEGAL; } for(Resource eqRelation : g.getObjects(terminal.relation, STR.ConnectionRelation_equivalentConnectionPoint)) if (g.hasStatement(terminal.component, eqRelation)) { if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println(terminal.toString(g) + " failed FunctionalRelation test of equivalent connection point -> ILLEGAL"); return ConnectionJudgement.ILLEGAL; } } } } } // Translate all connection points to terminals Set terminals = resolveTerminals(g, connectionPoints); // If there is a single default connection type that all connection // points agree on, it will be stored here. It will be used if there // is no single unambiguous connection type Resource defaultConnectionType = null; boolean singleDefaultConnectionType = false; // Determine the set of connection types all connection points accept Collection> typeSets = new ArrayList>(); for(CPTerminal terminal : terminals) { if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println(this + ": allowed connection types determination: " + terminal.toString(g) + " " + terminal.relation); Collection allowedConnectionTypes = g.syncRequest(new AllowedConnectionTypes(terminal.relation)); if(Policy.DEBUG_STANDARD_MODELING_RULES) for (Resource type : allowedConnectionTypes) System.out.println("\tallowedConnectionType: " + NameUtils.getSafeName(g, type, true)); typeSets.add(new THashSet(allowedConnectionTypes)); // Default connection type calculation Resource defaultsTo = g.getPossibleObject(terminal.relation, STR.DefaultsToConnectionType); if (defaultsTo != null) { if (defaultConnectionType == null) { defaultConnectionType = defaultsTo; singleDefaultConnectionType = true; } else if (!defaultsTo.equals(defaultConnectionType)) { singleDefaultConnectionType = false; } } } Collection connectionTypes = getAllowedConnectionTypes(g, typeSets); if(connectionTypes == null) { if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println("No connection types -> CANBEMADELEGAL"); return ConnectionJudgement.CANBEMADELEGAL; } // Validate connection types Resource possibleConnectionType = null; Resource chosenConnectionType = null; String chosenConnectionTypeName = null; connectionTypeLoop: for(Resource connectionType : connectionTypes) { if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println("Check constraints for " + NameUtils.getSafeName(g, connectionType)); switch (evaluateConstraints(g, connectionType, terminals)) { case ILLEGAL: continue connectionTypeLoop; case CANBEMADELEGAL: possibleConnectionType = connectionType; break; case LEGAL: if (chosenConnectionType != null) { if (singleDefaultConnectionType) { chosenConnectionType = defaultConnectionType; break connectionTypeLoop; } // There is no single unambiguous legal connection type. // Make a deterministic choice out of the available types // based on their names. Select that connection type placed // first in ascending lexicograpical order. if (chosenConnectionTypeName == null) chosenConnectionTypeName = NameUtils.getSafeName(g, chosenConnectionType); String connectionTypeName = NameUtils.getSafeName(g, connectionType); if (AlphanumComparator.COMPARATOR.compare(chosenConnectionTypeName, connectionTypeName) > 0) { chosenConnectionType = connectionType; chosenConnectionTypeName = connectionTypeName; } } else { chosenConnectionType = connectionType; } } } // Create result if(chosenConnectionType != null) { ConnectionJudgement judgement = new ConnectionJudgement(chosenConnectionType); judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE; return judgement; } else if(possibleConnectionType != null) { ConnectionJudgement judgement = new ConnectionJudgement( ConnectionJudgementType.CANBEMADELEGAL, possibleConnectionType); judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE; return judgement; } else return ConnectionJudgement.ILLEGAL; } private Collection getAllowedConnectionTypes(ReadGraph graph, Collection> typeSets) throws DatabaseException { // Preprocess sets according to STR.IsIncludedInConnectionType Set all = new THashSet(); for(Set s : typeSets) all.addAll(s); for(Resource r : all) for(Resource inc : graph.getObjects(r, STR.IsIncludedInConnectionType)) for(Set s : typeSets) if(s.contains(inc)) s.add(r); ArrayList connectionTypes = null; for(Set allowedConnectionTypes : typeSets) { if(!allowedConnectionTypes.isEmpty()) { if(connectionTypes == null) connectionTypes = new ArrayList(allowedConnectionTypes); else connectionTypes.retainAll(allowedConnectionTypes); } } return connectionTypes; } private ConnectionJudgementType evaluateConstraints(ReadGraph g, Resource connectionType, Set terminals) throws DatabaseException { boolean legal = true; for (Resource constraint : g.getObjects(connectionType, STR.HasConnectionConstraint)) { IConnectionConstraint cc = g.adapt(constraint, IConnectionConstraint.class); switch(cc.isLegal(g, terminals)) { case ILLEGAL: if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println(" " + cc.getClass().getSimpleName() + " -> ILLEGAL"); return ConnectionJudgementType.ILLEGAL; case CANBEMADELEGAL: if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println(" " + cc.getClass().getSimpleName() + " -> CANBEMADELEGAL"); legal = false; default: break; } } return legal ? ConnectionJudgementType.LEGAL : ConnectionJudgementType.CANBEMADELEGAL; } @Override public ConnectionJudgement judgeConnection(ReadGraph g, Collection connectionPoints) throws DatabaseException { return judgeConnection(g, connectionPoints, false); } @Override public Resource computeConnectionType(ReadGraph g, Collection connectionPoints) throws DatabaseException { return judgeConnection(g, connectionPoints, true).connectionType; } @Override public IAttachmentRelationMap getAttachmentRelations(ReadGraph g, Resource connection) { return StandardAttachmentRelationMap.INSTANCE; } @Override public Set resolveTerminals(ReadGraph g, Collection connectionPoints) throws DatabaseException { Set terminals = new THashSet(); StructuralResource2 STR = StructuralResource2.getInstance(g); for(IConnectionPoint cp : connectionPoints) { if(Policy.DEBUG_STANDARD_MODELING_RULES) System.out.println(this + ": translate connection point: " + cp.toString(g)); if(cp instanceof CPTerminal) terminals.add((CPTerminal)cp); else if(cp instanceof CPConnection) { CPConnection connection = (CPConnection)cp; for (Resource c : StructuralUtils.getRelatedConnections(g, connection.connection)) { for (Statement stat : g.getStatements(c, STR.Connects)) terminals.add(new CPTerminal(stat.getObject(), g.getInverse(stat.getPredicate()))); Resource inf = g.getPossibleObject(c, STR.Binds); // TODO: figure out a better component if(inf != null) terminals.add(new CPTerminal(inf, inf)); } } else if(cp instanceof CPConnectionJoin) { CPConnectionJoin connectionJoin = (CPConnectionJoin)cp; for (Resource c : StructuralUtils.getRelatedConnectionsOfConnectionJoin(g, connectionJoin.connectionJoin)) for (Statement stat : g.getStatements(c, STR.Connects)) terminals.add(new CPTerminal(stat.getObject(), g.getInverse(stat.getPredicate()))); } else throw new IllegalArgumentException("Connection point " + cp + " encountered."); } return terminals; } }