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.interop.mapping.Logger;
\r
10 import org.simantics.utils.datastructures.hints.HintContext;
\r
16 * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
\r
20 public class GraphNode<T> extends HintContext {
\r
22 private List<Link<T>> nodes = new ArrayList<Link<T>>();
\r
24 private boolean disposed = false;
\r
26 public GraphNode(T data) {
\r
34 public T getData() {
\r
40 public void setHint(Key key, Object value) {
\r
44 super.setHint(key, value);
\r
51 * Adds link to the other node.
\r
52 * @param relationName name of the link from this node to the other node.
\r
53 * @param inverseRelationName nam,e of the link from the other node to this node.
\r
54 * @param node the other node.
\r
55 * @return the created link. Returns null if the link cannot be created.
\r
57 public Link<T> addLink(String relationName, String inverseRelationName,GraphNode<T> node) {
\r
59 Logger.defaultLogInfo("Node link " + data + " " + node.data + " " + relationName + " " + inverseRelationName +"\n");
\r
60 if(containsLink(relationName, node) && node.containsLink(inverseRelationName, this)) {
\r
61 Logger.defaultLogWarning("Node " + getData() + " has already given child " + node.getData() + " ,with name " + relationName + " / " + inverseRelationName);
\r
64 Link<T> rel = _addLink(relationName,node);
\r
65 Link<T> inv = node._addLink(inverseRelationName,this);
\r
66 rel.setInverseLink(inv);
\r
67 inv.setMainLink(rel);
\r
72 * Adds link to the other node, with <pre>null</pre> link name from the other node to this node.
\r
73 * @param relationName
\r
74 * @param node name of the link from this node to the other node
\r
75 * @return the created link. Returns null if the link cannot be created.
\r
77 public Link<T> addLink(String relationName,GraphNode<T> node) {
\r
79 if(containsLink(relationName, node)) {
\r
80 Logger.defaultLogWarning("Node " + getData() + " has already given child " + node.getData() + " ,with name " + relationName );
\r
83 Link<T> rel = _addLink(relationName,node);
\r
84 Link<T> inv = node._addLink(null,this);
\r
85 rel.setInverseLink(inv);
\r
86 inv.setMainLink(rel);
\r
91 * Adds link to the other node, with copying link properties from given link.
\r
93 * @param node name of the link from this node to the other node
\r
94 * @return the created link. Returns null if the link cannot be created.
\r
96 public Link<T> addLink(Link<T> link ,GraphNode<T> node) {
\r
98 if(containsLink(link.getName(), node) && node.containsLink(link.getInverseName(), this)) {
\r
99 Logger.defaultLogWarning("Node " + getData() + " has already given child " + node.getData() + " ,with name " + link.getName() + " / " + link.getInverseName());
\r
102 Link<T> rel = _addLink(link.getName(),node);
\r
103 Link<T> inv = node._addLink(link.getInverseName(),this);
\r
104 rel.setInverseLink(inv);
\r
105 inv.setMainLink(rel);
\r
106 rel.setHints(link.getHints());
\r
107 inv.setHints(link.getInverse().getHints());
\r
111 protected Link<T> _addLink(String relation, GraphNode<T> node) {
\r
112 Link<T> link = new Link<T>(this, relation,node);
\r
117 protected Link<T> _removeLink(String relation,String inverse, GraphNode<T> node) {
\r
118 for (int i = 0; i < nodes.size(); i++) {
\r
119 Link<T> link = nodes.get(i);
\r
120 if (node.equals(link.to()) && equals(relation,link.getName()) && equals(inverse,link.getInverseName())) {
\r
121 return nodes.remove(i);
\r
127 protected Link<T> _removeLink(String relation) {
\r
128 for (int i = 0; i < nodes.size(); i++) {
\r
129 Link<T> link = nodes.get(i);
\r
130 if (equals(relation,link.getName())) {
\r
131 return nodes.remove(i);
\r
137 protected Link<T> _removeLink(String relation, String inverse) {
\r
138 for (int i = 0; i < nodes.size(); i++) {
\r
139 Link<T> link = nodes.get(i);
\r
140 if (equals(relation,link.getName()) && equals(inverse,link.getInverseName())) {
\r
141 return nodes.remove(i);
\r
147 protected Link<T> _removeLink(String relation, GraphNode<T> node) {
\r
148 for (int i = 0; i < nodes.size(); i++) {
\r
149 Link<T> link = nodes.get(i);
\r
150 if (node.equals(link.to()) && equals(relation,link.getName())) {
\r
151 return nodes.remove(i);
\r
157 protected Link<T> _removeLink(GraphNode<T> node) {
\r
158 for (int i = 0; i < nodes.size(); i++ ) {
\r
159 Link<T> link = nodes.get(i);
\r
160 if (node.equals(link.to())) {
\r
161 return nodes.remove(i);
\r
167 protected boolean _removeLink(Link<T> link) {
\r
168 return nodes.remove(link);
\r
172 * Removes the given link.
\r
174 * @return true if the link was removed.
\r
176 public boolean removeLink(Link<T> link) {
\r
178 if (_removeLink(link)) {
\r
179 if (link.hasInverse()) {
\r
180 link.to()._removeLink(link.getInverse());
\r
188 * Removes a link (or multiple links).
\r
189 * @param relation the name of the link from this node.
\r
190 * @return true if a link was removed.
\r
192 public boolean removeLink(String relation) {
\r
194 boolean deleted = false;
\r
196 Link<T> removed = _removeLink(relation);
\r
197 if (removed == null)
\r
199 if (removed.hasInverse()) {
\r
200 removed.to()._removeLink(removed.getInverse());
\r
208 * Removes a link (or multiple links).
\r
209 * @param relation the name of the link from this node.
\r
210 * @return true if the link was removed.
\r
212 public boolean removeLink(String relation, String inverse) {
\r
214 boolean deleted = false;
\r
216 Link<T> removed = _removeLink(relation, inverse);
\r
217 if (removed == null)
\r
219 if (removed.hasInverse()) {
\r
220 removed.to()._removeLink(removed.getInverse());
\r
229 * @param relation the name of the link from this node to the other node.
\r
230 * @param node the other node.
\r
231 * @return true if the link was removed.
\r
233 public boolean removeLink(String relation, GraphNode<T> node) {
\r
236 boolean deleted = false;
\r
238 Link<T> removed = _removeLink(relation, node);
\r
239 if (removed == null)
\r
241 if (removed.hasInverse()) {
\r
242 removed.to()._removeLink(removed.getInverse());
\r
250 * Removes all links from this node to the other node.
\r
251 * @param node the other node.
\r
254 public boolean removeLink(GraphNode<T> node) {
\r
256 boolean deleted = false;
\r
258 Link<T> removed = _removeLink(node);
\r
259 if (removed == null)
\r
261 if (removed.hasInverse()) {
\r
262 removed.to()._removeLink(removed.getInverse());
\r
271 * @param relation the name of the link from this node to the other node.
\r
272 * @param inverse the name of the link from the other node to this node.
\r
273 * @param node the other node.
\r
274 * @return true if the link was removed.
\r
276 public boolean removeLink(String relation, String inverse, GraphNode<T> node) {
\r
278 Link<T> removed = _removeLink(relation, inverse, node);
\r
279 if (removed != null && removed.hasInverse()) {
\r
280 removed.to()._removeLink(removed.getInverse());
\r
286 public boolean containsLink(GraphNode<T> node) {
\r
288 for (Link<T> link : nodes) {
\r
289 if (node.equals(link.to()))
\r
295 public boolean containsLink(String relationName, GraphNode<T> node) {
\r
297 if (relationName == null)
\r
299 for (Link<T> link : nodes) {
\r
300 if (node.equals(link.to()) && equals(relationName,link.getName()))
\r
306 public boolean containsLink(String relationName, String inverseName, GraphNode<T> node) {
\r
308 for (Link<T> link : nodes) {
\r
309 if (node.equals(link.to()) && equals(relationName,link.getName()) && equals(inverseName,link.getInverseName()))
\r
315 public Collection<Link<T>> getLinks() {
\r
317 Collection<Link<T>> coll = new ArrayList<Link<T>>(nodes);
\r
321 public Collection<GraphNode<T>> getNodes(String rel) {
\r
323 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
325 for (Link<T> link : nodes) {
\r
326 if (equals(rel,link.getName()))
\r
327 result.add(link.to());
\r
333 public Collection<Link<T>> getLinks(String rel) {
\r
335 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
337 for (Link<T> link : nodes) {
\r
338 if (equals(rel,link.getName()))
\r
345 public Collection<Link<T>> getLinks(String rel, GraphNode<T> node) {
\r
347 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
349 for (Link<T> link : nodes) {
\r
350 if (equals(rel,link.getName()) && link.to().equals(node))
\r
357 public Collection<Link<T>> getLinks(String rel, String inverse) {
\r
359 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
361 for (Link<T> link : nodes) {
\r
362 if (equals(rel,link.getName()) && equals(inverse, link.getInverseName()))
\r
369 public Collection<Link<T>> getLinks(String rel, String inverse, GraphNode<T> node) {
\r
371 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
373 for (Link<T> link : nodes) {
\r
374 if (equals(rel,link.getName()) && equals(inverse, link.getInverseName()) && link.to().equals(node))
\r
381 public Collection<GraphNode<T>> getNodesWithInv(String inv) {
\r
383 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
385 for (Link<T> link : nodes) {
\r
386 if (equals(inv,link.getInverseName()))
\r
387 result.add(link.to());
\r
393 public Collection<GraphNode<T>> getNodes(String rel, String inv) {
\r
395 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
397 for (Link<T> link : nodes) {
\r
398 if (equals(rel,link.getName()) && equals(inv,link.getInverseName()))
\r
399 result.add(link.to());
\r
405 public static boolean equals(String s1, String s2) {
\r
407 return s1.equals(s2);
\r
409 return s2.equals(s1);
\r
410 return true; // both null
\r
415 * Returns links from this node to the given node.
\r
419 public Collection<Link<T>> getLinks(GraphNode<T> node) {
\r
421 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
422 for (Link<T> link : nodes) {
\r
423 if (link.to().equals(node))
\r
430 * Returns names of links from this node to the given node. Does not return null names.
\r
434 public Collection<String> getRelations(GraphNode<T> node) {
\r
436 Collection<String> result = new ArrayList<String>();
\r
437 for (Link<T> link : nodes) {
\r
438 if (link.to().equals(node) && link.getName() != null)
\r
439 result.add(link.getName());
\r
445 * Returns links from given node to this node.
\r
449 public Collection<Link<T>> getInverseLinks(GraphNode<T> node) {
\r
451 Collection<Link<T>> result = new ArrayList<Link<T>>();
\r
452 for (Link<T> link : nodes) {
\r
453 if (link.to().equals(node))
\r
454 result.add(link.getInverse());
\r
460 * Returns names of links from given node to this node. Does not return null names.
\r
464 public Collection<String> getInverses(GraphNode<T> node) {
\r
466 Collection<String> result = new ArrayList<String>();
\r
467 for (Link<T> link : nodes) {
\r
468 if (link.to().equals(node) && link.getInverseName() != null)
\r
469 result.add(link.getInverseName());
\r
477 public int hashCode() {
\r
478 return data.hashCode();
\r
482 public boolean equals(Object arg0) {
\r
483 if (!arg0.getClass().equals(getClass()))
\r
485 GraphNode<?> other = (GraphNode<?>)arg0;
\r
488 if (this.data == null || other.data == null)
\r
490 if (!data.equals(other.data))
\r
497 * Merges given nodes to new node.
\r
498 * Does not copy hints!
\r
501 public GraphNode<T> merge(GraphNode<T> other, T mergedData) {
\r
503 GraphNode<T> mergedNode = new GraphNode<T>(mergedData);
\r
504 copyLinks(this, mergedNode);
\r
505 copyLinks(other, mergedNode);
\r
507 //mergedNode.setHints(other.getHints());
\r
508 //mergedNode.setHints(this.getHints());
\r
521 * Copies a link to other node.
\r
522 * @param from the node containing original link
\r
523 * @param to the node where link is copied to
\r
524 * @param l the link that is copied.
\r
525 * @return created link, if copy is successful. Otherwise returns null.
\r
527 public static <T> Link<T> copyLink(GraphNode<T> from, GraphNode<T> to, Link<T> l) {
\r
528 if (l.from() != from)
\r
529 throw new IllegalArgumentException("Link is not starting from " + from + " node.");
\r
532 if (to.containsLink(l.getName(), l.getInverseName(),l.to()))
\r
535 Link<T> newLink = to.addLink(l.getName(),l.getInverseName(),l.to());
\r
536 if (newLink != null) {
\r
537 newLink.setHints(l.getHints());
\r
538 newLink.getInverse().setHints(l.getInverse().getHints());
\r
544 Link<T> newLink = l.to().addLink(l.getInverseName(), l.getName(),to);
\r
545 if (newLink != null) {
\r
546 newLink.setHints(l.getInverse().getHints());
\r
547 newLink.getInverse().setHints(l.getHints());
\r
548 return newLink.getInverse();
\r
557 * Copies a link to other node, but inverts its direction.
\r
558 * @param from the node containing original link
\r
559 * @param to the node where link is copied to
\r
560 * @param l the link that is copied.
\r
561 * @return created link, if copy is successful. Otherwise returns null.
\r
563 public static <T> Link<T> copyLinkInverse(GraphNode<T> from, GraphNode<T> to, Link<T> l) {
\r
564 if (l.from() != from)
\r
565 throw new IllegalArgumentException("Link is not starting from " + from + " node.");
\r
568 if (to.containsLink(l.getInverseName(), l.getName(),l.to()))
\r
571 Link<T> newLink = l.to().addLink(l.getName(),l.getInverseName(),to);
\r
572 if (newLink != null) {
\r
573 newLink.setHints(l.getHints());
\r
574 newLink.getInverse().setHints(l.getInverse().getHints());
\r
580 Link<T> newLink = to.addLink(l.getInverseName(), l.getName(),l.to());
\r
581 if (newLink != null) {
\r
582 newLink.setHints(l.getInverse().getHints());
\r
583 newLink.getInverse().setHints(l.getHints());
\r
584 return newLink.getInverse();
\r
592 public static <T> void copyLinks(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {
\r
593 for (Link<T> l : links) {
\r
594 copyLink(from, to, l);
\r
599 public static <T> void copyLinks(GraphNode<T> from, GraphNode<T> to) {
\r
600 for (Link<T> l : from.getLinks()) {
\r
601 copyLink(from, to, l);
\r
606 * Copies a link to other node and removes the original link. If move is not successful, the original link is not removed.
\r
607 * @param from the node containing original link
\r
608 * @param to the node where link is copied to
\r
609 * @param l the link that is moved.
\r
610 * @return created link, if move is successful. Otherwise returns null.
\r
612 public static <T> Link<T> moveLink(GraphNode<T> from, GraphNode<T> to, Link<T> l) {
\r
613 Link<T> newLink = copyLink(from, to, l);
\r
614 if (newLink != null) {
\r
615 from.removeLink(l);
\r
621 public static <T> void moveLinks(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {
\r
622 for (Link<T> l : links) {
\r
623 Link<T> newLink = copyLink(from, to, l);
\r
624 if (newLink != null) {
\r
625 from.removeLink(l);
\r
630 public static <T> void moveLinkInverses(GraphNode<T> from, GraphNode<T> to, Collection<Link<T>> links) {
\r
631 for (Link<T> l : links) {
\r
632 Link<T> newLink = copyLinkInverse(from, to, l);
\r
633 if (newLink != null) {
\r
634 from.removeLink(l);
\r
639 public static <T> void moveLinks(GraphNode<T> from, GraphNode<T> to) {
\r
640 for (Link<T> l : from.getLinks()) {
\r
641 Link<T> newLink = copyLink(from, to, l);
\r
642 if (newLink != null) {
\r
643 from.removeLink(l);
\r
649 * Replaces a link with a link going first to given node, and the to the original destination.
\r
650 * The link names and hints are copied to the created links.
\r
652 * Example: link is 'a' -> 'b'. With insert attribute 'c', the result is 'a' -> 'c' -> 'b'.
\r
656 * @return Array containing created links (2).
\r
658 @SuppressWarnings("unchecked")
\r
659 public static <T> Link<T>[] insert(Link<T> link, GraphNode<T> insert) {
\r
660 GraphNode<T> a = link.from();
\r
661 Link<T> c_b = moveLink(a, insert, link);
\r
662 Link<T> a_c = a.addLink(c_b, insert);
\r
663 return new Link[]{a_c,c_b};
\r
669 * Finds all nodes that have the same relations to the same nodes as this node.
\r
672 public Collection<GraphNode<T>> getFullSimilars() {
\r
674 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
675 Set<GraphNode<T>> potential = new HashSet<GraphNode<T>>();
\r
676 for (Link<T> link : nodes) {
\r
677 for (Link<T> link2 : link.to().nodes) {
\r
678 if (!link2.to().equals(this))
\r
679 potential.add(link2.to());
\r
682 for (GraphNode<T> node : potential) {
\r
683 if (node.nodes.containsAll(nodes))
\r
691 * Finds all nodes that have at least one similar relation to the same nodes as this node.
\r
694 public Collection<GraphNode<T>> getSemiSimilars() {
\r
696 Collection<GraphNode<T>> result = new ArrayList<GraphNode<T>>();
\r
697 Set<GraphNode<T>> potential = new HashSet<GraphNode<T>>();
\r
698 for (Link<T> link : nodes) {
\r
699 for (Link<T> link2 : link.to().nodes) {
\r
700 if (!link2.to().equals(this))
\r
701 potential.add(link2.to());
\r
704 for (GraphNode<T> node : potential) {
\r
705 for (Link<T> link : nodes) {
\r
706 if (node.nodes.contains(link))
\r
716 * Disposes this node. Removes all the links that connect to this node.
\r
718 public void destroy() {
\r
721 Logger.defaultLogInfo("Node destroy " + data + " " + this);
\r
722 Collection<Link<T>> coll = new ArrayList<Link<T>>();
\r
723 coll.addAll(nodes);
\r
725 for (Link<T> link : coll) {
\r
726 link.to()._removeLink(this);
\r
733 * Removes this node from the graph, and reconnects parents and children of this node.
\r
735 public void remove() {
\r
738 // FIXME: link.to may be *this.
\r
739 Logger.defaultLogInfo("Node remove " + data + " " + this);
\r
740 if (nodes.size() == 1) {
\r
741 Link<T> link = nodes.get(0);
\r
742 link.to().removeLink(link.getInverseName(), link.getName(), this);
\r
744 for (int i = 0; i < nodes.size() -1 ; i++) {
\r
745 Link<T> link1 = nodes.get(i);
\r
746 link1.to()._removeLink(link1.getInverseName(), link1.getName(), this);
\r
747 for (int j = i+1; j < nodes.size(); j++) {
\r
748 Link<T> link2 = nodes.get(j);
\r
749 link2.to()._removeLink(link2.getInverseName(), link2.getName(), this);
\r
750 if (link1.to().equals(link2.to()))
\r
752 link1.to().addLink(link1.getInverseName(),link2.getInverseName(),link2.to());
\r
766 * @return true if the node has been disposed and cannot be used anymore.
\r
768 public boolean isDisposed() {
\r
773 * Dispose the node and all nodes that are in the same graph.
\r
775 public void dispose() {
\r
778 Collection<Link<T>> links = new ArrayList<Link<T>>();
\r
779 links.addAll(nodes);
\r
781 for (Link<T> l : links)
\r
789 protected void _checkDisposed() {
\r
791 Logger.defaultLogError("Remove Node, disposed " + this);
\r
792 throw new RuntimeException("Node " + this + " is disposed.");
\r
797 public int distanceTo(GraphNode<T> node, String rel, String inv) {
\r
798 if (node.equals(this))
\r
801 Set<GraphNode<T>> processed = new HashSet<GraphNode<T>>();
\r
802 Set<GraphNode<T>> next = new HashSet<GraphNode<T>>();
\r
805 if (next.size() == 0)
\r
808 processed.addAll(next);
\r
809 Set<GraphNode<T>> nextNext = new HashSet<GraphNode<T>>();
\r
810 for (GraphNode<T> n : next) {
\r
811 for (GraphNode<T> nn : n.getNodes(rel, inv))
\r
812 if (!processed.contains(nn))
\r
816 if (next.contains(node))
\r
821 public String toString() {
\r
822 return "Node : " + data;
\r