-package org.simantics.graph.db;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.common.uri.UnescapedChildMapOfResource;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.RuntimeDatabaseException;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.graph.representation.External;\r
-import org.simantics.graph.representation.Identity;\r
-import org.simantics.graph.representation.IdentityDefinition;\r
-import org.simantics.graph.representation.Internal;\r
-import org.simantics.graph.representation.Optional;\r
-import org.simantics.graph.representation.Root;\r
-import org.simantics.graph.representation.TransferableGraph1;\r
-import org.simantics.utils.datastructures.Pair;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-import gnu.trove.map.hash.TIntIntHashMap;\r
-import gnu.trove.procedure.TObjectObjectProcedure;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-/**\r
- * Analyses the dependencies between transferable graphs. Usage:\r
- * <pre>\r
-GraphDependencyAnalyzer<TransferableGraph1> analyzer =\r
- new GraphDependencyAnalyzer<TransferableGraph1>();\r
-for(TransferableGraph1 tg : tgs)\r
- analyzer.addGraph(tg, tg);\r
-if(!analyzer.analyzeDependency()) {\r
- <Report problems> analyzer.getConflicts()\r
-}\r
-else if(!analyzer.doesSatisfyExternalDependencies(graph)) {\r
- <Report problems> analyzer.getUnsatisfiedDependencies()\r
-}\r
-else\r
- for(TransferableGraph1 tg : analyzer.getSortedGraphs())\r
- <Import> tg\r
-}\r
- * </pre>\r
- * @author Hannu Niemist�\r
- */\r
-public class GraphDependencyAnalyzer<T extends Comparable<T>> {\r
-\r
- private ArrayList<IU> graphs = new ArrayList<IU>();\r
- private IdentityNode root = new IdentityNode(null, null);\r
- \r
- public static class IUList {\r
- IU head;\r
- IUList tail;\r
- \r
- public IUList(IU head, IUList tail) {\r
- this.head = head;\r
- this.tail = tail;\r
- } \r
- }\r
- \r
- public static Collection<IU> toCollection(IUList list) {\r
- ArrayList<IU> result = new ArrayList<IU>();\r
- while(list != null) {\r
- result.add(list.head);\r
- list = list.tail;\r
- }\r
- return result;\r
- }\r
- \r
- public static class IdentityNode {\r
- IdentityNode parent;\r
- String name;\r
- IUList provides = null;\r
- IUList providesOptionally = null;\r
- IUList requires = null;\r
- THashMap<String, IdentityNode> children = null; \r
-\r
- public IdentityNode(IdentityNode parent, String name) {\r
- this.parent = parent;\r
- this.name = name;\r
- }\r
-\r
- public IdentityNode get(String name) {\r
- if(children == null) {\r
- children = new THashMap<String, IdentityNode>(4);\r
- IdentityNode node = new IdentityNode(this, name);\r
- children.put(name, node);\r
- return node;\r
- }\r
- else {\r
- IdentityNode node = children.get(name);\r
- if(node == null) {\r
- node = new IdentityNode(this, name);\r
- children.put(name, node); \r
- }\r
- return node;\r
- }\r
- }\r
- \r
- @Override\r
- public String toString() {\r
- if(parent == null || parent.parent == null) {\r
- if(name.equals(""))\r
- return "http:/";\r
- else\r
- return name;\r
- }\r
- return parent.toString() + "/" + name;\r
- }\r
- \r
- public IUList getRequires() {\r
- return requires;\r
- }\r
- }\r
- \r
- public static class IU implements Comparable<IU> {\r
- final Comparable id;\r
- final TransferableGraph1 tg;\r
- IdentityNode[] identities;\r
- THashMap<IUList, IUList> tgLists = new THashMap<IUList, IUList>();\r
- THashSet<IU> dependencies = new THashSet<IU>();\r
-\r
- public IU(Comparable id, TransferableGraph1 tg) {\r
- this.id = id;\r
- this.tg = tg;\r
- }\r
- \r
- public Object getId() {\r
- return id;\r
- }\r
- \r
- public IUList pushThis(IUList node) {\r
- IUList result = tgLists.get(node);\r
- if(result == null) {\r
- result = new IUList(this, node);\r
- tgLists.put(node, result);\r
- }\r
- return result;\r
- }\r
- \r
- public IdentityNode defineIdentity(IdentityNode root, TIntIntHashMap identityIdToIdentityIndex, Identity identity) {\r
- IdentityDefinition definition = identity.definition;\r
- if(identities[identity.resource] != null) return identities[identity.resource]; \r
- if(definition instanceof External) {\r
- External def = (External)definition;\r
- IdentityNode in = identities[def.parent];\r
- if(in == null)\r
- in = defineIdentity(root, identityIdToIdentityIndex, tg.identities[identityIdToIdentityIndex.get(def.parent)]);\r
- IdentityNode node = in.get(def.name); \r
- node.requires = pushThis(node.requires);\r
- identities[identity.resource] = node;\r
- return node;\r
- }\r
- else if(definition instanceof Internal) {\r
- Internal def = (Internal)definition;\r
- IdentityNode in = identities[def.parent];\r
- if(in == null)\r
- in = defineIdentity(root, identityIdToIdentityIndex, tg.identities[identityIdToIdentityIndex.get(def.parent)]);\r
- IdentityNode node = in.get(def.name); \r
- node.provides = pushThis(node.provides);\r
- identities[identity.resource] = node;\r
- return node;\r
- }\r
- else if(definition instanceof Root) {\r
- Root def = (Root)definition;\r
- IdentityNode node = root.get(def.name);\r
- identities[identity.resource] = node;\r
- return node;\r
- }\r
- else if(definition instanceof Optional) {\r
- Optional def = (Optional)definition;\r
- IdentityNode node = identities[def.parent].get(def.name);\r
- node.providesOptionally = pushThis(node.providesOptionally);\r
- identities[identity.resource] = node;\r
- return node;\r
- }\r
- throw new IllegalStateException("definition: " + definition);\r
- }\r
- \r
- public void findIdentities(IdentityNode root) {\r
- identities = new IdentityNode[tg.resourceCount];\r
- TIntIntHashMap identityIdToIdentityIndex = new TIntIntHashMap();\r
- for(int i=0;i<tg.identities.length;i++) {\r
- Identity id = tg.identities[i];\r
- identityIdToIdentityIndex.put(id.resource, i);\r
- }\r
- for(Identity identity : tg.identities) {\r
- defineIdentity(root, identityIdToIdentityIndex, identity);\r
- }\r
- }\r
-\r
- @Override\r
- public int compareTo(IU o) {\r
- return id.compareTo(o.id);\r
- }\r
- }\r
- \r
- private static class IUPair {\r
- public final IU a, b;\r
- public IUPair(IU a, IU b) { \r
- this.a = a;\r
- this.b = b;\r
- }\r
- @Override\r
- public int hashCode() {\r
- return a.hashCode() + 31 * b.hashCode();\r
- }\r
- @Override\r
- public boolean equals(Object obj) {\r
- if(this == obj)\r
- return true;\r
- IUPair other = (IUPair)obj;\r
- return a==other.a && b == other.b;\r
- }\r
- }\r
- \r
- private static class IUListPair {\r
- public final IUList a;\r
- public final IU b;\r
- public IUListPair(IUList a, IU b) { \r
- this.a = a;\r
- this.b = b;\r
- }\r
- @Override\r
- public int hashCode() {\r
- return a.hashCode() + 31 * b.hashCode();\r
- }\r
- @Override\r
- public boolean equals(Object obj) {\r
- if(this == obj)\r
- return true;\r
- IUListPair other = (IUListPair)obj;\r
- return a==other.a && b == other.b;\r
- }\r
- }\r
- \r
- private THashSet<IUPair> conflicts = new THashSet<IUPair>();\r
- private THashSet<IUListPair> handled = new THashSet<IUListPair>();\r
- \r
- private void addConflicts(IUList a) {\r
- while(a != null) {\r
- IUList b = a.tail;\r
- while(b != null) {\r
- conflicts.add(new IUPair(a.head, b.head));\r
- b=b.tail;\r
- }\r
- a = a.tail;\r
- }\r
- }\r
- \r
- private void addDependencies(IUList ius, IU dependency) { \r
- while(ius != null) {\r
- ius.head.dependencies.add(dependency); \r
- ius = ius.tail;\r
- }\r
- }\r
- \r
- private void analyzeDependency(IdentityNode node) {\r
- if(node.provides != null) {\r
- if(node.provides.tail != null) {\r
- addConflicts(node.provides);\r
- }\r
- else {\r
- if(node.requires != null && handled.add(new IUListPair(node.requires, node.provides.head)))\r
- addDependencies(node.requires, node.provides.head);\r
- }\r
- } \r
- else if(node.providesOptionally != null) {\r
- if(handled.add(new IUListPair(node.requires, node.providesOptionally.head)))\r
- addDependencies(node.requires, node.providesOptionally.head);\r
- }\r
- if(node.children != null)\r
- for(IdentityNode child : node.children.values())\r
- analyzeDependency(child);\r
- } \r
-\r
- /**\r
- * Adds a graph to the analyzer\r
- * @param id An identifier that is used when reporting the results.\r
- * @param tg A tranferable graph.\r
- */\r
- public void addGraph(T id, TransferableGraph1 tg) {\r
- assert(id != null);\r
- IU iu = new IU(id, tg);\r
- graphs.add(iu);\r
- iu.findIdentities(root);\r
- }\r
- \r
- ArrayList<T> sortedGraphs = new ArrayList<T>();\r
- \r
- class Sorter {\r
- THashSet<IU> alreadyInList = new THashSet<IU>();\r
- @SuppressWarnings("unchecked")\r
- public void add(IU iu) {\r
- if(alreadyInList.add(iu)) {\r
- /*System.out.println(iu.id);\r
- for(IU dep : iu.dependencies)\r
- System.out.println(" " + dep.id);\r
- */\r
- ArrayList<IU> list = new ArrayList<IU>(iu.dependencies);\r
- Collections.sort(list);\r
- for(IU dep : list)\r
- add(dep);\r
- sortedGraphs.add((T)iu.id);\r
- }\r
- }\r
- }\r
- \r
- private void sortByDependency() {\r
- //System.out.println("SortByDependency");\r
- sortedGraphs.clear();\r
- Sorter sorter = new Sorter();\r
- Collections.sort(graphs);\r
- for(IU iu : graphs)\r
- sorter.add(iu);\r
- }\r
-\r
- /**\r
- * Analyzes the dependency between transferable graphs\r
- * @return true, if there are no conflicts or cyclic dependencies\r
- */\r
- public boolean analyzeDependency() {\r
- analyzeDependency(root);\r
- sortByDependency();\r
- return conflicts.isEmpty();\r
- }\r
-\r
- private ArrayList<IdentityNode> unsatisfiedRequirements = new ArrayList<IdentityNode>();\r
- \r
- private void doesSatisfyExternalDependencies(final ReadGraph g, IdentityNode node, Resource resource) { \r
- try {\r
- final Map<String, Resource> childMap = g.syncRequest(new UnescapedChildMapOfResource(resource));\r
- if(node.children != null)\r
- node.children.forEachEntry(new TObjectObjectProcedure<String, GraphDependencyAnalyzer.IdentityNode>() {\r
- @Override\r
- public boolean execute(String name, IdentityNode child) {\r
- Resource childResource = childMap.get(name);\r
- if(childResource == null) {\r
- if(child.provides == null && child.requires != null)\r
- unsatisfiedRequirements.add(child);\r
- }\r
- else\r
- doesSatisfyExternalDependencies(g, child, childResource);\r
- return true;\r
- }\r
- });\r
- } catch(DatabaseException e) {\r
- throw new RuntimeDatabaseException(e);\r
- }\r
- }\r
- \r
- /**\r
- * Returns true if previously analyzed graphs are importable,\r
- * i.e. all external dependencies are satisfied. This method can be called\r
- * after {@code analyzeDependency} method.\r
- */\r
- public boolean doesSatisfyExternalDependencies(ReadGraph g) throws DatabaseException {\r
- unsatisfiedRequirements.clear();\r
- IdentityNode rootLibrary = root.get("");\r
- if(rootLibrary != null)\r
- try {\r
- doesSatisfyExternalDependencies(g, root.get(""), g.getRootLibrary());\r
- } catch(RuntimeDatabaseException e) {\r
- Throwable cause = e.getCause();\r
- if(cause instanceof DatabaseException)\r
- throw (DatabaseException)cause;\r
- else\r
- throw e;\r
- }\r
- return unsatisfiedRequirements.isEmpty();\r
- }\r
- \r
- public Read<Boolean> queryExternalDependenciesSatisfied = new Read<Boolean>() {\r
- @Override\r
- public Boolean perform(ReadGraph graph) throws DatabaseException {\r
- return doesSatisfyExternalDependencies(graph);\r
- }\r
- }; \r
- \r
- /**\r
- * Returns a collection URIs of all resources not found in the database.\r
- * This method can be called after {@code doesSatisfyExternalDependencies} method.\r
- * @return\r
- */\r
- public ArrayList<IdentityNode> getUnsatisfiedDependencies() {\r
- return unsatisfiedRequirements;\r
- }\r
- \r
- /**\r
- * Returns all conflicting pairs of graphs. This method can be called after \r
- * {@code analyzeDependency} method. \r
- */\r
- @SuppressWarnings("unchecked")\r
- public Collection<Pair<T, T>> getConflicts() {\r
- ArrayList<Pair<T,T>> result = new ArrayList<Pair<T,T>>();\r
- for(IUPair pair : conflicts)\r
- result.add(Pair.make((T)pair.a.id, (T)pair.b.id));\r
- return result;\r
- }\r
- \r
- /**\r
- * Returns a list sorted by dependency so that if A depends on B\r
- * then A is in the list later than B.\r
- * @return\r
- */\r
- public List<T> getSortedGraphs() {\r
- return sortedGraphs;\r
- }\r
- \r
-}\r
+package org.simantics.graph.db;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.uri.UnescapedChildMapOfResource;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.RuntimeDatabaseException;
+import org.simantics.db.request.Read;
+import org.simantics.graph.representation.External;
+import org.simantics.graph.representation.Identity;
+import org.simantics.graph.representation.IdentityDefinition;
+import org.simantics.graph.representation.Internal;
+import org.simantics.graph.representation.Optional;
+import org.simantics.graph.representation.Root;
+import org.simantics.graph.representation.TransferableGraph1;
+import org.simantics.utils.datastructures.Pair;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.map.hash.TIntIntHashMap;
+import gnu.trove.procedure.TObjectObjectProcedure;
+import gnu.trove.set.hash.THashSet;
+
+/**
+ * Analyses the dependencies between transferable graphs. Usage:
+ * <pre>
+GraphDependencyAnalyzer<TransferableGraph1> analyzer =
+ new GraphDependencyAnalyzer<TransferableGraph1>();
+for(TransferableGraph1 tg : tgs)
+ analyzer.addGraph(tg, tg);
+if(!analyzer.analyzeDependency()) {
+ <Report problems> analyzer.getConflicts()
+}
+else if(!analyzer.doesSatisfyExternalDependencies(graph)) {
+ <Report problems> analyzer.getUnsatisfiedDependencies()
+}
+else
+ for(TransferableGraph1 tg : analyzer.getSortedGraphs())
+ <Import> tg
+}
+ * </pre>
+ * @author Hannu Niemist�
+ */
+public class GraphDependencyAnalyzer<T extends Comparable<T>> {
+
+ private ArrayList<IU> graphs = new ArrayList<IU>();
+ private IdentityNode root = new IdentityNode(null, null);
+
+ public static class IUList {
+ IU head;
+ IUList tail;
+
+ public IUList(IU head, IUList tail) {
+ this.head = head;
+ this.tail = tail;
+ }
+ }
+
+ public static Collection<IU> toCollection(IUList list) {
+ ArrayList<IU> result = new ArrayList<IU>();
+ while(list != null) {
+ result.add(list.head);
+ list = list.tail;
+ }
+ return result;
+ }
+
+ public static class IdentityNode {
+ IdentityNode parent;
+ String name;
+ IUList provides = null;
+ IUList providesOptionally = null;
+ IUList requires = null;
+ THashMap<String, IdentityNode> children = null;
+
+ public IdentityNode(IdentityNode parent, String name) {
+ this.parent = parent;
+ this.name = name;
+ }
+
+ public IdentityNode get(String name) {
+ if(children == null) {
+ children = new THashMap<String, IdentityNode>(4);
+ IdentityNode node = new IdentityNode(this, name);
+ children.put(name, node);
+ return node;
+ }
+ else {
+ IdentityNode node = children.get(name);
+ if(node == null) {
+ node = new IdentityNode(this, name);
+ children.put(name, node);
+ }
+ return node;
+ }
+ }
+
+ @Override
+ public String toString() {
+ if(parent == null || parent.parent == null) {
+ if(name.equals(""))
+ return "http:/";
+ else
+ return name;
+ }
+ return parent.toString() + "/" + name;
+ }
+
+ public IUList getRequires() {
+ return requires;
+ }
+ }
+
+ public static class IU implements Comparable<IU> {
+ final Comparable id;
+ final TransferableGraph1 tg;
+ IdentityNode[] identities;
+ THashMap<IUList, IUList> tgLists = new THashMap<IUList, IUList>();
+ THashSet<IU> dependencies = new THashSet<IU>();
+
+ public IU(Comparable id, TransferableGraph1 tg) {
+ this.id = id;
+ this.tg = tg;
+ }
+
+ public Object getId() {
+ return id;
+ }
+
+ public IUList pushThis(IUList node) {
+ IUList result = tgLists.get(node);
+ if(result == null) {
+ result = new IUList(this, node);
+ tgLists.put(node, result);
+ }
+ return result;
+ }
+
+ public IdentityNode defineIdentity(IdentityNode root, TIntIntHashMap identityIdToIdentityIndex, Identity identity) {
+ IdentityDefinition definition = identity.definition;
+ if(identities[identity.resource] != null) return identities[identity.resource];
+ if(definition instanceof External) {
+ External def = (External)definition;
+ IdentityNode in = identities[def.parent];
+ if(in == null)
+ in = defineIdentity(root, identityIdToIdentityIndex, tg.identities[identityIdToIdentityIndex.get(def.parent)]);
+ IdentityNode node = in.get(def.name);
+ node.requires = pushThis(node.requires);
+ identities[identity.resource] = node;
+ return node;
+ }
+ else if(definition instanceof Internal) {
+ Internal def = (Internal)definition;
+ IdentityNode in = identities[def.parent];
+ if(in == null)
+ in = defineIdentity(root, identityIdToIdentityIndex, tg.identities[identityIdToIdentityIndex.get(def.parent)]);
+ IdentityNode node = in.get(def.name);
+ node.provides = pushThis(node.provides);
+ identities[identity.resource] = node;
+ return node;
+ }
+ else if(definition instanceof Root) {
+ Root def = (Root)definition;
+ IdentityNode node = root.get(def.name);
+ identities[identity.resource] = node;
+ return node;
+ }
+ else if(definition instanceof Optional) {
+ Optional def = (Optional)definition;
+ IdentityNode node = identities[def.parent].get(def.name);
+ node.providesOptionally = pushThis(node.providesOptionally);
+ identities[identity.resource] = node;
+ return node;
+ }
+ throw new IllegalStateException("definition: " + definition);
+ }
+
+ public void findIdentities(IdentityNode root) {
+ identities = new IdentityNode[tg.resourceCount];
+ TIntIntHashMap identityIdToIdentityIndex = new TIntIntHashMap();
+ for(int i=0;i<tg.identities.length;i++) {
+ Identity id = tg.identities[i];
+ identityIdToIdentityIndex.put(id.resource, i);
+ }
+ for(Identity identity : tg.identities) {
+ defineIdentity(root, identityIdToIdentityIndex, identity);
+ }
+ }
+
+ @Override
+ public int compareTo(IU o) {
+ return id.compareTo(o.id);
+ }
+ }
+
+ private static class IUPair {
+ public final IU a, b;
+ public IUPair(IU a, IU b) {
+ this.a = a;
+ this.b = b;
+ }
+ @Override
+ public int hashCode() {
+ return a.hashCode() + 31 * b.hashCode();
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if(this == obj)
+ return true;
+ IUPair other = (IUPair)obj;
+ return a==other.a && b == other.b;
+ }
+ }
+
+ private static class IUListPair {
+ public final IUList a;
+ public final IU b;
+ public IUListPair(IUList a, IU b) {
+ this.a = a;
+ this.b = b;
+ }
+ @Override
+ public int hashCode() {
+ return a.hashCode() + 31 * b.hashCode();
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if(this == obj)
+ return true;
+ IUListPair other = (IUListPair)obj;
+ return a==other.a && b == other.b;
+ }
+ }
+
+ private THashSet<IUPair> conflicts = new THashSet<IUPair>();
+ private THashSet<IUListPair> handled = new THashSet<IUListPair>();
+
+ private void addConflicts(IUList a) {
+ while(a != null) {
+ IUList b = a.tail;
+ while(b != null) {
+ conflicts.add(new IUPair(a.head, b.head));
+ b=b.tail;
+ }
+ a = a.tail;
+ }
+ }
+
+ private void addDependencies(IUList ius, IU dependency) {
+ while(ius != null) {
+ ius.head.dependencies.add(dependency);
+ ius = ius.tail;
+ }
+ }
+
+ private void analyzeDependency(IdentityNode node) {
+ if(node.provides != null) {
+ if(node.provides.tail != null) {
+ addConflicts(node.provides);
+ }
+ else {
+ if(node.requires != null && handled.add(new IUListPair(node.requires, node.provides.head)))
+ addDependencies(node.requires, node.provides.head);
+ }
+ }
+ else if(node.providesOptionally != null) {
+ if(handled.add(new IUListPair(node.requires, node.providesOptionally.head)))
+ addDependencies(node.requires, node.providesOptionally.head);
+ }
+ if(node.children != null)
+ for(IdentityNode child : node.children.values())
+ analyzeDependency(child);
+ }
+
+ /**
+ * Adds a graph to the analyzer
+ * @param id An identifier that is used when reporting the results.
+ * @param tg A tranferable graph.
+ */
+ public void addGraph(T id, TransferableGraph1 tg) {
+ assert(id != null);
+ IU iu = new IU(id, tg);
+ graphs.add(iu);
+ iu.findIdentities(root);
+ }
+
+ ArrayList<T> sortedGraphs = new ArrayList<T>();
+
+ class Sorter {
+ THashSet<IU> alreadyInList = new THashSet<IU>();
+ @SuppressWarnings("unchecked")
+ public void add(IU iu) {
+ if(alreadyInList.add(iu)) {
+ /*System.out.println(iu.id);
+ for(IU dep : iu.dependencies)
+ System.out.println(" " + dep.id);
+ */
+ ArrayList<IU> list = new ArrayList<IU>(iu.dependencies);
+ Collections.sort(list);
+ for(IU dep : list)
+ add(dep);
+ sortedGraphs.add((T)iu.id);
+ }
+ }
+ }
+
+ private void sortByDependency() {
+ //System.out.println("SortByDependency");
+ sortedGraphs.clear();
+ Sorter sorter = new Sorter();
+ Collections.sort(graphs);
+ for(IU iu : graphs)
+ sorter.add(iu);
+ }
+
+ /**
+ * Analyzes the dependency between transferable graphs
+ * @return true, if there are no conflicts or cyclic dependencies
+ */
+ public boolean analyzeDependency() {
+ analyzeDependency(root);
+ sortByDependency();
+ return conflicts.isEmpty();
+ }
+
+ private ArrayList<IdentityNode> unsatisfiedRequirements = new ArrayList<IdentityNode>();
+
+ private void doesSatisfyExternalDependencies(final ReadGraph g, IdentityNode node, Resource resource) {
+ try {
+ final Map<String, Resource> childMap = g.syncRequest(new UnescapedChildMapOfResource(resource));
+ if(node.children != null)
+ node.children.forEachEntry(new TObjectObjectProcedure<String, GraphDependencyAnalyzer.IdentityNode>() {
+ @Override
+ public boolean execute(String name, IdentityNode child) {
+ Resource childResource = childMap.get(name);
+ if(childResource == null) {
+ if(child.provides == null && child.requires != null)
+ unsatisfiedRequirements.add(child);
+ }
+ else
+ doesSatisfyExternalDependencies(g, child, childResource);
+ return true;
+ }
+ });
+ } catch(DatabaseException e) {
+ throw new RuntimeDatabaseException(e);
+ }
+ }
+
+ /**
+ * Returns true if previously analyzed graphs are importable,
+ * i.e. all external dependencies are satisfied. This method can be called
+ * after {@code analyzeDependency} method.
+ */
+ public boolean doesSatisfyExternalDependencies(ReadGraph g) throws DatabaseException {
+ unsatisfiedRequirements.clear();
+ IdentityNode rootLibrary = root.get("");
+ if(rootLibrary != null)
+ try {
+ doesSatisfyExternalDependencies(g, root.get(""), g.getRootLibrary());
+ } catch(RuntimeDatabaseException e) {
+ Throwable cause = e.getCause();
+ if(cause instanceof DatabaseException)
+ throw (DatabaseException)cause;
+ else
+ throw e;
+ }
+ return unsatisfiedRequirements.isEmpty();
+ }
+
+ public Read<Boolean> queryExternalDependenciesSatisfied = new Read<Boolean>() {
+ @Override
+ public Boolean perform(ReadGraph graph) throws DatabaseException {
+ return doesSatisfyExternalDependencies(graph);
+ }
+ };
+
+ /**
+ * Returns a collection URIs of all resources not found in the database.
+ * This method can be called after {@code doesSatisfyExternalDependencies} method.
+ * @return
+ */
+ public ArrayList<IdentityNode> getUnsatisfiedDependencies() {
+ return unsatisfiedRequirements;
+ }
+
+ /**
+ * Returns all conflicting pairs of graphs. This method can be called after
+ * {@code analyzeDependency} method.
+ */
+ @SuppressWarnings("unchecked")
+ public Collection<Pair<T, T>> getConflicts() {
+ ArrayList<Pair<T,T>> result = new ArrayList<Pair<T,T>>();
+ for(IUPair pair : conflicts)
+ result.add(Pair.make((T)pair.a.id, (T)pair.b.id));
+ return result;
+ }
+
+ /**
+ * Returns a list sorted by dependency so that if A depends on B
+ * then A is in the list later than B.
+ * @return
+ */
+ public List<T> getSortedGraphs() {
+ return sortedGraphs;
+ }
+
+}