package org.simantics.graph.store; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.THashMap; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import gnu.trove.map.hash.TObjectIntHashMap; import gnu.trove.procedure.TIntProcedure; import gnu.trove.procedure.TObjectIntProcedure; import gnu.trove.procedure.TObjectObjectProcedure; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; import gnu.trove.set.hash.TIntHashSet; import java.util.ArrayList; import java.util.regex.Pattern; import org.simantics.graph.query.Path; import org.simantics.graph.query.PathChild; import org.simantics.graph.query.PathRoot; import org.simantics.graph.representation.External; import org.simantics.graph.representation.Identity; import org.simantics.graph.representation.Internal; import org.simantics.graph.representation.Root; public class IdentityStore implements IStore { private static int[] EMPTY_INT_ARRAY = new int[0]; public static class ConsistsOf { public int parent; public String name; public int child; public ConsistsOf(int parent, String name, int child) { //System.out.println(parent + "." + name + " = " + child); this.parent = parent; this.name = name; this.child = child; } } TObjectIntHashMap roots = new TObjectIntHashMap(); TIntObjectHashMap invRoots = new TIntObjectHashMap(); TIntObjectHashMap> parentMap = new TIntObjectHashMap>(); TIntObjectHashMap childMap = new TIntObjectHashMap(); TIntHashSet newResources = new TIntHashSet(); int resourceCount = 0; TIntIntHashMap unifications = new TIntIntHashMap(); TIntHashSet collisions = new TIntHashSet(); public int newResource() { return resourceCount++; } public boolean markNew(int resource) { return newResources.add(resource); } public int[] getNewResources() { return newResources.toArray(); } public int getResourceCount() { return resourceCount; } public void setResourceCount(int newCount) { resourceCount = newCount; } public void defineRoot(String name, int root) { if(roots.contains(name)) unify(root, roots.get(name)); roots.put(name, root); invRoots.put(root, name); } public void defineChild(int parent, String name, int child) { THashMap map = parentMap.get(parent); if(map == null) { map = new THashMap(); parentMap.put(parent, map); } ConsistsOf consistsOf = new ConsistsOf(parent, name, child); ConsistsOf oldC = map.put(name, consistsOf); if(oldC != null) unify(child, oldC.child); childMap.put(child, consistsOf); } public boolean hasChild(int parent, String name) { THashMap map = parentMap.get(parent); if(map == null) return false; else return map.contains(name); } public int getChild(int parent, String name) { THashMap map = parentMap.get(parent); if(map == null) { map = new THashMap(); parentMap.put(parent, map); } else if(map.contains(name)) return map.get(name).child; int child = newResource(); ConsistsOf consistsOf = new ConsistsOf(parent, name, child); map.put(name, consistsOf); childMap.put(child, consistsOf); return child; } public int getChildIfExists(int parent, String name) { THashMap map = parentMap.get(parent); if(map != null && map.contains(name)) return map.get(name).child; return -1; } public int getRoot(String uri) { if(roots.contains(uri)) return roots.get(uri); else { int root = newResource(); roots.put(uri, root); invRoots.put(root, uri); return root; } } public int getRootIfExists(String uri) { if(roots.contains(uri)) return roots.get(uri); return -1; } public int[] getChildren(int id) { THashMap map = parentMap.get(id); if(map == null) return EMPTY_INT_ARRAY; final int[] result = new int[map.size()]; map.forEachValue(new TObjectProcedure() { int i=0; @Override public boolean execute(ConsistsOf object) { result[i++] = object.child; return true; } }); return result; } public THashMap getChildMap(int parent) { return parentMap.get(parent); } private void collectIdentities(final int parent, final ArrayList identities) { THashMap map = parentMap.get(parent); if(map != null) { map.forEachEntry(new TObjectObjectProcedure() { @Override public boolean execute(String a, ConsistsOf b) { if(newResources.contains(b.child)) identities.add(new Identity(b.child, new Internal(parent, a))); else identities.add(new Identity(b.child, new External(parent, a))); collectIdentities(b.child, identities); return true; } }); } } private void collectIdentities(final ArrayList identities) { roots.forEachEntry(new TObjectIntProcedure() { @Override public boolean execute(String a, int b) { identities.add(new Identity(b, new Root(a, ""))); collectIdentities(b, identities); return true; } }); } @Override public void map(final TIntIntHashMap map) { collisions = IndexMappingUtils.map(map, collisions); newResources = IndexMappingUtils.map(map, newResources); IndexMappingUtils.map(map, roots); invRoots = IndexMappingUtils.map(map, invRoots, collisions); final ArrayList consistsOfs = new ArrayList(childMap.size()); childMap.forEachValue(new TObjectProcedure() { @Override public boolean execute(ConsistsOf c) { if(map.contains(c.parent)) c.parent = map.get(c.parent); if(map.contains(c.child)) c.child = map.get(c.child); consistsOfs.add(c); return true; } }); childMap.clear(); parentMap.clear(); assert(unifications.isEmpty()); for(ConsistsOf c : consistsOfs) { THashMap m = parentMap.get(c.parent); if(m == null) { m = new THashMap(); parentMap.put(c.parent, m); } ConsistsOf oldC = m.put(c.name, c); ConsistsOf childC = childMap.put(c.child, c); if(childC != null && (childC.parent != c.parent || !childC.name.equals(c.name))) collisions.add(c.child); if(oldC != null) unify(oldC.child, c.child); } } public int pathToId(Path path) { if(path instanceof PathChild) { PathChild child = (PathChild)path; int parent = pathToId(child.parent); if(parent < 0) return -1; return getChildIfExists(parent, child.name); } else if(path instanceof PathRoot) return getRootIfExists(((PathRoot)path).name); else throw new IllegalArgumentException(); } public int createPathToId(Path path) { if(path instanceof PathChild) { PathChild child = (PathChild)path; return getChild(createPathToId(child.parent), child.name); } else if(path instanceof PathRoot) return getRoot(((PathRoot)path).name); else throw new IllegalArgumentException(); } public Path idToPath(int id) { ConsistsOf consistsOf = childMap.get(id); if(consistsOf == null) { String root = invRoots.get(id); if(root == null) return null; else return new PathRoot(root); } /*System.out.println("ConsistsOf(" + consistsOf.parent + "." + consistsOf.name + " = " + consistsOf.child + ")"); */ //if(consistsOf.parent == consistsOf.child) // return null; Path parentPath = idToPath(consistsOf.parent); if(parentPath == null) return null; return new PathChild(consistsOf.name, parentPath); } public boolean contains(Path path) { return pathToId(path) >= 0; } public boolean isNewResource(int id) { return newResources.contains(id); } public Identity[] toArray() { final ArrayList identities = new ArrayList(); collectIdentities(identities); return identities.toArray(new Identity[identities.size()]); } public void collectReferences(final boolean[] set) { TIntProcedure proc = new TIntProcedure() { @Override public boolean execute(int value) { set[value] = true; return true; } }; roots.forEachValue(proc); childMap.forEach(proc); parentMap.forEach(proc); } public int removeIdentity(int id) { ConsistsOf c = childMap.remove(id); if(c != null) { parentMap.get(c.parent).remove(c.name); return c.parent; } return -1; } public boolean hasIdentity(int id) { return childMap.containsKey(id); } public void definePath(Path path, int resource) { if(path instanceof PathChild) { PathChild child = (PathChild)path; int parent = createPathToId(child.parent); defineChild(parent, child.name, resource); } else if(path instanceof PathRoot) { defineRoot(((PathRoot)path).name, resource); } else throw new IllegalArgumentException(); } public void printChildMap() { System.out.println("ChildMap:"); childMap.forEachValue(new TObjectProcedure() { @Override public boolean execute(ConsistsOf c) { System.out.println(" " + c.child + " = " + c.parent + "." + c.name); return true; } }); } private int canonical(int a) { if(unifications.contains(a)) { int b = unifications.get(a); if(unifications.contains(b)) { int c = canonical(b); unifications.put(a, c); unifications.put(b, c); return c; } else return b; } else return a; } public TIntIntHashMap extractUnifications() { // This normalizes the map for(int a : unifications.keys()) canonical(a); TIntIntHashMap result = unifications; unifications = new TIntIntHashMap(); return result; } public void unify(int a, int b) { if(a != b) { a = canonical(a); b = canonical(b); if(a != b) unifications.put(a, b); } } public TIntHashSet getCollisions() { return collisions; } public String[] getRoots() { return roots.keys(new String[roots.size()]); } void findChildren(int id, final Path path, final String suffix, final Pattern pattern, final THashSet result) { if(pattern.matcher(suffix).matches()) result.add(path); THashMap map = parentMap.get(id); if(map != null) map.forEachValue(new TObjectProcedure() { @Override public boolean execute(ConsistsOf consistsOf) { findChildren(consistsOf.child, new PathChild(consistsOf.name, path), suffix + "/" + consistsOf.name, pattern, result); return true; } }); } public int[] getUnfoundedIdentities() { TIntArrayList result = new TIntArrayList(); for(int resource : parentMap.keys()) { if(!childMap.containsKey(resource) && !invRoots.containsKey(resource)) { result.add(resource); } } return result.toArray(); } public void forEachChild(TObjectProcedure proc) { childMap.forEachValue(proc); } public void setIdentity(int child, int parent, String name) { THashMap map = parentMap.get(parent); if(map == null) { map = new THashMap(); parentMap.put(parent, map); } else if(map.contains(name)) throw new IllegalStateException(); ConsistsOf consistsOf = new ConsistsOf(parent, name, child); map.put(name, consistsOf); childMap.put(child, consistsOf); } }