]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/flags/LiftFlag.java
Fixed ComponentTypeCommands.setUnit to support unit == null
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / flags / LiftFlag.java
1 /*******************************************************************************
2  * Copyright (c) 2012 Association for Decentralized Information Management in
3  * 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.modeling.flags;
13
14 import gnu.trove.set.hash.THashSet;
15
16 import java.util.Collections;
17 import java.util.Set;
18
19 import org.simantics.databoard.Bindings;
20 import org.simantics.databoard.annotations.Optional;
21 import org.simantics.databoard.util.Bean;
22 import org.simantics.db.ReadGraph;
23 import org.simantics.db.Resource;
24 import org.simantics.db.Statement;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.utils.NameUtils;
27 import org.simantics.db.exception.DatabaseException;
28 import org.simantics.diagram.content.ConnectionUtil;
29 import org.simantics.diagram.stubs.DiagramResource;
30 import org.simantics.layer0.Layer0;
31 import org.simantics.modeling.ModelingResources;
32 import org.simantics.structural.stubs.StructuralResource2;
33 import org.simantics.structural2.utils.StructuralUtils;
34 import org.simantics.utils.ObjectUtils;
35
36 /**
37  * @author Hannu Niemistö
38  * @author Tuukka Lehtonen
39  */
40 public class LiftFlag {
41
42     private static final boolean DEBUG = false;
43
44     public static class LiftedConnectionPoint extends Bean {
45         @Optional
46         public Resource component;
47         @Optional
48         public Resource componentType;
49         @Optional
50         public Resource connectionPoint;
51         /**
52          * Read through STR.HasAttachmentRelation from configuration connection
53          * point.
54          */
55         @Optional
56         public Resource attachmentRelation;
57     }
58
59     /**
60      * Creates a connection point for the flag. Returns null
61      * if the operation succeeded, otherwise returns an error message.
62      */
63     public static String liftFlag(WriteGraph g, Resource flag) throws DatabaseException {
64         Layer0 L0 = Layer0.getInstance(g);
65         StructuralResource2 STR = StructuralResource2.getInstance(g);
66         DiagramResource DIA = DiagramResource.getInstance(g);
67         ModelingResources MOD = ModelingResources.getInstance(g);
68
69         // Check preconditions
70         if (g.hasStatement(flag, STR.IsJoinedBy))
71             return "Flag is already connected to other flag.";
72         if (g.hasStatement(flag, DIA.IsLiftedAs))
73             return "Flag is already lifted.";
74
75         // Find configuration connection
76         Resource connection = findConfigurationConnection(g, flag);
77         if (connection == null)
78             return "Couldn't find configuration connection.";
79
80         // Find component and connection point
81         LiftedConnectionPoint lcp = calculateLiftedConnectionPointForConnection(g, connection);
82
83         // Validate calculated result
84         if (lcp.component == null)
85             return "Didn't find a component where the flag is connected to.";
86         if (lcp.componentType == null)
87             return "Didn't find an enclosing user component.";
88
89         // Generate default name
90         if (DEBUG)
91             System.out.println("found user component : " + NameUtils.getSafeName(g, lcp.componentType, true));
92         String newName = generateTerminalName(g, lcp);
93         newName = NameUtils.findFreshName(g, newName, lcp.componentType);
94
95         // Create the connection point
96         Resource connectionPoint = g.newResource();
97         Resource connectionPointInv = g.newResource();
98
99         for (Resource superrelation : g.getObjects(lcp.connectionPoint, L0.SubrelationOf)) {
100             g.claim(connectionPoint, L0.SubrelationOf, null, superrelation);
101             g.claim(connectionPointInv, L0.SubrelationOf, null, g.getInverse(superrelation));
102         }
103         for (Resource type : g.getObjects(lcp.connectionPoint, L0.InstanceOf)) {
104             g.claim(connectionPoint, L0.InstanceOf, null, type);
105         }
106         g.claim(connectionPoint, L0.InverseOf, connectionPointInv);
107         g.claimLiteral(connectionPoint, L0.HasName, newName, Bindings.STRING);
108         g.claim(connectionPoint, L0.ConsistsOf, connectionPointInv);
109         g.claimLiteral(connectionPointInv, L0.HasName, "Inverse", Bindings.STRING);
110         g.claim(connectionPoint, L0.HasDomain, lcp.componentType);
111
112         // Copy custom connection point terminal definitions from referenced
113         // lifted connection point. Assertions from types may also bring some
114         // definitions in already.
115         for (Statement terminalStm : g.getStatements(lcp.connectionPoint, MOD.ConnectionRelationToTerminal)) {
116             if (!terminalStm.isAsserted(lcp.connectionPoint)) {
117                 g.claim(connectionPoint, MOD.ConnectionRelationToTerminal, terminalStm.getObject());
118             }
119         }
120
121         StructuralUtils.addConnectionPoint(g, lcp.componentType, connectionPoint);
122
123         g.claim(flag, DIA.IsLiftedAs, connectionPoint);
124         // This is now somewhat redundant, because statement is also added
125         // in mapping. But maybe flag is lifted when mapping is not active?
126         g.claim(connection, STR.Binds, connectionPoint);
127
128         // See platform issue https://www.simantics.org/redmine/issues/3482
129         if (lcp.attachmentRelation != null)
130             g.claim(connectionPoint, STR.HasAttachmentRelation, lcp.attachmentRelation);
131
132         g.claim(lcp.componentType, L0.ConsistsOf, connectionPoint);
133
134         return null;
135     }
136
137     /**
138      * @param g
139      * @param element
140      * @return
141      * @throws DatabaseException
142      */
143     public static Resource findConfigurationConnection(ReadGraph g, Resource element) throws DatabaseException {
144         StructuralResource2 STR = StructuralResource2.getInstance(g);
145         ModelingResources MOD = ModelingResources.getInstance(g);
146
147         for (Resource connector : g.getObjects(element, STR.IsConnectedTo)) {
148             Resource diagramConnection = ConnectionUtil.tryGetConnection(g, connector);
149             if (diagramConnection != null) {
150                 Resource connection = g.getPossibleObject(diagramConnection, MOD.DiagramConnectionToConnection);
151                 if (connection != null)
152                     return connection;
153             }
154             Resource mappedConnection = ConnectionUtil.tryGetMappedConnection(g, connector);
155             if(mappedConnection != null) return mappedConnection;
156         }
157         return null;
158     }
159
160     /**
161      * @param g
162      * @param lcp
163      * @return
164      * @throws DatabaseException 
165      */
166     public static String generateTerminalName(ReadGraph g, LiftedConnectionPoint lcp) throws DatabaseException {
167         String componentName = NameUtils.getSafeName(g, lcp.component);
168         String cpName = NameUtils.getSafeName(g, lcp.connectionPoint);
169
170         StringBuilder sb = new StringBuilder();
171
172         // NOTE: NameUtils.getSafeName never returns null so part of this logic below is a bit useless.
173         if (componentName == null) {
174             if (cpName == null)
175                 sb.append("ConnectionPoint");
176             else
177                 sb.append("Lifted").append(cpName);
178         }
179         else {
180             if (cpName == null)
181                 sb.append(componentName).append("Lifted");
182             else
183                 sb.append(componentName).append("_").append(cpName);
184         }
185
186         return sb.toString();
187     }
188
189     /**
190      * @param graph
191      * @param flag
192      * @return
193      * @throws DatabaseException
194      */
195     public static LiftedConnectionPoint calculateLiftedConnectionPointForFlag(ReadGraph graph, Resource flag)
196             throws DatabaseException {
197         Resource connection = findConfigurationConnection(graph, flag);
198         return connection == null
199                 ? new LiftedConnectionPoint()
200                 : calculateLiftedConnectionPointForConnection(graph, connection);
201     }
202
203     /**
204      * @param graph
205      * @param connection
206      * @return
207      * @throws DatabaseException
208      */
209     public static LiftedConnectionPoint calculateLiftedConnectionPointForConnection(ReadGraph graph, Resource connection)
210             throws DatabaseException {
211         Layer0 L0 = Layer0.getInstance(graph);
212         DiagramResource DIA = DiagramResource.getInstance(graph);
213         ModelingResources MOD = ModelingResources.getInstance(graph);
214         StructuralResource2 STR = StructuralResource2.getInstance(graph);
215
216         if (DEBUG)
217             System.out.println("calculateLiftedConnectionPoint from connection: " + NameUtils.getSafeName(graph, connection, true));
218
219         LiftedConnectionPoint result = new LiftedConnectionPoint();
220
221         findOutputTerminal: for (Statement stat : graph.getStatements(connection, STR.Connects)) {
222             result.component = stat.getObject();
223             result.connectionPoint = graph.getInverse(stat.getPredicate());
224             result.attachmentRelation = graph.getPossibleObject(result.connectionPoint, STR.HasAttachmentRelation);
225
226             if (DEBUG)
227                 System.out.println("  connection point " + NameUtils.getSafeName(graph, result.connectionPoint, true)
228                         + " connects component " + NameUtils.getSafeName(graph, result.component, true)
229                         + " with attachment " + NameUtils.getSafeName(graph, result.attachmentRelation));
230
231             // This code tries to find component and connector behind signals
232             Resource connector = graph.getPossibleObject(result.component, MOD.ComponentToConnector);
233             if (connector != null) {
234                 if (DEBUG)
235                     System.out.println("connector: " + NameUtils.getSafeName(graph, connector, true));
236
237                 for (Statement s : graph.getStatements(connector, STR.Connects)) {
238                     Resource element = s.getObject();
239                     Resource diagramRelation = graph.getInverse(s.getPredicate());
240                     Resource componentCandidate = graph.getPossibleObject(element, MOD.ElementToComponent);
241                     Resource cpCandidate = graph.getPossibleObject(diagramRelation, MOD.DiagramConnectionRelationToConnectionRelation);
242
243                     if (DEBUG) {
244                         System.out.println("element: " + NameUtils.getSafeName(graph, element, true));
245                         System.out.println("diagram connection relation: " + NameUtils.getSafeName(graph, diagramRelation, true));
246                         System.out.println("component candidate: " + NameUtils.getSafeName(graph, componentCandidate, true));
247                         System.out.println("connection point candidate: " + NameUtils.getSafeName(graph, cpCandidate, true));
248                     }
249
250                     if (componentCandidate != null && cpCandidate != null) {
251                         result.component = componentCandidate;
252                         result.connectionPoint = cpCandidate;
253                         result.attachmentRelation = graph.getPossibleObject(cpCandidate, STR.HasAttachmentRelation);
254
255                         if (DEBUG)
256                             System.out.println("attachmentRelation: " + NameUtils.getSafeName(graph, result.attachmentRelation));
257
258                         if (result.attachmentRelation != null
259                                 && graph.isSubrelationOf(result.attachmentRelation, DIA.HasTailConnector))
260                             // Found an output terminal, this is the one we want.
261                             break findOutputTerminal;
262                     }
263                 }
264             }
265         }
266
267         // Find component type
268         Resource componentType = null;
269         for (Resource curComponent = result.component; true;) {
270             componentType = graph.getPossibleObject(curComponent, STR.Defines);
271             if (componentType != null) {
272                 result.componentType = componentType;
273                 break;
274             }
275             curComponent = graph.getPossibleObject(curComponent, L0.PartOf);
276             if (curComponent == null)
277                 break;  
278         }
279
280         return result;
281     }
282
283     /**
284      * @param graph
285      * @param componentType
286      * @param connectionPoint
287      * @param proper
288      * @return <code>null</code> if connection point is valid
289      * @throws DatabaseException
290      */
291     public static String isConnectionPointValid(ReadGraph graph, Resource componentType, Resource connectionPoint,
292             LiftedConnectionPoint proper) throws DatabaseException {
293         Layer0 L0 = Layer0.getInstance(graph);
294         DiagramResource DIA = DiagramResource.getInstance(graph);
295         ModelingResources MOD = ModelingResources.getInstance(graph);
296         StructuralResource2 STR = StructuralResource2.getInstance(graph);
297
298         Resource existingHasAttachment = graph.getPossibleObject(connectionPoint, STR.HasAttachmentRelation);
299         if (!ObjectUtils.objectEquals(existingHasAttachment, proper.attachmentRelation))
300             return "Wrong connection point (" + connectionPoint + ") attachment relation: is " + existingHasAttachment
301                     + ", should be " + proper.attachmentRelation;
302
303         {
304             Set<Resource> existingTypes = new THashSet<Resource>(graph.getObjects(connectionPoint, L0.InstanceOf));
305             Set<Resource> properTypes = new THashSet<Resource>(proper.connectionPoint != null
306                     ? graph.getObjects(proper.connectionPoint, L0.InstanceOf)
307                             : Collections.<Resource>emptySet());
308             if (!existingTypes.equals(properTypes))
309                 return "Incorrect connection point relation (" + connectionPoint + ") types (existing " + existingTypes + " vs. proper " + properTypes + ")";
310         }
311
312         Resource diagramConnectionPoint = graph.getPossibleObject(connectionPoint, MOD.ConnectionRelationToDiagramConnectionRelation);
313         if (diagramConnectionPoint != null) {
314             Set<Resource> existingTypes = new THashSet<Resource>(graph.getObjects(diagramConnectionPoint, L0.InstanceOf));
315             Set<Resource> properTypes = new THashSet<Resource>(proper.connectionPoint != null
316                     ? graph.getObjects(proper.connectionPoint, MOD.ImpliesDiagramConnectionRelationType)
317                             : Collections.<Resource>emptySet());
318             if (!existingTypes.equals(properTypes))
319                 return "Incorrect diagram connection point relation (" + diagramConnectionPoint + ") types (existing " + existingTypes + " vs. proper " + properTypes + ")";
320
321             Set<Resource> properTerminalTypes = new THashSet<Resource>(graph.getObjects(connectionPoint, MOD.ConnectionRelationToTerminal));
322             for (Resource terminal : graph.getObjects(diagramConnectionPoint, DIA.HasConnectionPoint_Inverse)) {
323                 Set<Resource> existingTerminalTypes = new THashSet<Resource>(graph.getObjects(terminal, L0.InstanceOf));
324                 if (!existingTerminalTypes.equals(properTerminalTypes))
325                     return "Incorrect diagram connection point relation types (existing " + existingTypes + " vs. proper " + properTypes + ")";
326             }
327         }
328
329         return null;
330     }
331
332     /**
333      * @param graph
334      * @param componentType
335      * @param connectionPoint
336      * @param proper
337      * @return <code>null</code> if connection point was properly validated
338      * @throws DatabaseException
339      */
340     public static String validateConnectionPoint(WriteGraph graph, Resource componentType, Resource connectionPoint,
341             LiftedConnectionPoint proper) throws DatabaseException {
342
343         Layer0 L0 = Layer0.getInstance(graph);
344         DiagramResource DIA = DiagramResource.getInstance(graph);
345         ModelingResources MOD = ModelingResources.getInstance(graph);
346         StructuralResource2 STR = StructuralResource2.getInstance(graph);
347
348         // Fix HasAttachmentRelation of connectionPoint
349         Resource existingHasAttachment = graph.getPossibleObject(connectionPoint, STR.HasAttachmentRelation);
350         if (!ObjectUtils.objectEquals(existingHasAttachment, proper.attachmentRelation)) {
351             graph.deny(connectionPoint, STR.HasAttachmentRelation);
352             if (proper.attachmentRelation != null)
353                 graph.claim(connectionPoint, STR.HasAttachmentRelation, proper.attachmentRelation);
354         }
355
356         // Fix InstanceOf's of connectionPoint
357         fixDirectTypes(graph, connectionPoint, proper.connectionPoint != null
358                 ? new THashSet<Resource>( graph.getObjects(proper.connectionPoint, L0.InstanceOf) )
359                         : Collections.<Resource>emptySet());
360
361         // Fix InstanceOf's of connection point's diagram connection relation
362         Resource diagramConnectionPoint = graph.getPossibleObject(connectionPoint, MOD.ConnectionRelationToDiagramConnectionRelation);
363         if (diagramConnectionPoint != null) {
364             fixDirectTypes(graph, diagramConnectionPoint, proper.connectionPoint != null
365                     ? new THashSet<Resource>( graph.getObjects(proper.connectionPoint, MOD.ImpliesDiagramConnectionRelationType) )
366                             : Collections.<Resource>emptySet());
367
368             // Fix InstanceOf's of connection point's diagram connection relation's symbol terminal
369             Set<Resource> properTerminalTypes = new THashSet<Resource>(graph.getObjects(connectionPoint, MOD.ConnectionRelationToTerminal));
370             for (Resource terminal : graph.getObjects(diagramConnectionPoint, DIA.HasConnectionPoint_Inverse)) {
371                 fixDirectTypes(graph, terminal, properTerminalTypes);
372             }
373         }
374
375         return null;
376     }
377
378     /**
379      * @param graph
380      * @param r
381      * @param properTypes
382      * @throws DatabaseException
383      */
384     private static void fixDirectTypes(WriteGraph graph, Resource r, Set<Resource> properTypes) throws DatabaseException {
385         Layer0 L0 = Layer0.getInstance(graph);
386         Set<Resource> existingTypes = new THashSet<Resource>( graph.getObjects(r, L0.InstanceOf) );
387         if (!existingTypes.equals(properTypes)) {
388             Set<Resource> addTypes = new THashSet<Resource>(properTypes);
389             properTypes.removeAll(existingTypes);
390             existingTypes.removeAll(properTypes);
391             for (Resource remove : existingTypes)
392                 graph.deny(r, L0.InstanceOf, remove);
393             for (Resource add : addTypes)
394                 graph.claim(r, L0.InstanceOf, null, add);
395         }
396     }
397
398 }