]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.common/src/org/simantics/db/common/utils/CommonDBUtils.java
Set resources immutable
[simantics/platform.git] / bundles / org.simantics.db.common / src / org / simantics / db / common / utils / CommonDBUtils.java
1 package org.simantics.db.common.utils;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Set;
9
10 import org.simantics.databoard.Bindings;
11 import org.simantics.db.ReadGraph;
12 import org.simantics.db.Resource;
13 import org.simantics.db.Statement;
14 import org.simantics.db.WriteGraph;
15 import org.simantics.db.common.procedure.adapter.DirectStatementProcedure;
16 import org.simantics.db.common.request.IsParent;
17 import org.simantics.db.common.request.ObjectsWithType;
18 import org.simantics.db.common.request.PossibleChild;
19 import org.simantics.db.common.request.PossibleObjectWithType;
20 import org.simantics.db.common.request.PossibleOwner;
21 import org.simantics.db.common.request.RuntimeEnvironmentRequest;
22 import org.simantics.db.exception.DatabaseException;
23 import org.simantics.db.exception.InvalidResourceReferenceException;
24 import org.simantics.db.service.ClusterUID;
25 import org.simantics.db.service.ClusteringSupport;
26 import org.simantics.db.service.DirectQuerySupport;
27 import org.simantics.db.service.SerialisationSupport;
28 import org.simantics.db.service.XSupport;
29 import org.simantics.layer0.Layer0;
30 import org.simantics.scl.compiler.environment.Environments;
31 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
32 import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
33 import org.simantics.scl.compiler.types.Type;
34 import org.simantics.utils.datastructures.collections.CollectionUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import gnu.trove.list.array.TIntArrayList;
39 import gnu.trove.procedure.TIntProcedure;
40 import gnu.trove.set.hash.THashSet;
41 import gnu.trove.set.hash.TIntHashSet;
42
43 public class CommonDBUtils {
44
45         private static final Logger LOGGER = LoggerFactory.getLogger(CommonDBUtils.class);
46
47         public static boolean isParent(ReadGraph graph, Resource possibleParent, Resource possibleChild) throws DatabaseException {
48                 return graph.sync(new IsParent(possibleParent, possibleChild));
49         }
50         
51         public static Resource parent(ReadGraph graph, Resource child) throws DatabaseException {
52                 return graph.getSingleObject(child, Layer0.getInstance(graph).PartOf);
53         }
54         
55         public static String possibleRelatedString(ReadGraph graph, Resource subject, Resource relation) throws DatabaseException {
56                 return graph.getPossibleRelatedValue(subject, relation, Bindings.STRING);
57         }
58
59         public static Integer possibleRelatedInteger(ReadGraph graph, Resource subject, Resource relation) throws DatabaseException {
60                 return graph.getPossibleRelatedValue(subject, relation, Bindings.INTEGER);
61         }
62
63     public static Resource getPossibleOwner(ReadGraph graph, Resource resource) throws DatabaseException {
64         return graph.syncRequest(new PossibleOwner(resource));
65     }
66     
67     /**
68      * Finds a resource that is reachable from both input resources by as short as possible IsOwnedBy chain
69      */
70     public static Resource commonAncestor(ReadGraph graph, Resource r1, Resource r2) throws DatabaseException {
71         if(r1.equals(r2))
72             return r1;
73         
74         Layer0 L0 = Layer0.getInstance(graph);
75         HashSet<Resource> visited = new HashSet<Resource>();
76         visited.add(r1);
77         visited.add(r2);
78         while(true) {
79             if(r1 != null) {
80                 r1 = graph.getPossibleObject(r1, L0.IsOwnedBy);
81                 if(r1 != null && !visited.add(r1))
82                     return r1;
83             }
84             else if(r2 == null)
85                 return null;
86             if(r2 != null) {
87                 r2 = graph.getPossibleObject(r2, L0.IsOwnedBy);
88                 if(r2 != null && !visited.add(r2))
89                     return r2;
90             }
91         }
92     }
93     
94     private static Collection<Resource> getDirectOwners(ReadGraph graph, Resource resource) throws DatabaseException {
95         // TODO is necessary?
96         if(resource.equals(graph.getRootLibrary()))
97             return Collections.emptyList();
98         
99         Layer0 L0 = Layer0.getInstance(graph);
100         
101         Collection<Resource> owners = graph.getObjects(resource, L0.IsOwnedBy);
102         if(owners.isEmpty()) {
103             owners = new THashSet<Resource>();
104             
105             // If there are no owners, collect resources referring to this resource by IsRelatedTo
106             for(Statement statement : graph.getStatements(resource, L0.IsWeaklyRelatedTo)) {
107                 Resource inverse = graph.getPossibleInverse(statement.getPredicate());
108                 if(inverse != null) {
109                     if(graph.isSubrelationOf(inverse, L0.IsRelatedTo)) {
110                         // Filter away tags
111                         if(resource.equals(statement.getObject()))
112                             continue;
113                         owners.add(statement.getObject());
114                     }
115                 }
116             }
117         }
118         else if(owners.size() > 1) {
119             // TODO: getObjects returns duplicate entries (https://www.simantics.org/redmine/issues/4885) and therefore direct is Set<Resource>.
120             // Fix getObjects to not return duplicate entries
121             owners = new HashSet<Resource>(owners);
122         }
123
124         return owners;
125     }
126     
127     /**
128      * This method didn't have a clear specification and therefore should not be used. Use instead
129      * the methods in the class {@link NearestOwnerFinder}.
130      */
131     @Deprecated
132     public static Resource getNearestOwner(ReadGraph graph, Collection<Resource> resources) throws DatabaseException {
133         if(resources.size() == 1)
134             return NearestOwnerFinder.getNearestOwner(graph, resources.iterator().next());
135         else
136             return NearestOwnerFinder.getNearestOwnerFromDirectOwners(graph, resources);
137     }
138     
139     public static Resource getClusterSetForNewResource(ReadGraph graph, Resource ... resources) throws DatabaseException {
140
141         if(resources.length == 1) return getClusterSetForNewResource(graph, resources[0]);
142         
143         Resource owner = NearestOwnerFinder.getNearestOwnerFromDirectOwners(graph, CollectionUtils.toList(resources));
144         if(owner == null) return null;
145         return getClusterSetForNewResource(graph, owner, new HashSet<Resource>());
146         
147     }
148     
149     public static Resource getClusterSetForNewResource(ReadGraph graph, Collection<Resource> resources) throws DatabaseException {
150
151         if(resources.size() == 1) return getClusterSetForNewResource(graph, resources.iterator().next());
152
153         Resource owner = NearestOwnerFinder.getNearestOwnerFromDirectOwners(graph, resources);
154         return getClusterSetForNewResource(graph, owner, new HashSet<Resource>());
155         
156     }
157     
158     public static Resource getClusterSetForNewResource(ReadGraph graph, Resource resource, Set<Resource> visited) throws DatabaseException {
159         
160         ClusteringSupport cs = graph.getService(ClusteringSupport.class);
161         if(cs.isClusterSet(resource)) return resource;
162         
163         Resource owner = getPossibleOwner(graph, resource);
164         
165         if(owner == null || owner == resource) return null;
166         if(!visited.add(owner)) return null;
167
168         return getClusterSetForNewResource(graph, owner, visited);
169         
170     }
171
172     public static Resource getClusterSetForNewResource(ReadGraph graph, Resource r) throws DatabaseException {
173         return getClusterSetForNewResource(graph, r, new HashSet<Resource>());
174     }
175
176
177     public static void selectClusterSet(WriteGraph graph, Collection<Resource> resources) throws DatabaseException {
178         Resource clusterSet = getClusterSetForNewResource(graph, resources);
179         if(clusterSet == null) clusterSet = graph.getRootLibrary();
180         graph.setClusterSet4NewResource(clusterSet);
181     }
182
183     public static void selectClusterSet(WriteGraph graph, Resource ... resources) throws DatabaseException {
184         Resource clusterSet = getClusterSetForNewResource(graph, resources);
185         if(clusterSet == null) clusterSet = graph.getRootLibrary();
186         graph.setClusterSet4NewResource(clusterSet);
187     }
188     
189     public static void selectClusterSet(WriteGraph graph, Resource resource) throws DatabaseException {
190         Resource clusterSet = getClusterSetForNewResource(graph, resource);
191         if(clusterSet == null) clusterSet = graph.getRootLibrary();
192         graph.setClusterSet4NewResource(clusterSet);
193     }
194     
195     public static List<Resource> objectsWithType(ReadGraph graph, Resource subject, Resource relation, Resource type) throws DatabaseException {
196         return new ArrayList<Resource>(graph.syncRequest(new ObjectsWithType(subject, relation, type)));
197     }
198
199     public static Resource possibleObjectWithType(ReadGraph graph, Resource subject, Resource relation, Resource type) throws DatabaseException {
200         return graph.syncRequest(new PossibleObjectWithType(subject, relation, type));
201     }
202
203         public static List<ClusterUID> listClusters(ReadGraph graph) throws DatabaseException {
204         XSupport xs = graph.getService(XSupport.class);
205         ClusterUID uids[] = xs.listClusters();
206         ArrayList<ClusterUID> result = new ArrayList<>(uids.length);
207         for(ClusterUID uid : uids) result.add(uid);
208         return result;
209     }
210         
211         public static List<Resource> resourcesByCluster(ReadGraph graph, ClusterUID uid) throws DatabaseException {
212         SerialisationSupport ss = graph.getService(SerialisationSupport.class);
213         ArrayList<Resource> result = new ArrayList<Resource>();
214         // Index 0 is illegal
215                 for(int i=1;i<1<<12;i++) {
216                         try {
217                                 result.add(ss.getResource(uid.toRID(i)));
218                         } catch (InvalidResourceReferenceException e) {
219                         }
220                 }
221                 return result;
222         }
223         
224         public static List<Statement> directStatements(ReadGraph graph, Resource resource, boolean ignoreVirtual) throws DatabaseException {
225                 
226                 DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class);
227                 DirectStatementProcedure proc = new DirectStatementProcedure();
228
229                 if (ignoreVirtual) {
230                         return dqs.getDirectPersistentStatements(graph, resource);
231                 } else {
232                         return dqs.getDirectStatements(graph, resource);
233                 }
234
235         }
236         
237     public static List<Resource> garbageResources(ReadGraph graph) throws DatabaseException {
238         
239         SerialisationSupport ss = graph.getService(SerialisationSupport.class);
240         
241         TIntArrayList refs = new TIntArrayList();
242         TIntArrayList res = new TIntArrayList();
243         
244         // Find all statements in the database
245         for(ClusterUID uid : listClusters(graph)) {
246                 for(Resource r : resourcesByCluster(graph, uid)) {
247                         int sid = ss.getTransientId(r);
248                         for(Statement stm : directStatements(graph, r, true)) {
249                                 int oid = ss.getTransientId(stm.getObject());
250                                 refs.add(sid);
251                                 refs.add(oid);
252                         }
253                         res.add(sid);
254                 }
255         }
256
257         TIntHashSet reached = new TIntHashSet();
258         
259         // Initialize root
260         int root = ss.getTransientId(graph.getRootLibrary());
261         reached.add(root);
262         
263         int[] refArray = refs.toArray();
264         
265         boolean changes = true;
266         
267         while(changes) {
268                 changes = false;
269                 for(int i=0;i<refArray.length;i+=2) {
270                         int s = refArray[i];
271                         int o = refArray[i+1];
272                         if(reached.contains(s)) {
273                                 if(reached.add(o)) {
274                                         changes = true;
275                                 }
276                         }
277                 }
278                 
279                 System.err.println("Reachability iteration, changes = " + changes);
280         }
281         
282         ArrayList<Resource> result = new ArrayList<>();
283                 for(int i=0;i<refArray.length;i+=2) {
284                         int s = refArray[i];
285                         if(reached.contains(s)) {
286                                 if(reached.add(refArray[i+1]))
287                                         changes = true;
288                         }
289                 }
290
291                 res.forEach(new TIntProcedure() {
292                         
293                         @Override
294                         public boolean execute(int r) {
295                                 if(!reached.contains(r)) {
296                                         try {
297                                                 result.add(ss.getResource(r));
298                                         } catch (DatabaseException e) {
299                                                 LOGGER.error("Unexpected error while resolving garbage resources.", e);
300                                         }
301                                 }
302                                 return true;
303                         }
304                         
305                 });
306                 
307         return result;
308         
309     }
310
311     public static ClusterUID clusterUIDOfResource(ReadGraph graph, Resource resource) throws DatabaseException {
312         SerialisationSupport ss = graph.getService(SerialisationSupport.class);
313         return ss.getUID(resource).asCID();
314     }
315     
316     public static boolean isClusterLoaded(ReadGraph graph, ClusterUID clusterUID) throws DatabaseException {
317         XSupport xs = graph.getService(XSupport.class);
318         return xs.isClusterLoaded(clusterUID);
319     }
320     
321     public static void setImmutable(ReadGraph graph, Resource resource, boolean value) throws DatabaseException {
322         XSupport xs = graph.getService(XSupport.class);
323         xs.setImmutable(resource, value);
324     }
325
326     public static Type getSCLType(ReadGraph graph, RuntimeEnvironment runtimeEnvironment, String typeText) throws DatabaseException {
327         try {
328             return Environments.getType(runtimeEnvironment.getEnvironment(), typeText);
329         } catch (SCLExpressionCompilationException e) {
330             throw new DatabaseException(e);
331         }
332     }
333
334     public static Type getSCLType(ReadGraph graph, Resource resource, String typeText) throws DatabaseException {
335         try {
336             RuntimeEnvironment runtimeEnvironment = graph.syncRequest(new RuntimeEnvironmentRequest(resource));
337             return Environments.getType(runtimeEnvironment.getEnvironment(), typeText);
338         } catch (SCLExpressionCompilationException e) {
339             throw new DatabaseException(e);
340         }
341     }
342
343     public static Resource getPossibleChild(ReadGraph graph, Resource resource, String name) throws DatabaseException {
344         return graph.sync(new PossibleChild(resource, name));
345     }
346
347     public static Resource getPossibleChild(ReadGraph graph, Resource resource, Resource type, String name) throws DatabaseException {
348         Resource child = graph.sync(new PossibleChild(resource, name));
349         if(child == null) return null;
350         if(!graph.isInstanceOf(child, type)) return null;
351         return child;
352     }
353     
354 }