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