StructuralUtils.isImmutable now takes L0.readOnly of input into account
[simantics/platform.git] / bundles / org.simantics.structural2 / src / org / simantics / structural2 / utils / StructuralUtils.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.utils;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Set;
19
20 import org.simantics.databoard.Bindings;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.RequestProcessor;
23 import org.simantics.db.Resource;
24 import org.simantics.db.Statement;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.CommentMetadata;
27 import org.simantics.db.common.request.ObjectsWithType;
28 import org.simantics.db.common.request.PossibleTypedParent;
29 import org.simantics.db.common.utils.NameUtils;
30 import org.simantics.db.exception.DatabaseException;
31 import org.simantics.db.layer0.exception.MissingVariableException;
32 import org.simantics.db.layer0.request.InstantiateRequest;
33 import org.simantics.db.layer0.util.Layer0Utils;
34 import org.simantics.db.layer0.variable.Variable;
35 import org.simantics.layer0.Layer0;
36 import org.simantics.structural.stubs.StructuralResource2;
37 import org.simantics.structural2.Functions.StructuralOverrideData;
38 import org.simantics.structural2.internal.queries.ConnectedTo;
39 import org.simantics.structural2.internal.queries.RelatedConnections;
40 import org.simantics.structural2.internal.queries.RelatedConnectionsOfConnectionJoin;
41 import org.simantics.structural2.queries.Terminal;
42 import org.simantics.structural2.variables.Connection;
43 import org.simantics.utils.datastructures.Pair;
44
45 import gnu.trove.set.hash.THashSet;
46
47 /**
48  * A utility class for manipulating structural models.
49  * 
50  * @author Hannu Niemistö
51  */
52 public class StructuralUtils {
53     
54     public static enum StructuralComponentClass {
55
56         PRIMITIVE,REPLACEABLE,DEFINED,PROCEDURAL;
57
58         public static StructuralComponentClass get(ReadGraph graph, Resource componentType) throws DatabaseException {
59             StructuralResource2 STR = StructuralResource2.getInstance(graph);
60             Set<Resource> types = graph.getTypes(componentType);
61             if(types.contains(STR.ProceduralComponentType))
62                 return StructuralComponentClass.PROCEDURAL;
63             else if(graph.hasStatement(componentType, STR.IsDefinedBy))
64                 return StructuralComponentClass.DEFINED;
65             else if(types.contains(STR.ReplaceableDefinedComponentType))
66                 return StructuralComponentClass.REPLACEABLE;
67             else
68                 return StructuralComponentClass.PRIMITIVE;
69         }
70
71     }
72
73     public static Collection<Resource> getConnectionRelations(ReadGraph graph, Resource componentType) throws DatabaseException {
74         Layer0 L0 = Layer0.getInstance(graph);
75         StructuralResource2 STR = StructuralResource2.getInstance(graph);
76         return graph.syncRequest(new ObjectsWithType(componentType, L0.DomainOf, STR.ConnectionRelation));
77     }
78
79     public static Collection<Resource> getPropertyRelations(ReadGraph graph, Resource componentType) throws DatabaseException {
80         Layer0 L0 = Layer0.getInstance(graph);
81         ArrayList<Resource> result = new ArrayList<Resource>();
82         for(Resource relation : graph.getObjects(componentType, L0.DomainOf))
83             if(graph.isSubrelationOf(relation, L0.HasProperty))
84                 result.add(relation);
85         return result;
86     }
87     
88     public static boolean isDomainOfRelation(ReadGraph graph,
89             Resource componentType, Resource connectionRelation) throws DatabaseException {
90         Layer0 L0 = Layer0.getInstance(graph);
91         for(Resource domain : graph.getObjects(connectionRelation, L0.HasDomain))
92             if(graph.isInheritedFrom(componentType, domain))
93                 return true;
94         return false;
95     }
96     
97     public static void addConnectionPoint(WriteGraph g, Resource componentType,
98             Resource connectionPoint) throws DatabaseException {
99         Layer0 L0 = Layer0.getInstance(g);
100         g.claim(connectionPoint, L0.HasDomain, componentType);
101     }
102     
103     public static Resource createConnectionPoint(WriteGraph g, Resource componentType, Resource copy) throws DatabaseException {
104         String proposition = NameUtils.getSafeName(g, copy);
105         String newName = NameUtils.findFreshName(g, proposition, componentType);
106         return createConnectionPoint(g, componentType, copy, newName);
107     }
108     
109     public static Resource createConnectionPoint(WriteGraph g, Resource componentType, Resource copy, String name) throws DatabaseException {
110         return createConnectionPointP(g, componentType, copy, name).first;
111     }
112     
113     public static Pair<Resource,Resource> createConnectionPointP(WriteGraph g, Resource componentType, Resource copy, String name) throws DatabaseException {
114         
115         Layer0 L0 = Layer0.getInstance(g);
116         StructuralResource2 STR = StructuralResource2.getInstance(g);
117
118         // Create the connection point
119         Resource connectionPoint = g.newResource();
120         Resource connectionPointInv = g.newResource();
121
122         for (Resource superrelation : g.getObjects(copy, L0.SubrelationOf)) {
123             g.claim(connectionPoint, L0.SubrelationOf, null, superrelation);
124             Resource inverse = g.getPossibleInverse(superrelation);
125             if (inverse != null)
126                 g.claim(connectionPointInv, L0.SubrelationOf, null, inverse);
127         }
128         for (Resource type : g.getObjects(copy, L0.InstanceOf)) {
129             g.claim(connectionPoint, L0.InstanceOf, null, type);
130         }
131         for (Resource attachment : g.getObjects(copy, STR.HasAttachmentRelation)) {
132             g.claim(connectionPoint, STR.HasAttachmentRelation, attachment);
133         }
134         for (Statement stm : g.getStatements(copy, STR.AllowsConnectionType)) {
135             if (!stm.isAsserted(copy)) {
136                 g.claim(connectionPoint, stm.getPredicate(), stm.getObject());
137             }
138         }
139
140         g.claim(connectionPoint, L0.InverseOf, connectionPointInv);
141         g.claimLiteral(connectionPoint, L0.HasName, name, Bindings.STRING);
142         g.claim(connectionPoint, L0.ConsistsOf, connectionPointInv);
143         g.claimLiteral(connectionPointInv, L0.HasName, "Inverse", Bindings.STRING);
144
145         StructuralUtils.addConnectionPoint(g, componentType, connectionPoint);
146
147         g.claim(componentType, L0.ConsistsOf, connectionPoint);
148
149         return Pair.make(connectionPoint, connectionPointInv);
150         
151     }
152
153     /**
154      * Creates a new component and the templates associated with it.
155      * This method does not check whether there already exists 
156      * a component with the same name and parent.
157      * @param g
158      * @param parent The parent composite of the new component.
159      * @param name The name of the new component.
160      * @param componentType The type of the new component.
161      * @return
162      * @throws DatabaseException
163      */
164     public static Resource newComponent(WriteGraph g, Resource parent, String name, Resource componentType) throws DatabaseException {
165         Layer0 L0 = Layer0.getInstance(g);
166         
167         HashMap<String,Object> parameters = new HashMap<String,Object>();
168         parameters.put("parent", parent);
169
170         InstantiateRequest ir = new InstantiateRequest(componentType, parameters);
171         Resource component = ir.perform(g);        
172         g.claim(component, L0.HasName, Layer0Utils.literal(g, name));
173         g.claim(component, L0.HasLabel, Layer0Utils.literal(g, ""));
174         g.claim(parent, L0.ConsistsOf, component);
175         Layer0Utils.claimNewIdentifier(g, component, true);
176         // Add comment to change set.
177         CommentMetadata cm = g.getMetadata(CommentMetadata.class);
178         g.addMetadata(cm.add("Created component " + component));
179         return component;
180     }
181     
182     /**
183      * Returns all child components of a composite.
184      */
185     public static Collection<Resource> getChildComponents(ReadGraph g, Resource parent) throws DatabaseException {
186         Layer0 L0 = Layer0.getInstance(g);
187         StructuralResource2 STR = StructuralResource2.getInstance(g);
188         
189         Collection<Resource> allChildren = g.getObjects(parent, L0.ConsistsOf);
190         ArrayList<Resource> result = new ArrayList<Resource>(allChildren.size());
191         for(Resource child : allChildren)
192             // Composite may contain other things than components, therefore we must do checking
193             if(g.isInstanceOf(child, STR.Component))
194                 result.add(child);
195         return result;
196     }
197
198     /**
199      * Returns the component type of the given component or null if the 
200      * parameter is not a component.
201      */
202     @Deprecated
203     public static Resource getComponentType(ReadGraph g, Resource component) throws DatabaseException {
204         StructuralResource2 STR = StructuralResource2.getInstance(g);
205         return g.getPossibleType(component, STR.Component);
206     }
207     
208     /**
209      * Returns the definitions of the component type or null, if the component
210      * type does not have a definition.
211      */
212     public static Resource getComponentTypeDefinition(ReadGraph g, Resource componentType) throws DatabaseException {
213         StructuralResource2 STR = StructuralResource2.getInstance(g);
214         return g.getPossibleObject(componentType, STR.IsDefinedBy);
215     }
216     
217     /**
218      * Returns all (component,connectionRelation) pairs that are connected
219      * to the given component and connectionRelation.
220      * @param component
221      * @param bindingRelation
222      * @return
223      */
224     public static Set<Terminal> getRelatedTerminals(RequestProcessor g, Resource component, Resource connectionRelation) throws DatabaseException {
225         return g.syncRequest(new ConnectedTo(component, connectionRelation));
226     }
227     
228     /**
229      * Returns all connections that are reachable from the given connection
230      * with IsJoinedBy-relation including the given connection itself. 
231      */ 
232     public static Set<Resource> getRelatedConnections(RequestProcessor g, Resource connection) throws DatabaseException {
233         return g.syncRequest(new RelatedConnections(connection));
234     }
235     
236     /**
237      * Returns all connections that are reachable from the given connection join.     * 
238      */ 
239     public static Set<Resource> getRelatedConnectionsOfConnectionJoin(RequestProcessor g, Resource connectionJoin) throws DatabaseException {
240         return g.syncRequest(new RelatedConnectionsOfConnectionJoin(connectionJoin));
241     }
242     
243     /**
244      * Returns all connections that are reachable from the given connection join
245      * without visiting the resources in the collection excludedConnections.
246      */
247     public static Set<Resource> getRelatedConnectionsOfConnectionJoin(RequestProcessor g, Resource connectionJoin, Collection<Resource> excludedConnections) throws DatabaseException {
248         return g.syncRequest(new RelatedConnectionsOfConnectionJoin(connectionJoin, excludedConnections));
249     }
250     
251     /**
252      * Returns true if the given composite is a parent of some/all components 
253      * where the connection is attached to.
254      */
255     public static boolean isConnectionInComposite(ReadGraph g, Resource connection, Resource composite) throws DatabaseException {
256         StructuralResource2 STR = StructuralResource2.getInstance(g);
257         Layer0 L0 = Layer0.getInstance(g);
258         Collection<Resource> connects = g.getObjects(connection, STR.Connects);
259         if (!connects.isEmpty()) {
260             for(Resource component : connects)
261                 for(Resource parent : g.getObjects(component, L0.PartOf))
262                     if(parent.equals(composite))
263                         return true;
264         } else {
265             Set<Resource> joinedComposites = null;
266             for(Resource join : g.getObjects(connection, STR.IsJoinedBy)) {
267                 Collection<Resource> joined = g.getObjects(join, STR.JoinsComposite);
268                 if (joinedComposites == null) {
269                     joinedComposites = new THashSet<Resource>(joined);
270                 } else {
271                     joinedComposites.retainAll(joined);
272                 }
273             }
274             if (joinedComposites != null && joinedComposites.size() == 1) {
275                 return joinedComposites.contains(composite);
276             }
277         }
278         return false;
279     }
280     
281     public static Variable getConnectionPoint(ReadGraph graph, Variable component, Resource relation) throws DatabaseException {
282         Layer0 L0 = Layer0.getInstance(graph);
283         String relationName = graph.getRelatedValue(relation, L0.HasName, Bindings.STRING);
284         return component.getProperty(graph, relationName);
285     }
286
287     public static Variable getPossibleConnectionPoint(ReadGraph graph, Variable component, Resource relation) throws DatabaseException {
288         try {
289                 return getConnectionPoint(graph, component, relation);
290         } catch (MissingVariableException e) {
291                 return null;
292         }
293     }
294
295     public static Variable getPossibleConnectionTo(ReadGraph graph, Variable component, Resource relation) throws DatabaseException {
296         Variable property = component.getPossibleProperty(graph, relation);
297         if(property == null) return null;
298         Connection conn = property.getPossibleValue(graph);
299         if(conn == null) return null;
300         Collection<Variable> cps = conn.getConnectionPoints(graph, null);
301         if(cps.size() == 2) {
302                 for(Variable var : cps) {
303                         if(property.equals(var)) continue;
304                         return var;
305                 }
306         }
307         return null;
308     }
309
310     public static boolean isImmutable(ReadGraph graph, Resource r) throws DatabaseException {
311         // Marking a resource L0.readOnly also makes it immutable
312         if (graph.isImmutable(r) || Layer0Utils.isMarkedReadOnly(graph, r))
313             return true;
314         StructuralResource2 STR = StructuralResource2.getInstance(graph);
315         Resource uc = graph.syncRequest(new PossibleTypedParent(r, STR.ComponentType));
316         return  // Anything under a published or locked user component is published as well
317                 (uc != null && (Layer0Utils.isPublished(graph, uc)
318                          || graph.hasStatement(uc, STR.ComponentType_Locked)))
319                 // Anything under a published container (shared library) is published as well
320                 || Layer0Utils.isContainerPublished(graph, r)
321                 ;
322     }
323     
324     public static List<Variable> structuralConnectionConnectionPoints(ReadGraph graph, Connection conn, Resource relationType) throws DatabaseException {
325         return new ArrayList<Variable>(conn.getConnectionPoints(graph, relationType));
326     }
327     
328     public static Resource structuralTypeResource(ReadGraph graph, Variable component, Resource baseType) throws DatabaseException {
329         StructuralOverrideData od = StructuralOverrideData.compute(graph, component);
330         if (od != null)
331             return od.type();
332         return null;
333     }
334     
335     public static Resource getComponentType(ReadGraph graph, Variable configuration, Resource component) throws DatabaseException {
336         Variable componentVariable = configuration.browse(graph, component);
337         return componentVariable.getType(graph);
338     }
339
340     public static Resource getPossibleComponentType(ReadGraph graph, Variable configuration, Resource component) throws DatabaseException {
341         Variable componentVariable = configuration.browsePossible(graph, component);
342         if(componentVariable == null) return null;
343         StructuralResource2 STR = StructuralResource2.getInstance(graph);
344         return componentVariable.getPossibleType(graph, STR.Component);
345     }
346
347
348 }