]> gerrit.simantics Code Review - simantics/interop.git/blobdiff - org.simantics.interop.mapping/src/org/simantics/interop/mapping/data/GraphNode.java
refs #3483
[simantics/interop.git] / org.simantics.interop.mapping / src / org / simantics / interop / mapping / data / GraphNode.java
diff --git a/org.simantics.interop.mapping/src/org/simantics/interop/mapping/data/GraphNode.java b/org.simantics.interop.mapping/src/org/simantics/interop/mapping/data/GraphNode.java
new file mode 100644 (file)
index 0000000..c775e8b
--- /dev/null
@@ -0,0 +1,820 @@
+package org.simantics.interop.mapping.data;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Set;\r
+\r
+import org.simantics.interop.mapping.Logger;\r
+import org.simantics.utils.datastructures.hints.HintContext;\r
+\r
+\r
+/**\r
+ * \r
+ * \r
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
+ *\r
+ * @param <T>\r
+ */\r
+public class GraphNode<T> extends HintContext {\r
+       protected T data;\r
+       private List<Link<T>> nodes = new ArrayList<Link<T>>();\r
+\r
+       private boolean disposed = false;\r
+       \r
+       public GraphNode(T data) {\r
+               this.data = data;\r
+       }\r
+       \r
+       /**\r
+        * Returns data.\r
+        * @return\r
+        */\r
+       public T getData() {\r
+               return data;\r
+       }\r
+\r
+       \r
+       @Override\r
+    public void setHint(Key key, Object value) {\r
+               _checkDisposed();\r
+               if (value == null)\r
+                       return; \r
+               super.setHint(key, value);\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       /**\r
+        * Adds link to the other node.\r
+        * @param relationName name of the link from this node to the other node.\r
+        * @param inverseRelationName nam,e of the link from the other node to this node.\r
+        * @param node the other node.\r
+        * @return the created link. Returns null if the link cannot be created.\r
+        */\r
+       public Link<T> addLink(String relationName, String inverseRelationName,GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Logger.defaultLogInfo("Node link " + data + " " + node.data + " " + relationName + " " + inverseRelationName +"\n");\r
+               if(containsLink(relationName, node) && node.containsLink(inverseRelationName, this)) {\r
+                       Logger.defaultLogWarning("Node " + getData() + " has already given child " +  node.getData() + " ,with name " + relationName + " / " + inverseRelationName);\r
+                       return null;\r
+               }\r
+               Link<T> rel = _addLink(relationName,node);\r
+               Link<T> inv = node._addLink(inverseRelationName,this);\r
+               rel.setInverseLink(inv);\r
+               inv.setMainLink(rel);\r
+               return rel;\r
+       }\r
+       \r
+       /**\r
+        * Adds link to the other node, with <pre>null</pre> link name from the other node to this node.\r
+        * @param relationName\r
+        * @param node name of the link from this node to the other node\r
+        * @return the created link. Returns null if the link cannot be created.\r
+        */\r
+       public Link<T> addLink(String relationName,GraphNode<T> node) {\r
+               _checkDisposed();\r
+               if(containsLink(relationName, node)) {\r
+                       Logger.defaultLogWarning("Node " + getData() + " has already given child " +  node.getData() + " ,with name " + relationName );\r
+                       return null;\r
+               }\r
+               Link<T> rel = _addLink(relationName,node);\r
+               Link<T> inv = node._addLink(null,this);\r
+               rel.setInverseLink(inv);\r
+               inv.setMainLink(rel);\r
+               return rel;\r
+       }\r
+       \r
+       /**\r
+        * Adds link to the other node, with copying link properties from given link.\r
+        * @param link\r
+        * @param node name of the link from this node to the other node\r
+        * @return the created link. Returns null if the link cannot be created.\r
+        */\r
+       public Link<T> addLink(Link<T> link ,GraphNode<T> node) {\r
+               _checkDisposed();\r
+               if(containsLink(link.getName(), node) && node.containsLink(link.getInverseName(), this)) {\r
+                       Logger.defaultLogWarning("Node " + getData() + " has already given child " +  node.getData() + " ,with name " + link.getName() + " / " + link.getInverseName());\r
+                       return null;\r
+               }\r
+               Link<T> rel = _addLink(link.getName(),node);\r
+               Link<T> inv = node._addLink(link.getInverseName(),this);\r
+               rel.setInverseLink(inv);\r
+               inv.setMainLink(rel);\r
+               rel.setHints(link.getHints());\r
+               inv.setHints(link.getInverse().getHints());\r
+               return rel;\r
+       }\r
+       \r
+       protected Link<T> _addLink(String relation,  GraphNode<T> node) {\r
+               Link<T> link = new Link<T>(this, relation,node);\r
+               nodes.add(link);\r
+               return link;\r
+       }\r
+       \r
+       protected Link<T> _removeLink(String relation,String inverse, GraphNode<T> node) {\r
+               for (int i = 0; i < nodes.size(); i++) {\r
+                       Link<T> link = nodes.get(i);\r
+                       if (node.equals(link.to()) && equals(relation,link.getName()) && equals(inverse,link.getInverseName())) {\r
+                               return nodes.remove(i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       protected Link<T> _removeLink(String relation) {\r
+               for (int i = 0; i < nodes.size(); i++) {\r
+                       Link<T> link = nodes.get(i);\r
+                       if (equals(relation,link.getName())) {\r
+                               return nodes.remove(i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       protected Link<T> _removeLink(String relation, String inverse) {\r
+               for (int i = 0; i < nodes.size(); i++) {\r
+                       Link<T> link = nodes.get(i);\r
+                       if (equals(relation,link.getName()) && equals(inverse,link.getInverseName())) {\r
+                               return nodes.remove(i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       protected Link<T> _removeLink(String relation, GraphNode<T> node) {\r
+               for (int i = 0; i < nodes.size(); i++) {\r
+                       Link<T> link = nodes.get(i);\r
+                       if (node.equals(link.to()) && equals(relation,link.getName())) {\r
+                               return nodes.remove(i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       protected Link<T> _removeLink(GraphNode<T> node) {\r
+               for (int i = 0; i < nodes.size(); i++ ) {\r
+                       Link<T> link = nodes.get(i);\r
+                       if (node.equals(link.to())) {\r
+                               return nodes.remove(i);\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       protected boolean _removeLink(Link<T> link) {\r
+               return nodes.remove(link);\r
+       }\r
+       \r
+       /**\r
+        * Removes the given link.\r
+        * @param link\r
+        * @return true if the link was removed.\r
+        */\r
+       public boolean removeLink(Link<T> link) {\r
+               _checkDisposed();\r
+               if (_removeLink(link)) {\r
+                       if (link.hasInverse()) {\r
+                               link.to()._removeLink(link.getInverse());\r
+                       }\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Removes a link (or multiple links).\r
+        * @param relation the name of the link from this node.\r
+        * @return true if a link was removed.\r
+        */\r
+       public boolean removeLink(String relation) {\r
+               _checkDisposed();\r
+               boolean deleted = false;\r
+               while (true) {\r
+                       Link<T> removed = _removeLink(relation);\r
+                       if (removed == null)\r
+                               break;\r
+                       if (removed.hasInverse()) {\r
+                               removed.to()._removeLink(removed.getInverse());\r
+                               deleted = true;\r
+                       }\r
+               }\r
+               return deleted;\r
+       }\r
+       \r
+       /**\r
+        * Removes a link (or multiple links).\r
+        * @param relation the name of the link from this node.\r
+        * @return true if the link was removed.\r
+        */\r
+       public boolean removeLink(String relation, String inverse) {\r
+               _checkDisposed();\r
+               boolean deleted = false;\r
+               while (true) {\r
+                       Link<T> removed = _removeLink(relation, inverse);\r
+                       if (removed == null)\r
+                               break;\r
+                       if (removed.hasInverse()) {\r
+                               removed.to()._removeLink(removed.getInverse());\r
+                               deleted = true;\r
+                       }\r
+               }\r
+               return deleted;\r
+       }\r
+\r
+       /**\r
+        * Removes a link.\r
+        * @param relation the name of the link from this node to the other node.\r
+        * @param node the other node.\r
+        * @return true if the link was removed.\r
+        */\r
+       public boolean removeLink(String relation, GraphNode<T> node) {\r
+               _checkDisposed();\r
+               \r
+               boolean deleted = false;\r
+               while (true) {\r
+                       Link<T> removed = _removeLink(relation, node);\r
+                       if (removed == null)\r
+                               break;\r
+                       if (removed.hasInverse()) {\r
+                               removed.to()._removeLink(removed.getInverse());\r
+                               deleted = true;\r
+                       }\r
+               }\r
+               return deleted;\r
+       }\r
+       \r
+       /**\r
+        * Removes all links from this node to the other node.\r
+        * @param node the other node.\r
+        * @return\r
+        */\r
+       public boolean removeLink(GraphNode<T> node) {\r
+               _checkDisposed();\r
+               boolean deleted = false;\r
+               while (true) {\r
+                       Link<T> removed = _removeLink(node);\r
+                       if (removed == null)\r
+                               break;\r
+                       if (removed.hasInverse()) {\r
+                               removed.to()._removeLink(removed.getInverse());\r
+                               deleted = true;\r
+                       }\r
+               }\r
+               return deleted;\r
+       }\r
+       \r
+       /**\r
+        * Removes a link.\r
+        * @param relation the name of the link from this node to the other node.\r
+        * @param inverse the name of the link from the other node to this node.\r
+        * @param node the other node.\r
+        * @return true if the link was removed.\r
+        */\r
+       public boolean removeLink(String relation, String inverse, GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Link<T> removed = _removeLink(relation, inverse, node);\r
+               if (removed != null && removed.hasInverse()) {\r
+                       removed.to()._removeLink(removed.getInverse());\r
+                       return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       public boolean containsLink(GraphNode<T> node) {\r
+               _checkDisposed();\r
+               for (Link<T> link : nodes) {\r
+                       if (node.equals(link.to()))\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       public boolean containsLink(String relationName, GraphNode<T> node) {\r
+               _checkDisposed();\r
+               if (relationName == null)\r
+                       return false;\r
+               for (Link<T> link : nodes) {\r
+                       if (node.equals(link.to()) && equals(relationName,link.getName()))\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       public boolean containsLink(String relationName, String inverseName, GraphNode<T> node) {\r
+               _checkDisposed();\r
+               for (Link<T> link : nodes) {\r
+                       if (node.equals(link.to()) && equals(relationName,link.getName()) && equals(inverseName,link.getInverseName()))\r
+                               return true;\r
+               }\r
+               return false;\r
+       }\r
+\r
+       public Collection<Link<T>> getLinks() {\r
+               _checkDisposed();\r
+               Collection<Link<T>> coll = new ArrayList<Link<T>>(nodes);\r
+               return coll;\r
+       }\r
+       \r
+       public Collection<GraphNode<T>> getNodes(String rel) {\r
+               _checkDisposed();\r
+               Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(rel,link.getName()))\r
+                               result.add(link.to());\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Collection<Link<T>> getLinks(String rel) {\r
+               _checkDisposed();\r
+               Collection<Link<T>> result = new ArrayList<Link<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(rel,link.getName()))\r
+                               result.add(link);\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Collection<Link<T>> getLinks(String rel, GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Collection<Link<T>> result = new ArrayList<Link<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(rel,link.getName()) && link.to().equals(node))\r
+                               result.add(link);\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Collection<Link<T>> getLinks(String rel, String inverse) {\r
+               _checkDisposed();\r
+               Collection<Link<T>> result = new ArrayList<Link<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(rel,link.getName()) && equals(inverse, link.getInverseName()))\r
+                               result.add(link);\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Collection<Link<T>> getLinks(String rel, String inverse, GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Collection<Link<T>> result = new ArrayList<Link<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(rel,link.getName()) && equals(inverse, link.getInverseName()) && link.to().equals(node))\r
+                               result.add(link);\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Collection<GraphNode<T>> getNodesWithInv(String inv) {\r
+               _checkDisposed();\r
+               Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(inv,link.getInverseName()))\r
+                               result.add(link.to());\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Collection<GraphNode<T>> getNodes(String rel, String inv) {\r
+               _checkDisposed();\r
+               Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();\r
+\r
+               for (Link<T> link : nodes) {\r
+                       if (equals(rel,link.getName()) && equals(inv,link.getInverseName()))\r
+                               result.add(link.to());\r
+               }\r
+\r
+               return result;\r
+       }\r
+       \r
+       public static boolean equals(String s1, String s2) {\r
+               if (s1 != null)\r
+                       return s1.equals(s2);\r
+               if (s2 != null)\r
+                       return s2.equals(s1);\r
+               return true; // both null\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Returns links from this node to the given node.\r
+        * @param node\r
+        * @return\r
+        */\r
+       public Collection<Link<T>> getLinks(GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Collection<Link<T>> result = new ArrayList<Link<T>>();\r
+               for (Link<T> link : nodes) {\r
+                       if (link.to().equals(node))\r
+                               result.add(link);\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Returns names of links from this node to the given node. Does not return null names.\r
+        * @param node\r
+        * @return\r
+        */\r
+       public Collection<String> getRelations(GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Collection<String> result = new ArrayList<String>();\r
+               for (Link<T> link : nodes) {\r
+                       if (link.to().equals(node) && link.getName() != null)\r
+                               result.add(link.getName());\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Returns links from given node to this node.\r
+        * @param node\r
+        * @return\r
+        */\r
+       public Collection<Link<T>> getInverseLinks(GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Collection<Link<T>> result = new ArrayList<Link<T>>();\r
+               for (Link<T> link : nodes) {\r
+                       if (link.to().equals(node))\r
+                               result.add(link.getInverse());\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Returns names of links from given node to this node. Does not return null names.\r
+        * @param node\r
+        * @return\r
+        */\r
+       public Collection<String> getInverses(GraphNode<T> node) {\r
+               _checkDisposed();\r
+               Collection<String> result = new ArrayList<String>();\r
+               for (Link<T> link : nodes) {\r
+                       if (link.to().equals(node) && link.getInverseName() != null)\r
+                               result.add(link.getInverseName());\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       \r
+       \r
+       @Override\r
+       public int hashCode() {\r
+               return data.hashCode();\r
+       }\r
+       \r
+       @Override\r
+       public boolean equals(Object arg0) {\r
+               if (!arg0.getClass().equals(getClass()))\r
+                       return false;\r
+               GraphNode<?> other = (GraphNode<?>)arg0;\r
+               if (this == other)\r
+                       return true;\r
+               if (this.data == null || other.data == null)\r
+                       return false;\r
+               if (!data.equals(other.data))\r
+                       return false;\r
+\r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * Merges given nodes to new node. \r
+        * Does not copy hints!\r
+        * @param other\r
+        */\r
+       public GraphNode<T> merge(GraphNode<T> other, T mergedData) {\r
+               _checkDisposed();\r
+               GraphNode<T> mergedNode = new GraphNode<T>(mergedData);\r
+               copyLinks(this, mergedNode);\r
+               copyLinks(other, mergedNode);\r
+               \r
+               //mergedNode.setHints(other.getHints());\r
+               //mergedNode.setHints(this.getHints());\r
+               \r
+               this.destroy();\r
+               other.destroy();\r
+               \r
+               return mergedNode;\r
+       }\r
+       \r
+       \r
+       \r
+       \r
+       \r
+       /**\r
+        * Copies a link to other node.\r
+        * @param from the node containing original link\r
+        * @param to the node where link is copied to\r
+        * @param l the link that is copied.\r
+        * @return created link, if copy is successful. Otherwise returns null.\r
+        */\r
+       public static <T> Link<T> copyLink(GraphNode<T> from, GraphNode<T> to, Link<T> l) {\r
+               if (l.from() != from)\r
+                       throw new IllegalArgumentException("Link is not starting from " + from + " node.");\r
+               if (l.to() == to)\r
+                       return null;\r
+               if (to.containsLink(l.getName(), l.getInverseName(),l.to()))\r
+                       return null;\r
+               if (l.isMain()) {\r
+                       Link<T> newLink = to.addLink(l.getName(),l.getInverseName(),l.to());\r
+                       if (newLink != null) {\r
+                               newLink.setHints(l.getHints());\r
+                               newLink.getInverse().setHints(l.getInverse().getHints());\r
+                               return newLink;\r
+                       } else {\r
+                               return null;\r
+                       }\r
+               } else {\r
+                       Link<T> newLink = l.to().addLink(l.getInverseName(), l.getName(),to);\r
+                       if (newLink != null) {\r
+                               newLink.setHints(l.getInverse().getHints());\r
+                               newLink.getInverse().setHints(l.getHints());\r
+                               return newLink.getInverse();\r
+                       } else {\r
+                               return null;\r
+                       }\r
+               }       \r
+               \r
+       }\r
+       \r
+       /**\r
+        * Copies a link to other node, but inverts its direction.\r
+        * @param from the node containing original link\r
+        * @param to the node where link is copied to\r
+        * @param l the link that is copied.\r
+        * @return created link, if copy is successful. Otherwise returns null.\r
+        */\r
+       public static <T> Link<T> copyLinkInverse(GraphNode<T> from, GraphNode<T> to, Link<T> l) {\r
+               if (l.from() != from)\r
+                       throw new IllegalArgumentException("Link is not starting from " + from + " node.");\r
+               if (l.to() == to)\r
+                       return null;\r
+               if (to.containsLink(l.getInverseName(), l.getName(),l.to()))\r
+                       return null;\r
+               if (l.isMain()) {\r
+                       Link<T> newLink = l.to().addLink(l.getName(),l.getInverseName(),to);\r
+                       if (newLink != null) {\r
+                               newLink.setHints(l.getHints());\r
+                               newLink.getInverse().setHints(l.getInverse().getHints());\r
+                               return newLink;\r
+                       } else {\r
+                               return null;\r
+                       }\r
+               } else {\r
+                       Link<T> newLink = to.addLink(l.getInverseName(), l.getName(),l.to());\r
+                       if (newLink != null) {\r
+                               newLink.setHints(l.getInverse().getHints());\r
+                               newLink.getInverse().setHints(l.getHints());\r
+                               return newLink.getInverse();\r
+                       } else {\r
+                               return null;\r
+                       }\r
+               }       \r
+               \r
+       }\r
+\r
+       public static <T> void copyLinks(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {\r
+               for (Link<T> l : links) {\r
+                       copyLink(from, to, l);\r
+               }\r
+       }\r
+\r
+       \r
+       public static <T> void copyLinks(GraphNode<T> from, GraphNode<T> to) {\r
+               for (Link<T> l : from.getLinks()) {\r
+                       copyLink(from, to, l);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Copies a link to other node and removes the original link. If move is not successful, the original link is not removed.\r
+        * @param from the node containing original link\r
+        * @param to the node where link is copied to\r
+        * @param l the link that is moved.\r
+        * @return created link, if move is successful. Otherwise returns null. \r
+        */\r
+       public static <T> Link<T> moveLink(GraphNode<T> from, GraphNode<T> to, Link<T> l) {\r
+               Link<T> newLink = copyLink(from, to, l);\r
+               if (newLink != null) {\r
+                       from.removeLink(l);\r
+                       return newLink;\r
+               }\r
+               return null;\r
+       }\r
+       \r
+       public static <T> void moveLinks(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {\r
+               for (Link<T> l : links) {\r
+                       Link<T> newLink = copyLink(from, to, l);\r
+                       if (newLink != null) {\r
+                               from.removeLink(l);\r
+                       }       \r
+               }\r
+       }\r
+       \r
+       public static <T> void moveLinkInverses(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {\r
+               for (Link<T> l : links) {\r
+                       Link<T> newLink = copyLinkInverse(from, to, l);\r
+                       if (newLink != null) {\r
+                               from.removeLink(l);\r
+                       }       \r
+               }\r
+       }\r
+       \r
+       public static <T> void moveLinks(GraphNode<T> from, GraphNode<T> to) {\r
+               for (Link<T> l : from.getLinks()) {\r
+                       Link<T> newLink = copyLink(from, to, l);\r
+                       if (newLink != null) {\r
+                               from.removeLink(l);\r
+                       }       \r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Replaces a link with a link going first to given node, and the to the original destination.\r
+        * The link names and hints are copied to the created links.\r
+        * \r
+        * Example: link is 'a' -> 'b'. With insert attribute 'c', the result is 'a' -> 'c' -> 'b'.\r
+        *   \r
+        * @param link\r
+        * @param insert\r
+        * @return Array containing created links (2).\r
+        */\r
+       @SuppressWarnings("unchecked")\r
+       public static <T> Link<T>[] insert(Link<T> link, GraphNode<T> insert) {\r
+               GraphNode<T> a = link.from();\r
+               Link<T> c_b = moveLink(a, insert, link);\r
+               Link<T> a_c = a.addLink(c_b, insert);\r
+               return new Link[]{a_c,c_b};\r
+       }\r
+\r
+       \r
+       \r
+       /**\r
+        * Finds all nodes that have the same relations to the same nodes as this node. \r
+        * @return\r
+        */\r
+       public Collection<GraphNode<T>> getFullSimilars() {\r
+               _checkDisposed();\r
+               Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();\r
+               Set<GraphNode<T>> potential = new HashSet<GraphNode<T>>();\r
+               for (Link<T> link : nodes) {\r
+                       for (Link<T> link2 : link.to().nodes) {\r
+                               if (!link2.to().equals(this))\r
+                                       potential.add(link2.to());\r
+                       }\r
+               }\r
+               for (GraphNode<T> node : potential) {\r
+                       if (node.nodes.containsAll(nodes))\r
+                               result.add(node);\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Finds all nodes that have at least one similar relation to the same nodes as this node. \r
+        * @return\r
+        */\r
+       public Collection<GraphNode<T>> getSemiSimilars() {\r
+               _checkDisposed();\r
+               Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();\r
+               Set<GraphNode<T>> potential = new HashSet<GraphNode<T>>();\r
+               for (Link<T> link : nodes) {\r
+                       for (Link<T> link2 : link.to().nodes) {\r
+                               if (!link2.to().equals(this))\r
+                                       potential.add(link2.to());\r
+                       }\r
+               }\r
+               for (GraphNode<T> node : potential) {\r
+                       for (Link<T> link : nodes) {\r
+                               if (node.nodes.contains(link))\r
+                                       result.add(node);\r
+                       }\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+\r
+       /**\r
+        * Disposes this node. Removes all the links that connect to thi snode.\r
+        */\r
+       public void destroy() {\r
+               if (disposed)\r
+                       return;\r
+               Logger.defaultLogInfo("Node destroy " + data + " " + this);\r
+               Collection<Link<T>> coll = new ArrayList<Link<T>>();\r
+               coll.addAll(nodes);\r
+               nodes.clear();\r
+               for (Link<T> link : coll) {\r
+                       link.to()._removeLink(this);\r
+               }\r
+               nodes = null;\r
+               disposed = true;\r
+       }\r
+       \r
+       /**\r
+        * Removes this node from the graph, and reconnects parents and children of this node.\r
+        */\r
+       public void remove() {\r
+               if (disposed)\r
+                       return;\r
+               // FIXME: link.to may be *this.\r
+               Logger.defaultLogInfo("Node remove " + data + " " + this);\r
+               if (nodes.size() == 1) {\r
+                       Link<T> link = nodes.get(0);\r
+                       link.to().removeLink(link.getInverseName(), link.getName(), this);\r
+               } else {\r
+                       for (int i = 0; i < nodes.size() -1 ; i++) {\r
+                               Link<T> link1 = nodes.get(i);\r
+                               link1.to().removeLink(link1.getInverseName(), link1.getName(), this);\r
+                               for (int j = i; j < nodes.size(); j++) {\r
+                                       Link<T> link2 = nodes.get(j);\r
+                                       link2.to().removeLink(link2.getInverseName(), link2.getName(), this);\r
+                                       if (link1.to().equals(link2.to()))\r
+                                               continue;\r
+                                       link1.to().addLink(link1.getInverseName(),link2.getInverseName(),link2.to());\r
+                               }\r
+                               \r
+                       }\r
+               }\r
+               \r
+               nodes.clear();\r
+               nodes = null;\r
+               disposed = true;\r
+\r
+       }\r
+       \r
+       /**\r
+        * \r
+        * @return true if the node has been disposed and cannot be used anymore.\r
+        */\r
+       public boolean isDisposed() {\r
+               return disposed;\r
+       }\r
+       \r
+       /**\r
+        * Disposed the node and all nodes that are in the same graph.\r
+        */\r
+       public void dispose() {\r
+               if (disposed)\r
+                       return;\r
+               Collection<Link<T>> links = new ArrayList<Link<T>>();\r
+               links.addAll(nodes);\r
+               nodes.clear();\r
+               for (Link<T> l : links)\r
+                       l.to().dispose();\r
+               links.clear();\r
+               nodes = null;\r
+               disposed = true;\r
+       }\r
+       \r
+\r
+       protected void _checkDisposed() {\r
+               if (disposed) {\r
+                       Logger.defaultLogError("Remove Node, disposed " + this);\r
+                       throw new RuntimeException("Node " + this + " is disposed.");\r
+               }\r
+       }\r
+       \r
+       \r
+       public int distanceTo(GraphNode<T> node, String rel, String inv) {\r
+               if (node.equals(this))\r
+                       return 0;\r
+               int count = 0;\r
+               Set<GraphNode<T>> processed = new HashSet<GraphNode<T>>();\r
+               Set<GraphNode<T>> next = new HashSet<GraphNode<T>>();\r
+               next.add(this);\r
+               while (true) {\r
+                       if (next.size() == 0)\r
+                               return -1;\r
+                       count++;\r
+                       processed.addAll(next);\r
+                       Set<GraphNode<T>> nextNext = new HashSet<GraphNode<T>>();\r
+                       for (GraphNode<T> n : next) {\r
+                               for (GraphNode<T> nn : n.getNodes(rel, inv))\r
+                                       if (!processed.contains(nn))\r
+                                               nextNext.add(nn);\r
+                       }\r
+                       next = nextNext;\r
+                       if (next.contains(node))\r
+                               return count;\r
+               }\r
+       }\r
+}\r