]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.structural2/src/org/simantics/structural2/modelingRules/StandardModelingRules.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.structural2 / src / org / simantics / structural2 / modelingRules / StandardModelingRules.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.structural2.modelingRules;
13
14
15 import gnu.trove.set.hash.THashSet;
16
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Set;
20
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;
30
31 public class StandardModelingRules extends AbstractModelingRules {
32
33         Layer0 L0;
34         StructuralResource2 STR;
35
36         public StandardModelingRules(ReadGraph g) {
37                 L0 = Layer0.getInstance(g);
38                 STR = StructuralResource2.getInstance(g);
39         }
40
41         private ConnectionJudgement judgeConnection(ReadGraph g,
42                         Collection<IConnectionPoint> connectionPoints, boolean allowExisting)
43                         throws DatabaseException {
44
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;
51                         return judgement;
52                 }
53
54                 // Check that connecting to all of the terminals is allowed
55                 if(!allowExisting) {
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)
61                                                 continue;
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;
66                                         }
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;
72                             }   
73                                         }
74                                 }
75                         }
76                 }
77                 
78                 // Translate all connection points to terminals
79                 Set<CPTerminal> terminals = resolveTerminals(g, connectionPoints);
80                 
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;
86
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));
97
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;
106                                 }
107                         }
108                 }
109                 
110                 Collection<Resource> connectionTypes = getAllowedConnectionTypes(g, typeSets);
111                 
112                 if(connectionTypes == null) {
113                         if(Policy.DEBUG_STANDARD_MODELING_RULES)
114                                 System.out.println("No connection types -> CANBEMADELEGAL");
115                         return ConnectionJudgement.CANBEMADELEGAL;
116                 }
117
118                 // Validate connection types
119                 Resource possibleConnectionType = null;
120                 Resource chosenConnectionType = null;
121                 String chosenConnectionTypeName = null;
122                 connectionTypeLoop:
123                 for(Resource connectionType : connectionTypes) {
124                         if(Policy.DEBUG_STANDARD_MODELING_RULES)
125                                 System.out.println("Check constraints for " + NameUtils.getSafeName(g, connectionType));
126
127                         switch (evaluateConstraints(g, connectionType, terminals)) {
128                         case ILLEGAL:
129                                 continue connectionTypeLoop;
130
131                         case CANBEMADELEGAL:
132                                 possibleConnectionType = connectionType;
133                                 break;
134
135                         case LEGAL:
136                                 if (chosenConnectionType != null) {
137                                         if (singleDefaultConnectionType) {
138                                                 chosenConnectionType = defaultConnectionType;
139                                                 break connectionTypeLoop;
140                                         }
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;
151                                         }
152                                 } else {
153                                         chosenConnectionType = connectionType;
154                                 }
155                         }
156                 }
157                 
158                 // Create result
159                 if(chosenConnectionType != null) {
160                         ConnectionJudgement judgement = new ConnectionJudgement(chosenConnectionType);
161                         judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;
162                         return judgement;
163                 }
164                 else if(possibleConnectionType != null) {
165                         ConnectionJudgement judgement = new ConnectionJudgement(
166                                         ConnectionJudgementType.CANBEMADELEGAL, possibleConnectionType);
167                         judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;
168                         return judgement;
169                 }
170                 else
171                         return ConnectionJudgement.ILLEGAL;
172         }
173
174         private Collection<Resource> getAllowedConnectionTypes(ReadGraph graph, Collection<Set<Resource>> typeSets) throws DatabaseException {
175                 
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);
183                 
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);
189                                 else
190                                         connectionTypes.retainAll(allowedConnectionTypes);
191                         }
192                 }
193
194                 return connectionTypes;
195                 
196         }
197
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)) {
205                         case ILLEGAL:
206                                 if(Policy.DEBUG_STANDARD_MODELING_RULES)
207                                         System.out.println("    " + cc.getClass().getSimpleName() +
208                                         " -> ILLEGAL");
209                                 return ConnectionJudgementType.ILLEGAL;
210                         case CANBEMADELEGAL:
211                                 if(Policy.DEBUG_STANDARD_MODELING_RULES)
212                                         System.out.println("    " + cc.getClass().getSimpleName() +
213                                         " -> CANBEMADELEGAL");
214                                 legal = false;
215                         default:
216                                 break;
217                         }
218                 }
219                 return legal ? ConnectionJudgementType.LEGAL : ConnectionJudgementType.CANBEMADELEGAL;
220         }
221
222         @Override
223         public ConnectionJudgement judgeConnection(ReadGraph g,
224                         Collection<IConnectionPoint> connectionPoints)
225                         throws DatabaseException {
226                 
227                 return judgeConnection(g, connectionPoints, false);
228                 
229         }
230
231         @Override
232         public Resource computeConnectionType(ReadGraph g,
233                         Collection<IConnectionPoint> connectionPoints)
234                         throws DatabaseException {
235
236                 return judgeConnection(g, connectionPoints, true).connectionType;
237                 
238         }
239         
240         @Override
241         public IAttachmentRelationMap getAttachmentRelations(ReadGraph g,
242                         Resource connection) {
243                 return StandardAttachmentRelationMap.INSTANCE;
244         }
245
246         @Override
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));
265                                 }
266                         }
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())));
273                         }
274                         else
275                                 throw new IllegalArgumentException("Connection point " + cp + " encountered.");
276                 }
277                 
278                 return terminals;
279                 
280         }
281         
282 }