package org.simantics.db.common.utils.traverser; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import org.simantics.db.AsyncRequestProcessor; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.procedure.AsyncListener; import org.simantics.db.procedure.AsyncProcedure; import org.simantics.db.procedure.Listener; import org.simantics.db.procedure.Procedure; import org.simantics.db.procedure.SyncListener; import org.simantics.db.procedure.SyncProcedure; import org.simantics.db.request.Read; import org.simantics.db.request.ReadInterface; /** * Traverse query is generic query that traverses accroding to given rules. * Use {@link TraverseQueryBuilder} to construct traverse query * * @author toni.kalajainen@semantum.fi */ public class TraverseQuery implements Read, ReadInterface { // Start locations public final Resource[] startResources; // Instances of types to follow to public final Resource[] instancesOfTypesToFollowTo; // Instances of types to follow to public final Resource[] inheritedFromTypesToFollowTo; // Relations to traverse public final Resource[] relations; // Types to add to result public final Resource[] instancesOfTypesToAddToResult; // Types to add to result public final Resource[] inheritedFromTypesToAddToResult; private int hash; public TraverseQuery( Resource[] startResources, Resource[] instancesOfTypesToFollowTo, Resource[] relations, Resource[] instancesOfTypesToAddToResult, Resource[] inheritedFromTypesToFollowTo, Resource[] inheritedFromTypesToAddToResult ) { this.startResources = startResources; this.relations = relations; this.instancesOfTypesToFollowTo = instancesOfTypesToFollowTo; this.instancesOfTypesToAddToResult = instancesOfTypesToAddToResult; this.inheritedFromTypesToFollowTo = inheritedFromTypesToFollowTo; this.inheritedFromTypesToAddToResult = inheritedFromTypesToAddToResult; int hash = 345436534; for (Resource r : startResources) hash = 13*hash + r.hashCode(); for (Resource r : relations) hash = 7*hash + r.hashCode(); for (Resource r : instancesOfTypesToFollowTo) hash = 3*hash + r.hashCode(); for (Resource r : instancesOfTypesToAddToResult) hash = 11*hash + r.hashCode(); for (Resource r : inheritedFromTypesToFollowTo) hash = 3*hash + r.hashCode(); for (Resource r : inheritedFromTypesToAddToResult) hash = 11*hash + r.hashCode(); } @Override public int hashCode() { return hash; } @Override public boolean equals(Object object) { if (this == object) return true; if ( object instanceof TraverseQuery == false ) return false; TraverseQuery other = (TraverseQuery) this; if (hash != other.hash) return false; // The order should not count - but who cares if (!Arrays.deepEquals(startResources, other.startResources)) return false; if (!Arrays.deepEquals(relations, other.relations)) return false; if (!Arrays.deepEquals(instancesOfTypesToFollowTo, other.instancesOfTypesToFollowTo)) return false; if (!Arrays.deepEquals(instancesOfTypesToAddToResult, other.instancesOfTypesToAddToResult)) return false; if (!Arrays.deepEquals(inheritedFromTypesToFollowTo, other.inheritedFromTypesToFollowTo)) return false; if (!Arrays.deepEquals(inheritedFromTypesToAddToResult, other.inheritedFromTypesToAddToResult)) return false; return true; } @Override public void request(AsyncRequestProcessor processor, AsyncProcedure procedure) { processor.asyncRequest(this, procedure); } @Override public void request(AsyncRequestProcessor processor, Procedure procedure) { processor.asyncRequest(this, procedure); } @Override public void request(AsyncRequestProcessor processor, SyncProcedure procedure) { processor.asyncRequest(this, procedure); } @Override public void request(AsyncRequestProcessor processor, AsyncListener procedure) { processor.asyncRequest(this, procedure); } @Override public void request(AsyncRequestProcessor processor, Listener procedure) { processor.asyncRequest(this, procedure); } @Override public void request(AsyncRequestProcessor processor, SyncListener procedure) { processor.asyncRequest(this, procedure); } @Override public TraverseResult request(RequestProcessor processor) throws DatabaseException { return processor.syncRequest(this); } @Override public TraverseResult perform(ReadGraph graph) throws DatabaseException { Traverser traverser = new Traverser(); for (Resource r : startResources) traverser.toBeVisited.add(r); while ( !traverser.toBeVisited.isEmpty() ) { _doTraverse(graph, traverser); } return traverser.result; } void _doTraverse(ReadGraph graph, Traverser traverser) throws ServiceException { // Remove one resource while (!traverser.toBeVisited.isEmpty()) { Resource r = traverser.toBeVisited.removeFirst(); if ( traverser.visited.contains(r) ) continue; // Get Objects for (Resource relation : relations) { nextObj: for (Resource obj : graph.getObjects(r, relation)) { if (traverser.visited.contains(obj)) continue nextObj; boolean isAcceptedObject = false; for (Resource acceptedType : instancesOfTypesToFollowTo) { isAcceptedObject |= graph.isInstanceOf(obj, acceptedType); if ( isAcceptedObject ) break; } if ( !isAcceptedObject ) { for (Resource acceptedType : inheritedFromTypesToFollowTo) { isAcceptedObject |= graph.isInheritedFrom(obj, acceptedType); if ( isAcceptedObject ) break; } } if ( !isAcceptedObject ) continue nextObj; traverser.toBeVisited.add(obj); } } // Add to result? boolean addToResult = false; for (Resource typeToAddToResult : instancesOfTypesToAddToResult) { addToResult |= graph.isInstanceOf(r, typeToAddToResult); if ( addToResult) break; } if (!addToResult) for (Resource typeToAddToResult : inheritedFromTypesToAddToResult) { addToResult |= graph.isInheritedFrom(r, typeToAddToResult); if ( addToResult) break; } if ( addToResult ) { traverser.result.result.add(r); } traverser.visited.add(r); } } class Traverser { TraverseResult result = new TraverseResult(); public Set visited = new HashSet(); public LinkedList toBeVisited = new LinkedList(); } }