package org.simantics.db.common.utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.WriteGraph; import org.simantics.db.common.procedure.adapter.DirectStatementProcedure; import org.simantics.db.common.request.IsParent; import org.simantics.db.common.request.ObjectsWithType; import org.simantics.db.common.request.PossibleObjectWithType; import org.simantics.db.common.request.PossibleOwner; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.InvalidResourceReferenceException; import org.simantics.db.service.ClusterUID; import org.simantics.db.service.ClusteringSupport; import org.simantics.db.service.DirectQuerySupport; import org.simantics.db.service.SerialisationSupport; import org.simantics.db.service.XSupport; import org.simantics.layer0.Layer0; import org.simantics.utils.datastructures.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import gnu.trove.list.array.TIntArrayList; import gnu.trove.procedure.TIntProcedure; import gnu.trove.set.hash.TIntHashSet; public class CommonDBUtils { private static final Logger LOGGER = LoggerFactory.getLogger(CommonDBUtils.class); public static boolean isParent(ReadGraph graph, Resource possibleParent, Resource possibleChild) throws DatabaseException { return graph.sync(new IsParent(possibleParent, possibleChild)); } public static Resource parent(ReadGraph graph, Resource child) throws DatabaseException { return graph.getSingleObject(child, Layer0.getInstance(graph).PartOf); } public static String possibleRelatedString(ReadGraph graph, Resource subject, Resource relation) throws DatabaseException { return graph.getPossibleRelatedValue(subject, relation, Bindings.STRING); } public static Integer possibleRelatedInteger(ReadGraph graph, Resource subject, Resource relation) throws DatabaseException { return graph.getPossibleRelatedValue(subject, relation, Bindings.INTEGER); } public static Resource getPossibleOwner(ReadGraph graph, Resource resource) throws DatabaseException { return graph.syncRequest(new PossibleOwner(resource)); } public static Resource commonAncestor(ReadGraph graph, Resource r1, Resource r2) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); if(r1.equals(r2)) return r1; HashSet visited = new HashSet(); visited.add(r1); visited.add(r2); while(true) { if(r1 != null) { r1 = graph.getPossibleObject(r1, L0.IsOwnedBy); if(r1 != null) if(!visited.add(r1)) return r1; } else if(r2 == null) return null; if(r2 != null) { r2 = graph.getPossibleObject(r2, L0.IsOwnedBy); if(r2 != null) if(!visited.add(r2)) return r2; } } } public static Resource getNearestOwner(ReadGraph graph, Collection resources) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Set direct = new HashSet(); Set owners = new HashSet(); for(Resource r : resources) { Collection objects = graph.getObjects(r, L0.IsOwnedBy); // FIXME: // TODO: getObjects returns duplicate entries (https://www.simantics.org/redmine/issues/4885) and therefore direct is Set. Fix getObjects to not return duplicate entries if (objects.size() > 1) objects = new HashSet(objects); if (objects.size() == 1) direct.addAll(objects); else if (objects.isEmpty()) { for(Statement stm : graph.getStatements(r, L0.IsWeaklyRelatedTo)) { Resource inverse = graph.getPossibleInverse(stm.getPredicate()); if(inverse != null) { if(graph.isSubrelationOf(inverse, L0.IsRelatedTo)) { // Filter away tags if(!r.equals(stm.getObject())) owners.add(stm.getObject()); } } } } else { System.err.println("Multiple owners for " + graph.getPossibleURI(r) + " id : " + r); for (Resource r2 : objects) System.err.println("owner : " + graph.getPossibleURI(r2) + " id " + r2); return null; } } if(!direct.isEmpty()) { Iterator iter = direct.iterator(); Resource common = iter.next(); while (iter.hasNext()) { Resource other = iter.next(); common = commonAncestor(graph, common, other); if (common == null) break; } if(common != null) owners.add(common); } if(!Collections.disjoint(owners, resources)) { System.err.println("Overlapping owners:"); for(Resource r : resources) System.err.println("-resource " + NameUtils.getSafeName(graph, r, true)); for(Resource r : owners) System.err.println("-owner " + NameUtils.getSafeName(graph, r, true)); return null; } if(owners.size() == 1) return owners.iterator().next(); if(owners.size() == 0) return null; return getNearestOwner(graph, owners); } public static Resource getClusterSetForNewResource(ReadGraph graph, Resource ... resources) throws DatabaseException { if(resources.length == 1) return getClusterSetForNewResource(graph, resources[0]); Resource owner = getNearestOwner(graph, CollectionUtils.toList(resources)); if(owner == null) return null; return getClusterSetForNewResource(graph, owner, new HashSet()); } public static Resource getClusterSetForNewResource(ReadGraph graph, Collection resources) throws DatabaseException { if(resources.size() == 1) return getClusterSetForNewResource(graph, resources.iterator().next()); Resource owner = getNearestOwner(graph, resources); return getClusterSetForNewResource(graph, owner, new HashSet()); } public static Resource getClusterSetForNewResource(ReadGraph graph, Resource resource, Set visited) throws DatabaseException { ClusteringSupport cs = graph.getService(ClusteringSupport.class); if(cs.isClusterSet(resource)) return resource; Resource owner = getPossibleOwner(graph, resource); if(owner == null || owner == resource) return null; if(!visited.add(owner)) return null; return getClusterSetForNewResource(graph, owner, visited); } public static Resource getClusterSetForNewResource(ReadGraph graph, Resource r) throws DatabaseException { return getClusterSetForNewResource(graph, r, new HashSet()); } public static void selectClusterSet(WriteGraph graph, Collection resources) throws DatabaseException { Resource clusterSet = getClusterSetForNewResource(graph, resources); if(clusterSet == null) clusterSet = graph.getRootLibrary(); graph.setClusterSet4NewResource(clusterSet); } public static void selectClusterSet(WriteGraph graph, Resource ... resources) throws DatabaseException { Resource clusterSet = getClusterSetForNewResource(graph, resources); if(clusterSet == null) clusterSet = graph.getRootLibrary(); graph.setClusterSet4NewResource(clusterSet); } public static void selectClusterSet(WriteGraph graph, Resource resource) throws DatabaseException { Resource clusterSet = getClusterSetForNewResource(graph, resource); if(clusterSet == null) clusterSet = graph.getRootLibrary(); graph.setClusterSet4NewResource(clusterSet); } public static List objectsWithType(ReadGraph graph, Resource subject, Resource relation, Resource type) throws DatabaseException { return new ArrayList(graph.syncRequest(new ObjectsWithType(subject, relation, type))); } public static Resource possibleObjectWithType(ReadGraph graph, Resource subject, Resource relation, Resource type) throws DatabaseException { return graph.syncRequest(new PossibleObjectWithType(subject, relation, type)); } public static List listClusters(ReadGraph graph) throws DatabaseException { XSupport xs = graph.getService(XSupport.class); ClusterUID uids[] = xs.listClusters(); ArrayList result = new ArrayList<>(uids.length); for(ClusterUID uid : uids) result.add(uid); return result; } public static List resourcesByCluster(ReadGraph graph, ClusterUID uid) throws DatabaseException { SerialisationSupport ss = graph.getService(SerialisationSupport.class); ArrayList result = new ArrayList(); // Index 0 is illegal for(int i=1;i<1<<12;i++) { try { result.add(ss.getResource(uid.toRID(i))); } catch (InvalidResourceReferenceException e) { } } return result; } public static List directStatements(ReadGraph graph, Resource resource, boolean ignoreVirtual) throws DatabaseException { DirectQuerySupport dqs = graph.getService(DirectQuerySupport.class); DirectStatementProcedure proc = new DirectStatementProcedure(); if (ignoreVirtual) { dqs.forEachDirectPersistentStatement(graph, resource, proc); } else { dqs.forEachDirectStatement(graph, resource, proc); } return proc.getOrThrow(); } public static List garbageResources(ReadGraph graph) throws DatabaseException { SerialisationSupport ss = graph.getService(SerialisationSupport.class); TIntArrayList refs = new TIntArrayList(); TIntArrayList res = new TIntArrayList(); // Find all statements in the database for(ClusterUID uid : listClusters(graph)) { for(Resource r : resourcesByCluster(graph, uid)) { int sid = ss.getTransientId(r); for(Statement stm : directStatements(graph, r, true)) { int oid = ss.getTransientId(stm.getObject()); refs.add(sid); refs.add(oid); } res.add(sid); } } TIntHashSet reached = new TIntHashSet(); // Initialize root int root = ss.getTransientId(graph.getRootLibrary()); reached.add(root); int[] refArray = refs.toArray(); boolean changes = true; while(changes) { changes = false; for(int i=0;i