InputStream returns -1 on EOF instead of throwing IOException
[simantics/platform.git] / bundles / org.simantics.structural2 / src / org / simantics / structural2 / modelingRules / StandardModelingRules.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.structural2.modelingRules;\r
13 \r
14 \r
15 import gnu.trove.set.hash.THashSet;\r
16 \r
17 import java.util.ArrayList;\r
18 import java.util.Collection;\r
19 import java.util.Set;\r
20 \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
30 \r
31 public class StandardModelingRules extends AbstractModelingRules {\r
32 \r
33         Layer0 L0;\r
34         StructuralResource2 STR;\r
35 \r
36         public StandardModelingRules(ReadGraph g) {\r
37                 L0 = Layer0.getInstance(g);\r
38                 STR = StructuralResource2.getInstance(g);\r
39         }\r
40 \r
41         private ConnectionJudgement judgeConnection(ReadGraph g,\r
42                         Collection<IConnectionPoint> connectionPoints, boolean allowExisting)\r
43                         throws DatabaseException {\r
44 \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
51                         return judgement;\r
52                 }\r
53 \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
61                                                 continue;\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
66                                         }\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
72                             }   \r
73                                         }\r
74                                 }\r
75                         }\r
76                 }\r
77                 \r
78                 // Translate all connection points to terminals\r
79                 Set<CPTerminal> terminals = resolveTerminals(g, connectionPoints);\r
80                 \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
86 \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
97 \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
106                                 }\r
107                         }\r
108                 }\r
109                 \r
110                 Collection<Resource> connectionTypes = getAllowedConnectionTypes(g, typeSets);\r
111                 \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
116                 }\r
117 \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
126 \r
127                         switch (evaluateConstraints(g, connectionType, terminals)) {\r
128                         case ILLEGAL:\r
129                                 continue connectionTypeLoop;\r
130 \r
131                         case CANBEMADELEGAL:\r
132                                 possibleConnectionType = connectionType;\r
133                                 break;\r
134 \r
135                         case LEGAL:\r
136                                 if (chosenConnectionType != null) {\r
137                                         if (singleDefaultConnectionType) {\r
138                                                 chosenConnectionType = defaultConnectionType;\r
139                                                 break connectionTypeLoop;\r
140                                         }\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
151                                         }\r
152                                 } else {\r
153                                         chosenConnectionType = connectionType;\r
154                                 }\r
155                         }\r
156                 }\r
157                 \r
158                 // Create result\r
159                 if(chosenConnectionType != null) {\r
160                         ConnectionJudgement judgement = new ConnectionJudgement(chosenConnectionType);\r
161                         judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;\r
162                         return judgement;\r
163                 }\r
164                 else if(possibleConnectionType != null) {\r
165                         ConnectionJudgement judgement = new ConnectionJudgement(\r
166                                         ConnectionJudgementType.CANBEMADELEGAL, possibleConnectionType);\r
167                         judgement.attachmentRelations = StandardAttachmentRelationMap.INSTANCE;\r
168                         return judgement;\r
169                 }\r
170                 else\r
171                         return ConnectionJudgement.ILLEGAL;\r
172         }\r
173 \r
174         private Collection<Resource> getAllowedConnectionTypes(ReadGraph graph, Collection<Set<Resource>> typeSets) throws DatabaseException {\r
175                 \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
183                 \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
189                                 else\r
190                                         connectionTypes.retainAll(allowedConnectionTypes);\r
191                         }\r
192                 }\r
193 \r
194                 return connectionTypes;\r
195                 \r
196         }\r
197 \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
203                         case ILLEGAL:\r
204                                 if(Policy.DEBUG_STANDARD_MODELING_RULES)\r
205                                         System.out.println("    " + cc.getClass().getSimpleName() +\r
206                                         " -> ILLEGAL");\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
212                                 legal = false;\r
213                         default:\r
214                                 break;\r
215                         }\r
216                 }\r
217                 return legal ? ConnectionJudgementType.LEGAL : ConnectionJudgementType.CANBEMADELEGAL;\r
218         }\r
219 \r
220         @Override\r
221         public ConnectionJudgement judgeConnection(ReadGraph g,\r
222                         Collection<IConnectionPoint> connectionPoints)\r
223                         throws DatabaseException {\r
224                 \r
225                 return judgeConnection(g, connectionPoints, false);\r
226                 \r
227         }\r
228 \r
229         @Override\r
230         public Resource computeConnectionType(ReadGraph g,\r
231                         Collection<IConnectionPoint> connectionPoints)\r
232                         throws DatabaseException {\r
233 \r
234                 return judgeConnection(g, connectionPoints, true).connectionType;\r
235                 \r
236         }\r
237         \r
238         @Override\r
239         public IAttachmentRelationMap getAttachmentRelations(ReadGraph g,\r
240                         Resource connection) {\r
241                 return StandardAttachmentRelationMap.INSTANCE;\r
242         }\r
243 \r
244         @Override\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
263                                 }\r
264                         }\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
271                         }\r
272                         else\r
273                                 throw new IllegalArgumentException("Connection point " + cp + " encountered.");\r
274                 }\r
275                 \r
276                 return terminals;\r
277                 \r
278         }\r
279         \r
280 }\r