1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.structural2.modelingRules;
15 import gnu.trove.set.hash.THashSet;
17 import java.util.ArrayList;
18 import java.util.Collection;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.Resource;
23 import org.simantics.db.Statement;
24 import org.simantics.db.common.utils.NameUtils;
25 import org.simantics.db.exception.DatabaseException;
26 import org.simantics.layer0.Layer0;
27 import org.simantics.structural.stubs.StructuralResource2;
28 import org.simantics.structural2.utils.StructuralUtils;
29 import org.simantics.utils.strings.AlphanumComparator;
31 public class StandardModelingRules extends AbstractModelingRules {
34 StructuralResource2 STR;
36 public StandardModelingRules(ReadGraph g) {
37 L0 = Layer0.getInstance(g);
38 STR = StructuralResource2.getInstance(g);
41 private ConnectionJudgement judgeConnection(ReadGraph g,
42 Collection<IConnectionPoint> connectionPoints, boolean allowExisting)
43 throws DatabaseException {
45 // TODO: not a 100% sure if this is safe, will some critical cases break
46 // in other applications than Apros? Anyway, this by default allows
47 // any connections without classifiable connection points.
48 if (connectionPoints.isEmpty()) {
49 ConnectionJudgement judgement = new ConnectionJudgement(ConnectionJudgementType.LEGAL);
50 judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;
54 // Check that connecting to all of the terminals is allowed
56 for(IConnectionPoint cp : connectionPoints) {
57 if(cp instanceof CPTerminal) {
58 CPTerminal terminal = (CPTerminal)cp;
59 if(g.isInstanceOf(terminal.relation, L0.FunctionalRelation)) {
60 if(terminal.component == null)
62 if (g.hasStatement(terminal.component, terminal.relation)) {
63 if(Policy.DEBUG_STANDARD_MODELING_RULES)
64 System.out.println(terminal.toString(g) + " failed FunctionalRelation test -> ILLEGAL");
65 return ConnectionJudgement.ILLEGAL;
67 for(Resource eqRelation : g.getObjects(terminal.relation, STR.ConnectionRelation_equivalentConnectionPoint))
68 if (g.hasStatement(terminal.component, eqRelation)) {
69 if(Policy.DEBUG_STANDARD_MODELING_RULES)
70 System.out.println(terminal.toString(g) + " failed FunctionalRelation test of equivalent connection point -> ILLEGAL");
71 return ConnectionJudgement.ILLEGAL;
78 // Translate all connection points to terminals
79 Set<CPTerminal> terminals = resolveTerminals(g, connectionPoints);
81 // If there is a single default connection type that all connection
82 // points agree on, it will be stored here. It will be used if there
83 // is no single unambiguous connection type
84 Resource defaultConnectionType = null;
85 boolean singleDefaultConnectionType = false;
87 // Determine the set of connection types all connection points accept
88 Collection<Set<Resource>> typeSets = new ArrayList<Set<Resource>>();
89 for(CPTerminal terminal : terminals) {
90 if(Policy.DEBUG_STANDARD_MODELING_RULES)
91 System.out.println(this + ": allowed connection types determination: " + terminal.toString(g) + " " + terminal.relation);
92 Collection<Resource> allowedConnectionTypes = g.syncRequest(new AllowedConnectionTypes(terminal.relation));
93 if(Policy.DEBUG_STANDARD_MODELING_RULES)
94 for (Resource type : allowedConnectionTypes)
95 System.out.println("\tallowedConnectionType: " + NameUtils.getSafeName(g, type, true));
96 typeSets.add(new THashSet<Resource>(allowedConnectionTypes));
98 // Default connection type calculation
99 Resource defaultsTo = g.getPossibleObject(terminal.relation, STR.DefaultsToConnectionType);
100 if (defaultsTo != null) {
101 if (defaultConnectionType == null) {
102 defaultConnectionType = defaultsTo;
103 singleDefaultConnectionType = true;
104 } else if (!defaultsTo.equals(defaultConnectionType)) {
105 singleDefaultConnectionType = false;
110 Collection<Resource> connectionTypes = getAllowedConnectionTypes(g, typeSets);
112 if(connectionTypes == null) {
113 if(Policy.DEBUG_STANDARD_MODELING_RULES)
114 System.out.println("No connection types -> CANBEMADELEGAL");
115 return ConnectionJudgement.CANBEMADELEGAL;
118 // Validate connection types
119 Resource possibleConnectionType = null;
120 Resource chosenConnectionType = null;
121 String chosenConnectionTypeName = null;
123 for(Resource connectionType : connectionTypes) {
124 if(Policy.DEBUG_STANDARD_MODELING_RULES)
125 System.out.println("Check constraints for " + NameUtils.getSafeName(g, connectionType));
127 switch (evaluateConstraints(g, connectionType, terminals)) {
129 continue connectionTypeLoop;
132 possibleConnectionType = connectionType;
136 if (chosenConnectionType != null) {
137 if (singleDefaultConnectionType) {
138 chosenConnectionType = defaultConnectionType;
139 break connectionTypeLoop;
141 // There is no single unambiguous legal connection type.
142 // Make a deterministic choice out of the available types
143 // based on their names. Select that connection type placed
144 // first in ascending lexicograpical order.
145 if (chosenConnectionTypeName == null)
146 chosenConnectionTypeName = NameUtils.getSafeName(g, chosenConnectionType);
147 String connectionTypeName = NameUtils.getSafeName(g, connectionType);
148 if (AlphanumComparator.COMPARATOR.compare(chosenConnectionTypeName, connectionTypeName) > 0) {
149 chosenConnectionType = connectionType;
150 chosenConnectionTypeName = connectionTypeName;
153 chosenConnectionType = connectionType;
159 if(chosenConnectionType != null) {
160 ConnectionJudgement judgement = new ConnectionJudgement(chosenConnectionType);
161 judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;
164 else if(possibleConnectionType != null) {
165 ConnectionJudgement judgement = new ConnectionJudgement(
166 ConnectionJudgementType.CANBEMADELEGAL, possibleConnectionType);
167 judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;
171 return ConnectionJudgement.ILLEGAL;
174 private Collection<Resource> getAllowedConnectionTypes(ReadGraph graph, Collection<Set<Resource>> typeSets) throws DatabaseException {
176 // Preprocess sets according to STR.IsIncludedInConnectionType
177 Set<Resource> all = new THashSet<Resource>();
178 for(Set<Resource> s : typeSets) all.addAll(s);
179 for(Resource r : all)
180 for(Resource inc : graph.getObjects(r, STR.IsIncludedInConnectionType))
181 for(Set<Resource> s : typeSets)
182 if(s.contains(inc)) s.add(r);
184 ArrayList<Resource> connectionTypes = null;
185 for(Set<Resource> allowedConnectionTypes : typeSets) {
186 if(!allowedConnectionTypes.isEmpty()) {
187 if(connectionTypes == null)
188 connectionTypes = new ArrayList<Resource>(allowedConnectionTypes);
190 connectionTypes.retainAll(allowedConnectionTypes);
194 return connectionTypes;
198 private ConnectionJudgementType evaluateConstraints(ReadGraph g, Resource connectionType, Set<CPTerminal> terminals) throws DatabaseException {
199 boolean legal = true;
200 for (Resource constraint : g.getObjects(connectionType, STR.HasConnectionConstraint)) {
201 IConnectionConstraint cc = g.adapt(constraint, IConnectionConstraint.class);
202 if(Policy.DEBUG_STANDARD_MODELING_RULES)
203 System.out.println("Checking " + cc.getClass().getSimpleName());
204 switch(cc.isLegal(g, terminals)) {
206 if(Policy.DEBUG_STANDARD_MODELING_RULES)
207 System.out.println(" " + cc.getClass().getSimpleName() +
209 return ConnectionJudgementType.ILLEGAL;
211 if(Policy.DEBUG_STANDARD_MODELING_RULES)
212 System.out.println(" " + cc.getClass().getSimpleName() +
213 " -> CANBEMADELEGAL");
219 return legal ? ConnectionJudgementType.LEGAL : ConnectionJudgementType.CANBEMADELEGAL;
223 public ConnectionJudgement judgeConnection(ReadGraph g,
224 Collection<IConnectionPoint> connectionPoints)
225 throws DatabaseException {
227 return judgeConnection(g, connectionPoints, false);
232 public Resource computeConnectionType(ReadGraph g,
233 Collection<IConnectionPoint> connectionPoints)
234 throws DatabaseException {
236 return judgeConnection(g, connectionPoints, true).connectionType;
241 public IAttachmentRelationMap getAttachmentRelations(ReadGraph g,
242 Resource connection) {
243 return StandardAttachmentRelationMap.INSTANCE;
247 public Set<CPTerminal> resolveTerminals(ReadGraph g, Collection<IConnectionPoint> connectionPoints)
248 throws DatabaseException {
249 Set<CPTerminal> terminals = new THashSet<CPTerminal>();
250 StructuralResource2 STR = StructuralResource2.getInstance(g);
251 for(IConnectionPoint cp : connectionPoints) {
252 if(Policy.DEBUG_STANDARD_MODELING_RULES)
253 System.out.println(this + ": translate connection point: " + cp.toString(g));
254 if(cp instanceof CPTerminal)
255 terminals.add((CPTerminal)cp);
256 else if(cp instanceof CPConnection) {
257 CPConnection connection = (CPConnection)cp;
258 for (Resource c : StructuralUtils.getRelatedConnections(g, connection.connection)) {
259 for (Statement stat : g.getStatements(c, STR.Connects))
260 terminals.add(new CPTerminal(stat.getObject(),
261 g.getInverse(stat.getPredicate())));
262 Resource inf = g.getPossibleObject(c, STR.Binds);
263 // TODO: figure out a better component
264 if(inf != null) terminals.add(new CPTerminal(inf, inf));
267 else if(cp instanceof CPConnectionJoin) {
268 CPConnectionJoin connectionJoin = (CPConnectionJoin)cp;
269 for (Resource c : StructuralUtils.getRelatedConnectionsOfConnectionJoin(g, connectionJoin.connectionJoin))
270 for (Statement stat : g.getStatements(c, STR.Connects))
271 terminals.add(new CPTerminal(stat.getObject(),
272 g.getInverse(stat.getPredicate())));
275 throw new IllegalArgumentException("Connection point " + cp + " encountered.");