1 package org.simantics.interop.mapping.data;
\r
3 import java.util.ArrayList;
\r
4 import java.util.Collection;
\r
5 import java.util.HashSet;
\r
6 import java.util.List;
\r
7 import java.util.Set;
\r
9 import org.simantics.utils.datastructures.hints.HintContext;
\r
10 import org.slf4j.Logger;
\r
16 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
\r
20 public class GraphNode<T> extends HintContext {
\r
22 private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(GraphNode.class);
\r
25 private List<Link<T>> nodes = new ArrayList<Link<T>>();
\r
27 private boolean disposed = false;
\r
29 public GraphNode(T data) {
\r
37 public T getData() {
\r
43 public void setHint(Key key, Object value) {
\r
47 super.setHint(key, value);
\r
54 * Adds link to the other node.
\r
55 * @param relationName name of the link from this node to the other node.
\r
56 * @param inverseRelationName nam,e of the link from the other node to this node.
\r
57 * @param node the other node.
\r
58 * @return the created link. Returns null if the link cannot be created.
\r
60 public Link<T> addLink(String relationName, String inverseRelationName,GraphNode<T> node) {
\r
62 LOGGER.info("Node link " + data + " " + node.data + " " + relationName + " " + inverseRelationName +"\n");
\r
63 if(containsLink(relationName, node) && node.containsLink(inverseRelationName, this)) {
\r
64 LOGGER.warn("Node " + getData() + " has already given child " + node.getData() + " ,with name " + relationName + " / " + inverseRelationName);
\r
67 Link<T> rel = _addLink(relationName,node);
\r
68 Link<T> inv = node._addLink(inverseRelationName,this);
\r
69 rel.setInverseLink(inv);
\r
70 inv.setMainLink(rel);
\r
75 * Adds link to the other node, with <pre>null</pre> link name from the other node to this node.
\r
76 * @param relationName
\r
77 * @param node name of the link from this node to the other node
\r
78 * @return the created link. Returns null if the link cannot be created.
\r
80 public Link<T> addLink(String relationName,GraphNode<T> node) {
\r
82 if(containsLink(relationName, node)) {
\r
83 LOGGER.warn("Node " + getData() + " has already given child " + node.getData() + " ,with name " + relationName );
\r
86 Link<T> rel = _addLink(relationName,node);
\r
87 Link<T> inv = node._addLink(null,this);
\r
88 rel.setInverseLink(inv);
\r
89 inv.setMainLink(rel);
\r
94 * Adds link to the other node, with copying link properties from given link.
\r
96 * @param node name of the link from this node to the other node
\r
97 * @return the created link. Returns null if the link cannot be created.
\r
99 public Link<T> addLink(Link<T> link ,GraphNode<T> node) {
\r
101 if(containsLink(link.getName(), node) && node.containsLink(link.getInverseName(), this)) {
\r
102 LOGGER.warn("Node " + getData() + " has already given child " + node.getData() + " ,with name " + link.getName() + " / " + link.getInverseName());
\r
105 Link<T> rel = _addLink(link.getName(),node);
\r
106 Link<T> inv = node._addLink(link.getInverseName(),this);
\r
107 rel.setInverseLink(inv);
\r
108 inv.setMainLink(rel);
\r
109 rel.setHints(link.getHints());
\r
110 inv.setHints(link.getInverse().getHints());
\r
114 protected Link<T> _addLink(String relation, GraphNode<T> node) {
\r
115 Link<T> link = new Link<T>(this, relation,node);
\r
120 protected Link<T> _removeLink(String relation,String inverse, GraphNode<T> node) {
\r
121 for (int i = 0; i < nodes.size(); i++) {
\r
122 Link<T> link = nodes.get(i);
\r
123 if (node.equals(link.to()) && equals(relation,link.getName()) && equals(inverse,link.getInverseName())) {
\r
124 return nodes.remove(i);
\r
130 protected Link<T> _removeLink(String relation) {
\r
131 for (int i = 0; i < nodes.size(); i++) {
\r
132 Link<T> link = nodes.get(i);
\r
133 if (equals(relation,link.getName())) {
\r
134 return nodes.remove(i);
\r
140 protected Link<T> _removeLink(String relation, String inverse) {
\r
141 for (int i = 0; i < nodes.size(); i++) {
\r
142 Link<T> link = nodes.get(i);
\r
143 if (equals(relation,link.getName()) && equals(inverse,link.getInverseName())) {
\r
144 return nodes.remove(i);
\r
150 protected Link<T> _removeLink(String relation, GraphNode<T> node) {
\r
151 for (int i = 0; i < nodes.size(); i++) {
\r
152 Link<T> link = nodes.get(i);
\r
153 if (node.equals(link.to()) && equals(relation,link.getName())) {
\r
154 return nodes.remove(i);
\r
160 protected Link<T> _removeLink(GraphNode<T> node) {
\r
161 for (int i = 0; i < nodes.size(); i++ ) {
\r
162 Link<T> link = nodes.get(i);
\r
163 if (node.equals(link.to())) {
\r
164 return nodes.remove(i);
\r
170 protected boolean _removeLink(Link<T> link) {
\r
171 return nodes.remove(link);
\r
175 * Removes the given link.
\r
177 * @return true if the link was removed.
\r
179 public boolean removeLink(Link<T> link) {
\r
181 if (_removeLink(link)) {
\r
182 if (link.hasInverse()) {
\r
183 link.to()._removeLink(link.getInverse());
\r
191 * Removes a link (or multiple links).
\r
192 * @param relation the name of the link from this node.
\r
193 * @return true if a link was removed.
\r
195 public boolean removeLink(String relation) {
\r
197 boolean deleted = false;
\r
199 Link<T> removed = _removeLink(relation);
\r
200 if (removed == null)
\r
202 if (removed.hasInverse()) {
\r
203 removed.to()._removeLink(removed.getInverse());
\r
211 * Removes a link (or multiple links).
\r
212 * @param relation the name of the link from this node.
\r
213 * @return true if the link was removed.
\r
215 public boolean removeLink(String relation, String inverse) {
\r
217 boolean deleted = false;
\r
219 Link<T> removed = _removeLink(relation, inverse);
\r
220 if (removed == null)
\r
222 if (removed.hasInverse()) {
\r
223 removed.to()._removeLink(removed.getInverse());
\r
232 * @param relation the name of the link from this node to the other node.
\r
233 * @param node the other node.
\r
234 * @return true if the link was removed.
\r
236 public boolean removeLink(String relation, GraphNode<T> node) {
\r
239 boolean deleted = false;
\r
241 Link<T> removed = _removeLink(relation, node);
\r
242 if (removed == null)
\r
244 if (removed.hasInverse()) {
\r
245 removed.to()._removeLink(removed.getInverse());
\r
253 * Removes all links from this node to the other node.
\r
254 * @param node the other node.
\r
257 public boolean removeLink(GraphNode<T> node) {
\r
259 boolean deleted = false;
\r
261 Link<T> removed = _removeLink(node);
\r
262 if (removed == null)
\r
264 if (removed.hasInverse()) {
\r
265 removed.to()._removeLink(removed.getInverse());
\r
274 * @param relation the name of the link from this node to the other node.
\r
275 * @param inverse the name of the link from the other node to this node.
\r
276 * @param node the other node.
\r
277 * @return true if the link was removed.
\r
279 public boolean removeLink(String relation, String inverse, GraphNode<T> node) {
\r
281 Link<T> removed = _removeLink(relation, inverse, node);
\r
282 if (removed != null && removed.hasInverse()) {
\r
283 removed.to()._removeLink(removed.getInverse());
\r
289 public boolean containsLink(GraphNode<T> node) {
\r
291 for (Link<T> link : nodes) {
\r
292 if (node.equals(link.to()))
\r
298 public boolean containsLink(String relationName, GraphNode<T> node) {
\r
300 if (relationName == null)
\r
302 for (Link<T> link : nodes) {
\r
303 if (node.equals(link.to()) && equals(relationName,link.getName()))
\r
309 public boolean containsLink(String relationName, String inverseName, GraphNode<T> node) {
\r
311 for (Link<T> link : nodes) {
\r
312 if (node.equals(link.to()) && equals(relationName,link.getName()) && equals(inverseName,link.getInverseName()))
\r
318 public Collection<Link<T>> getLinks() {
\r
320 Collection<Link<T>> coll = new ArrayList<Link<T>>(nodes);
\r
324 public Collection<GraphNode<T>> getNodes(String rel) {
\r
326 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
328 for (Link<T> link : nodes) {
\r
329 if (equals(rel,link.getName()))
\r
330 result.add(link.to());
\r
336 public Collection<Link<T>> getLinks(String rel) {
\r
338 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
340 for (Link<T> link : nodes) {
\r
341 if (equals(rel,link.getName()))
\r
348 public Collection<Link<T>> getLinks(String rel, GraphNode<T> node) {
\r
350 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
352 for (Link<T> link : nodes) {
\r
353 if (equals(rel,link.getName()) && link.to().equals(node))
\r
360 public Collection<Link<T>> getLinks(String rel, String inverse) {
\r
362 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
364 for (Link<T> link : nodes) {
\r
365 if (equals(rel,link.getName()) && equals(inverse, link.getInverseName()))
\r
372 public Collection<Link<T>> getLinks(String rel, String inverse, GraphNode<T> node) {
\r
374 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
376 for (Link<T> link : nodes) {
\r
377 if (equals(rel,link.getName()) && equals(inverse, link.getInverseName()) && link.to().equals(node))
\r
384 public Collection<GraphNode<T>> getNodesWithInv(String inv) {
\r
386 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
388 for (Link<T> link : nodes) {
\r
389 if (equals(inv,link.getInverseName()))
\r
390 result.add(link.to());
\r
396 public Collection<GraphNode<T>> getNodes(String rel, String inv) {
\r
398 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
400 for (Link<T> link : nodes) {
\r
401 if (equals(rel,link.getName()) && equals(inv,link.getInverseName()))
\r
402 result.add(link.to());
\r
408 public static boolean equals(String s1, String s2) {
\r
410 return s1.equals(s2);
\r
412 return s2.equals(s1);
\r
413 return true; // both null
\r
418 * Returns links from this node to the given node.
\r
422 public Collection<Link<T>> getLinks(GraphNode<T> node) {
\r
424 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
425 for (Link<T> link : nodes) {
\r
426 if (link.to().equals(node))
\r
433 * Returns names of links from this node to the given node. Does not return null names.
\r
437 public Collection<String> getRelations(GraphNode<T> node) {
\r
439 Collection<String> result = new ArrayList<String>();
\r
440 for (Link<T> link : nodes) {
\r
441 if (link.to().equals(node) && link.getName() != null)
\r
442 result.add(link.getName());
\r
448 * Returns links from given node to this node.
\r
452 public Collection<Link<T>> getInverseLinks(GraphNode<T> node) {
\r
454 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
455 for (Link<T> link : nodes) {
\r
456 if (link.to().equals(node))
\r
457 result.add(link.getInverse());
\r
463 * Returns names of links from given node to this node. Does not return null names.
\r
467 public Collection<String> getInverses(GraphNode<T> node) {
\r
469 Collection<String> result = new ArrayList<String>();
\r
470 for (Link<T> link : nodes) {
\r
471 if (link.to().equals(node) && link.getInverseName() != null)
\r
472 result.add(link.getInverseName());
\r
480 public int hashCode() {
\r
481 return data.hashCode();
\r
485 public boolean equals(Object arg0) {
\r
486 if (!arg0.getClass().equals(getClass()))
\r
488 GraphNode<?> other = (GraphNode<?>)arg0;
\r
491 if (this.data == null || other.data == null)
\r
493 if (!data.equals(other.data))
\r
500 * Merges given nodes to new node.
\r
501 * Does not copy hints!
\r
504 public GraphNode<T> merge(GraphNode<T> other, T mergedData) {
\r
506 GraphNode<T> mergedNode = new GraphNode<T>(mergedData);
\r
507 copyLinks(this, mergedNode);
\r
508 copyLinks(other, mergedNode);
\r
510 //mergedNode.setHints(other.getHints());
\r
511 //mergedNode.setHints(this.getHints());
\r
524 * Copies a link to other node.
\r
525 * @param from the node containing original link
\r
526 * @param to the node where link is copied to
\r
527 * @param l the link that is copied.
\r
528 * @return created link, if copy is successful. Otherwise returns null.
\r
530 public static <T> Link<T> copyLink(GraphNode<T> from, GraphNode<T> to, Link<T> l) {
\r
531 if (l.from() != from)
\r
532 throw new IllegalArgumentException("Link is not starting from " + from + " node.");
\r
535 if (to.containsLink(l.getName(), l.getInverseName(),l.to()))
\r
538 Link<T> newLink = to.addLink(l.getName(),l.getInverseName(),l.to());
\r
539 if (newLink != null) {
\r
540 newLink.setHints(l.getHints());
\r
541 newLink.getInverse().setHints(l.getInverse().getHints());
\r
547 Link<T> newLink = l.to().addLink(l.getInverseName(), l.getName(),to);
\r
548 if (newLink != null) {
\r
549 newLink.setHints(l.getInverse().getHints());
\r
550 newLink.getInverse().setHints(l.getHints());
\r
551 return newLink.getInverse();
\r
560 * Copies a link to other node, but inverts its direction.
\r
561 * @param from the node containing original link
\r
562 * @param to the node where link is copied to
\r
563 * @param l the link that is copied.
\r
564 * @return created link, if copy is successful. Otherwise returns null.
\r
566 public static <T> Link<T> copyLinkInverse(GraphNode<T> from, GraphNode<T> to, Link<T> l) {
\r
567 if (l.from() != from)
\r
568 throw new IllegalArgumentException("Link is not starting from " + from + " node.");
\r
571 if (to.containsLink(l.getInverseName(), l.getName(),l.to()))
\r
574 Link<T> newLink = l.to().addLink(l.getName(),l.getInverseName(),to);
\r
575 if (newLink != null) {
\r
576 newLink.setHints(l.getHints());
\r
577 newLink.getInverse().setHints(l.getInverse().getHints());
\r
583 Link<T> newLink = to.addLink(l.getInverseName(), l.getName(),l.to());
\r
584 if (newLink != null) {
\r
585 newLink.setHints(l.getInverse().getHints());
\r
586 newLink.getInverse().setHints(l.getHints());
\r
587 return newLink.getInverse();
\r
595 public static <T> void copyLinks(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {
\r
596 for (Link<T> l : links) {
\r
597 copyLink(from, to, l);
\r
602 public static <T> void copyLinks(GraphNode<T> from, GraphNode<T> to) {
\r
603 for (Link<T> l : from.getLinks()) {
\r
604 copyLink(from, to, l);
\r
609 * Copies a link to other node and removes the original link. If move is not successful, the original link is not removed.
\r
610 * @param from the node containing original link
\r
611 * @param to the node where link is copied to
\r
612 * @param l the link that is moved.
\r
613 * @return created link, if move is successful. Otherwise returns null.
\r
615 public static <T> Link<T> moveLink(GraphNode<T> from, GraphNode<T> to, Link<T> l) {
\r
616 Link<T> newLink = copyLink(from, to, l);
\r
617 if (newLink != null) {
\r
618 from.removeLink(l);
\r
624 public static <T> void moveLinks(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {
\r
625 for (Link<T> l : links) {
\r
626 Link<T> newLink = copyLink(from, to, l);
\r
627 if (newLink != null) {
\r
628 from.removeLink(l);
\r
633 public static <T> void moveLinkInverses(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {
\r
634 for (Link<T> l : links) {
\r
635 Link<T> newLink = copyLinkInverse(from, to, l);
\r
636 if (newLink != null) {
\r
637 from.removeLink(l);
\r
642 public static <T> void moveLinks(GraphNode<T> from, GraphNode<T> to) {
\r
643 for (Link<T> l : from.getLinks()) {
\r
644 Link<T> newLink = copyLink(from, to, l);
\r
645 if (newLink != null) {
\r
646 from.removeLink(l);
\r
652 * Replaces a link with a link going first to given node, and the to the original destination.
\r
653 * The link names and hints are copied to the created links.
\r
655 * Example: link is 'a' -> 'b'. With insert attribute 'c', the result is 'a' -> 'c' -> 'b'.
\r
659 * @return Array containing created links (2).
\r
661 @SuppressWarnings("unchecked")
\r
662 public static <T> Link<T>[] insert(Link<T> link, GraphNode<T> insert) {
\r
663 GraphNode<T> a = link.from();
\r
664 Link<T> c_b = moveLink(a, insert, link);
\r
665 Link<T> a_c = a.addLink(c_b, insert);
\r
666 return new Link[]{a_c,c_b};
\r
672 * Finds all nodes that have the same relations to the same nodes as this node.
\r
675 public Collection<GraphNode<T>> getFullSimilars() {
\r
677 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
678 Set<GraphNode<T>> potential = new HashSet<GraphNode<T>>();
\r
679 for (Link<T> link : nodes) {
\r
680 for (Link<T> link2 : link.to().nodes) {
\r
681 if (!link2.to().equals(this))
\r
682 potential.add(link2.to());
\r
685 for (GraphNode<T> node : potential) {
\r
686 if (node.nodes.containsAll(nodes))
\r
694 * Finds all nodes that have at least one similar relation to the same nodes as this node.
\r
697 public Collection<GraphNode<T>> getSemiSimilars() {
\r
699 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
700 Set<GraphNode<T>> potential = new HashSet<GraphNode<T>>();
\r
701 for (Link<T> link : nodes) {
\r
702 for (Link<T> link2 : link.to().nodes) {
\r
703 if (!link2.to().equals(this))
\r
704 potential.add(link2.to());
\r
707 for (GraphNode<T> node : potential) {
\r
708 for (Link<T> link : nodes) {
\r
709 if (node.nodes.contains(link))
\r
719 * Disposes this node. Removes all the links that connect to this node.
\r
721 public void destroy() {
\r
724 LOGGER.info("Node destroy " + data + " " + this);
\r
725 Collection<Link<T>> coll = new ArrayList<Link<T>>();
\r
726 coll.addAll(nodes);
\r
728 for (Link<T> link : coll) {
\r
729 link.to()._removeLink(this);
\r
736 * Removes this node from the graph, and reconnects parents and children of this node.
\r
738 public void remove() {
\r
741 // FIXME: link.to may be *this.
\r
742 LOGGER.info("Node remove " + data + " " + this);
\r
743 if (nodes.size() == 1) {
\r
744 Link<T> link = nodes.get(0);
\r
745 link.to().removeLink(link.getInverseName(), link.getName(), this);
\r
747 for (int i = 0; i < nodes.size() -1 ; i++) {
\r
748 Link<T> link1 = nodes.get(i);
\r
749 link1.to()._removeLink(link1.getInverseName(), link1.getName(), this);
\r
750 for (int j = i+1; j < nodes.size(); j++) {
\r
751 Link<T> link2 = nodes.get(j);
\r
752 link2.to()._removeLink(link2.getInverseName(), link2.getName(), this);
\r
753 if (link1.to().equals(link2.to()))
\r
755 link1.to().addLink(link1.getInverseName(),link2.getInverseName(),link2.to());
\r
769 * @return true if the node has been disposed and cannot be used anymore.
\r
771 public boolean isDisposed() {
\r
776 * Dispose the node and all nodes that are in the same graph.
\r
778 public void dispose() {
\r
781 Collection<Link<T>> links = new ArrayList<Link<T>>();
\r
782 links.addAll(nodes);
\r
784 for (Link<T> l : links)
\r
792 protected void _checkDisposed() {
\r
794 LOGGER.error("Remove Node, disposed " + this);
\r
795 throw new RuntimeException("Node " + this + " is disposed.");
\r
800 public int distanceTo(GraphNode<T> node, String rel, String inv) {
\r
801 if (node.equals(this))
\r
804 Set<GraphNode<T>> processed = new HashSet<GraphNode<T>>();
\r
805 Set<GraphNode<T>> next = new HashSet<GraphNode<T>>();
\r
808 if (next.size() == 0)
\r
811 processed.addAll(next);
\r
812 Set<GraphNode<T>> nextNext = new HashSet<GraphNode<T>>();
\r
813 for (GraphNode<T> n : next) {
\r
814 for (GraphNode<T> nn : n.getNodes(rel, inv))
\r
815 if (!processed.contains(nn))
\r
819 if (next.contains(node))
\r
824 public String toString() {
\r
825 return "Node : " + data;
\r