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.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.PossibleChild; import org.simantics.db.common.request.PossibleObjectWithType; import org.simantics.db.common.request.PossibleOwner; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.request.RuntimeEnvironmentRequest; import org.simantics.db.exception.AdaptionException; 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.scl.compiler.environment.Environments; import org.simantics.scl.compiler.runtime.RuntimeEnvironment; import org.simantics.scl.compiler.top.SCLExpressionCompilationException; import org.simantics.scl.compiler.types.Type; 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.THashSet; 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)); } /** * Finds a resource that is reachable from both input resources by as short as possible IsOwnedBy chain */ public static Resource commonAncestor(ReadGraph graph, Resource r1, Resource r2) throws DatabaseException { if(r1.equals(r2)) return r1; Layer0 L0 = Layer0.getInstance(graph); HashSet visited = new HashSet(); visited.add(r1); visited.add(r2); while(true) { if(r1 != null) { r1 = graph.getPossibleObject(r1, L0.IsOwnedBy); if(r1 != null && !visited.add(r1)) return r1; } else if(r2 == null) return null; if(r2 != null) { r2 = graph.getPossibleObject(r2, L0.IsOwnedBy); if(r2 != null && !visited.add(r2)) return r2; } } } private static Collection getDirectOwners(ReadGraph graph, Resource resource) throws DatabaseException { // TODO is necessary? if(resource.equals(graph.getRootLibrary())) return Collections.emptyList(); Layer0 L0 = Layer0.getInstance(graph); Collection owners = graph.getObjects(resource, L0.IsOwnedBy); if(owners.isEmpty()) { owners = new THashSet(); // If there are no owners, collect resources referring to this resource by IsRelatedTo for(Statement statement : graph.getStatements(resource, L0.IsWeaklyRelatedTo)) { Resource inverse = graph.getPossibleInverse(statement.getPredicate()); if(inverse != null) { if(graph.isSubrelationOf(inverse, L0.IsRelatedTo)) { // Filter away tags if(resource.equals(statement.getObject())) continue; owners.add(statement.getObject()); } } } } else if(owners.size() > 1) { // TODO: getObjects returns duplicate entries (https://www.simantics.org/redmine/issues/4885) and therefore direct is Set. // Fix getObjects to not return duplicate entries owners = new HashSet(owners); } return owners; } /** * This method didn't have a clear specification and therefore should not be used. Use instead * the methods in the class {@link NearestOwnerFinder}. */ @Deprecated public static Resource getNearestOwner(ReadGraph graph, Collection resources) throws DatabaseException { if(resources.size() == 1) return NearestOwnerFinder.getNearestOwner(graph, resources.iterator().next()); else return NearestOwnerFinder.getNearestOwnerFromDirectOwners(graph, resources); } public static Resource getClusterSetForNewResource(ReadGraph graph, Resource ... resources) throws DatabaseException { if(resources.length == 1) return getClusterSetForNewResource(graph, resources[0]); Resource owner = NearestOwnerFinder.getNearestOwnerFromDirectOwners(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 = NearestOwnerFinder.getNearestOwnerFromDirectOwners(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) { return dqs.getDirectPersistentStatements(graph, resource); } else { return dqs.getDirectStatements(graph, resource); } } 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 { public StringAdapterRequest(Resource resource) { super(resource); } @Override public String perform(ReadGraph graph) throws DatabaseException { try { return graph.adapt(resource, String.class); } catch (AdaptionException e) { return NameUtils.getSafeName(graph, resource); } } } }