package org.simantics.graph.store; import java.util.ArrayList; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.procedure.TIntObjectProcedure; import gnu.trove.procedure.TIntProcedure; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.TIntHashSet; /** * Statement store indexes a set of statements. * @author Hannu Niemist� */ public class StatementStore implements IStore { static private final TIntArrayList EMPTY_INT_LIST = new TIntArrayList(0); TIntObjectHashMap> statements = new TIntObjectHashMap>(); /** * Adds a statement to the store. * @param subject * @param predicate * @param object */ public void add(int subject, int predicate, int object) { assert(subject >= 0); assert(predicate >= 0); assert(object >= 0); TIntObjectHashMap localStatements = statements.get(subject); if(localStatements == null) { localStatements = new TIntObjectHashMap(3); statements.put(subject, localStatements); } TIntArrayList objects = localStatements.get(predicate); if(objects == null) { objects = new TIntArrayList(2); localStatements.put(predicate, objects); } objects.add(object); } public void map(final TIntIntHashMap map) { final TIntObjectHashMap> newStatements = new TIntObjectHashMap>(statements.size()*2+1); statements.forEachEntry(new TIntObjectProcedure>() { TIntObjectHashMap newLocalStatements; TIntArrayList newObjects; TIntProcedure objectProcedure = new TIntProcedure() { @Override public boolean execute(int o) { if(map.contains(o)) o = map.get(o); newObjects.add(o); return true; } }; TIntObjectProcedure predicateProcedure = new TIntObjectProcedure() { @Override public boolean execute(int p, TIntArrayList objects) { if(map.contains(p)) p = map.get(p); TIntArrayList exObjects = newLocalStatements.get(p); if(exObjects == null) { IndexMappingUtils.map(map, objects); newLocalStatements.put(p, objects); } else { newObjects = exObjects; objects.forEach(objectProcedure); } return true; } }; @Override public boolean execute(int s, TIntObjectHashMap localStatements) { if(map.contains(s)) s = map.get(s); TIntObjectHashMap exLocalStatements = newStatements.get(s); if(exLocalStatements == null) { exLocalStatements = new TIntObjectHashMap(localStatements.size()*2 + 1); newStatements.put(s, exLocalStatements); } newLocalStatements = exLocalStatements; localStatements.forEachEntry(predicateProcedure); return true; } }); statements = newStatements; } public void forStatements(final IStatementProcedure proc) { statements.forEachEntry(new TIntObjectProcedure>() { @Override public boolean execute(final int s, TIntObjectHashMap localStatements) { localStatements.forEachEntry(new TIntObjectProcedure() { @Override public boolean execute(final int p, TIntArrayList objects) { objects.forEach(new TIntProcedure() { @Override public boolean execute(int o) { proc.execute(s, p, o); return true; } }); return true; } }); return true; } }); } private static class ForStatementsWithSubjectProc implements TIntObjectProcedure, TIntProcedure { int s; int p; IStatementProcedure proc; public ForStatementsWithSubjectProc(int s, IStatementProcedure proc) { this.s = s; this.proc = proc; } @Override public boolean execute(int o) { proc.execute(s, p, o); return true; } @Override public boolean execute(int p, TIntArrayList b) { this.p = p; b.forEach(this); return true; } } public void forStatementsWithSubject(int s, IStatementProcedure proc) { TIntObjectHashMap localStatements = statements.get(s); if(localStatements == null) return; localStatements.forEachEntry(new ForStatementsWithSubjectProc(s, proc)); } public void forStatements(final int predicate, final IStatementProcedure proc) { statements.forEachEntry(new TIntObjectProcedure>() { @Override public boolean execute(final int s, TIntObjectHashMap localStatements) { TIntArrayList objects = localStatements.get(predicate); if(objects != null) objects.forEach(new TIntProcedure() { @Override public boolean execute(int o) { proc.execute(s, predicate, o); return true; } }); return true; } }); } public TIntArrayList getRelation(int predicate) { final TIntArrayList result = new TIntArrayList(); forStatements(predicate, new IStatementProcedure() { @Override public void execute(int s, int p, int o) { result.add(s); result.add(o); } }); return result; } public TIntHashSet getRelationDomain(final int predicate) { final TIntHashSet result = new TIntHashSet(); statements.forEachEntry(new TIntObjectProcedure>() { @Override public boolean execute(int s, TIntObjectHashMap localStatements) { if(localStatements.containsKey(predicate)) result.add(s); return true; } }); return result; } public TIntArrayList extractRelation(final int predicate) { final TIntArrayList result = new TIntArrayList(); final TIntArrayList removals = new TIntArrayList(); statements.forEachEntry(new TIntObjectProcedure>() { @Override public boolean execute(int s, TIntObjectHashMap localStatements) { TIntArrayList objects = localStatements.remove(predicate); if(objects != null) { for(int o : objects.toArray()) { result.add(s); result.add(o); } } if(localStatements.isEmpty()) removals.add(s); return true; } }); for(int s : removals.toArray()) statements.remove(s); return result; } public TIntArrayList getObjects(int subject, int predicate) { TIntObjectHashMap localStatements = statements.get(subject); if(localStatements == null) return EMPTY_INT_LIST; TIntArrayList objects = localStatements.get(predicate); if(objects == null) return EMPTY_INT_LIST; return objects; } public int[] toArray(final TIntIntHashMap inverseMap) { final TIntArrayList statements = new TIntArrayList(); forStatements(new IStatementProcedure() { @Override public void execute(int s, int p, int o) { statements.add(s); statements.add(p); int inverse = -1; if(inverseMap.contains(p)) { inverse = inverseMap.get(p); if(p==inverse && s == o) inverse = -1; } statements.add(inverse); statements.add(o); } }); return statements.toArray(); } public void collectReferences(final boolean[] set) { forStatements(new IStatementProcedure() { @Override public void execute(int s, int p, int o) { set[s] = true; set[p] = true; set[o] = true; } }); } public TIntHashSet getPredicates() { final TIntHashSet result = new TIntHashSet(); statements.forEachValue(new TObjectProcedure>() { TIntProcedure proc = new TIntProcedure() { @Override public boolean execute(int value) { result.add(value); return true; } }; @Override public boolean execute(TIntObjectHashMap localStatements) { localStatements.forEachKey(proc); return true; } }); return result; } public int[] toArray() { final TIntArrayList statements = new TIntArrayList(); forStatements(new IStatementProcedure() { @Override public void execute(int s, int p, int o) { statements.add(s); statements.add(p); statements.add(-1); statements.add(o); } }); return statements.toArray(); } private static class CollisionSubjectProcedure implements TIntObjectProcedure> { CollisionPredicateProcedure predicateProcedure; public CollisionSubjectProcedure(CollisionPredicateProcedure predicateProcedure) { this.predicateProcedure = predicateProcedure; } @Override public boolean execute(int subject, TIntObjectHashMap predicateObjectMap) { predicateProcedure.subject = subject; predicateObjectMap.forEachEntry(predicateProcedure); return true; } } private static class CollisionPredicateProcedure implements TIntObjectProcedure { ArrayList collisions; int subject; public CollisionPredicateProcedure(ArrayList collisions) { this.collisions = collisions; } @Override public boolean execute(int predicate, TIntArrayList objects) { if(objects.size() > 1) { objects.sort(); int oldObject = objects.get(0); for(int i=1;i getCollisions() { ArrayList collisions = new ArrayList(); CollisionPredicateProcedure predicateProcedure = new CollisionPredicateProcedure(collisions); CollisionSubjectProcedure subjectProcedure = new CollisionSubjectProcedure(predicateProcedure); statements.forEachEntry(subjectProcedure); return collisions; } }