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