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