/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.graph.dhns.graph;
import org.gephi.graph.api.Edge;
import org.gephi.graph.api.HierarchicalGraph;
import org.gephi.graph.api.ImmutableTreeNode;
import org.gephi.graph.api.Node;
import org.gephi.graph.api.NodeIterable;
import org.gephi.graph.dhns.core.Dhns;
import org.gephi.graph.dhns.core.GraphViewImpl;
import org.gephi.graph.dhns.edge.AbstractEdge;
import org.gephi.graph.dhns.node.AbstractNode;
import org.gephi.graph.dhns.node.iterators.ChildrenIterator;
import org.gephi.graph.dhns.node.iterators.DescendantIterator;
import org.gephi.graph.dhns.node.iterators.LevelIterator;
import org.gephi.graph.dhns.node.iterators.TreeIterator;
import org.gephi.graph.dhns.predicate.Predicate;
import org.gephi.graph.dhns.predicate.Tautology;
import org.gephi.graph.dhns.utils.TreeNodeWrapper;
/**
*
* @author Mathieu Bastian
*/
public abstract class HierarchicalGraphImpl extends AbstractGraphImpl implements HierarchicalGraph {
protected Predicate<AbstractNode> enabledNodePredicate;
public HierarchicalGraphImpl(Dhns dhns, GraphViewImpl view) {
super(dhns, view);
enabledNodePredicate = new Predicate<AbstractNode>() {
public boolean evaluate(AbstractNode element) {
return element.isEnabled();
}
};
}
public abstract HierarchicalGraphImpl copy(Dhns dhns, GraphViewImpl view);
public boolean addNode(Node node, Node parent) {
if (node == null) {
throw new IllegalArgumentException("Node can't be null");
}
AbstractNode absNode = (AbstractNode) node;
AbstractNode absParent = null;
if (parent != null) {
absParent = checkNode(parent);
}
if (absNode.isValid(view.getViewId())) {
return false;
}
if (absNode.avlNode != null) { //exist in another view
if (absNode.getInView(view.getViewId()) != null) {
return false;
}
absNode = new AbstractNode(absNode.getNodeData(), view.getViewId());
}
if (!absNode.getNodeData().hasAttributes()) {
absNode.getNodeData().setAttributes(dhns.factory().newNodeAttributes(absNode.getNodeData()));
}
view.getStructureModifier().addNode(absNode, absParent);
return true;
}
public boolean addNode(Node node) {
return addNode(node, null);
}
public boolean contains(Node node) {
if (node == null) {
throw new NullPointerException();
}
AbstractNode absNode = (AbstractNode) node;
boolean res = false;
if (absNode.isValid(view.getViewId())) {
res = structure.getTree().contains(absNode);
} else if ((absNode = absNode.getInView(view.getViewId())) != null) {
res = true;
}
return res;
}
public Node getNode(int id) {
return dhns.getGraphStructure().getNodeFromDictionnary(id, view.getViewId());
}
public Edge getEdge(int id) {
return dhns.getGraphStructure().getEdgeFromDictionnary(id);
}
public Node getNode(String id) {
if (id == null) {
throw new NullPointerException();
}
return dhns.getGraphStructure().getNodeFromDictionnary(id, view.getViewId());
}
public Edge getEdge(String id) {
if (id == null) {
throw new NullPointerException();
}
return dhns.getGraphStructure().getEdgeFromDictionnary(id);
}
public NodeIterable getNodes() {
readLock();
return dhns.newNodeIterable(new TreeIterator(structure, true, Tautology.instance));
}
public NodeIterable getNodesTree() {
readLock();
return dhns.newNodeIterable(new TreeIterator(structure, false, Tautology.instance));
}
public int getNodeCount() {
//int count = structure.getTreeSize() - 1;// -1 Exclude virtual root
int count = view.getNodesEnabled();
return count;
}
public NodeIterable getNodes(int level) {
level += 1; //Because we ignore the virtual root
readLock();
int height = structure.getTreeHeight();
if (level > height) {
readUnlock();
throw new IllegalArgumentException("Level must be between 0 and the height of the tree, currently height=" + (height - 1));
}
return dhns.newNodeIterable(new LevelIterator(structure, level, Tautology.instance));
}
public int getLevelSize(int level) {
level += 1; //Because we ignore the virtual root
int height = structure.getTreeHeight();
if (level > height) {
throw new IllegalArgumentException("Level must be between 0 and the height of the tree, currently height=" + (height - 1));
}
int res = structure.getLevelSize(level);
return res;
}
public boolean isSelfLoop(Edge edge) {
AbstractEdge absEdge = checkEdge(edge);
return absEdge.getSource(view.getViewId()) == absEdge.getTarget(view.getViewId());
}
public boolean isAdjacent(Edge edge1, Edge edge2) {
if (edge1 == edge2) {
throw new IllegalArgumentException("Edges can't be the same");
}
AbstractEdge absEdge1 = checkEdge(edge1);
AbstractEdge absEdge2 = checkEdge(edge2);
return absEdge1.getSource(view.getViewId()) == absEdge2.getSource(view.getViewId())
|| absEdge1.getSource(view.getViewId()) == absEdge2.getTarget(view.getViewId())
|| absEdge1.getTarget(view.getViewId()) == absEdge2.getSource(view.getViewId())
|| absEdge1.getTarget(view.getViewId()) == absEdge2.getTarget(view.getViewId());
}
public Node getOpposite(Node node, Edge edge) {
checkNode(node);
AbstractEdge absEdge = checkEdge(edge);
if (absEdge.getSource(view.getViewId()) == node) {
return absEdge.getTarget(view.getViewId());
} else if (absEdge.getTarget(view.getViewId()) == node) {
return absEdge.getSource(view.getViewId());
}
throw new IllegalArgumentException("Node must be either source or target of the edge.");
}
public boolean removeNode(Node node) {
AbstractNode absNode = checkNode(node);
view.getStructureModifier().deleteNode(absNode);
return true;
}
public void clear() {
view.getStructureModifier().clear();
}
public void clearEdges() {
view.getStructureModifier().clearEdges();
}
public void clearEdges(Node node) {
AbstractNode absNode = checkNode(node);
view.getStructureModifier().clearEdges(absNode);
}
public void clearMetaEdges(Node node) {
AbstractNode absNode = checkNode(node);
view.getStructureModifier().clearMetaEdges(absNode);
}
public void setId(Node node, String id) {
if (node == null) {
throw new NullPointerException("node can't be null");
}
dhns.getGraphStructure().setNodeId(((AbstractNode) node).getNodeData(), id);
}
public void setId(Edge edge, String id) {
if (edge == null) {
throw new NullPointerException("edge can't be null");
}
dhns.getGraphStructure().setEdgeId((AbstractEdge) edge, id);
}
public ImmutableTreeNode wrapToTreeNode() {
TreeNodeWrapper wrapper = new TreeNodeWrapper(structure);
ImmutableTreeNode treeNode;
readLock();
treeNode = wrapper.wrap(new TreeIterator(structure, false, Tautology.instance));
readUnlock();
return treeNode;
}
public int getChildrenCount(Node node) {
AbstractNode absNode = checkNode(node);
int count = 0;
ChildrenIterator itr = new ChildrenIterator(structure, absNode, Tautology.instance);
for (; itr.hasNext();) {
itr.next();
count++;
}
return count;
}
public int getDescendantCount(Node node) {
AbstractNode absNode = checkNode(node);
return absNode.size;
}
public Node getParent(Node node) {
AbstractNode absNode = checkNode(node);
Node parent = null;
if (absNode.parent != structure.getRoot()) {
parent = absNode.parent;
}
return parent;
}
public NodeIterable getChildren(Node node) {
readLock();
AbstractNode absNode = checkNode(node);
return dhns.newNodeIterable(new ChildrenIterator(structure, absNode, Tautology.instance));
}
public NodeIterable getDescendant(Node node) {
readLock();
AbstractNode absNode = checkNode(node);
return dhns.newNodeIterable(new DescendantIterator(structure, absNode, Tautology.instance));
}
public NodeIterable getTopNodes() {
readLock();
return dhns.newNodeIterable(new ChildrenIterator(structure, Tautology.instance));
}
public boolean isDescendant(Node node, Node descendant) {
AbstractNode absNode = checkNode(node);
AbstractNode absDesc = checkNode(descendant);
boolean res = false;
res = absDesc.getPre() > absNode.getPre() && absDesc.getPost() < absNode.getPost();
return res;
}
public boolean isAncestor(Node node, Node ancestor) {
return isDescendant(ancestor, node);
}
public boolean isFollowing(Node node, Node following) {
AbstractNode absNode = checkNode(node);
AbstractNode absFoll = checkNode(following);
boolean res = absFoll.getPre() > absNode.getPre() && absFoll.getPost() > absNode.getPost();
return res;
}
public boolean isPreceding(Node node, Node preceding) {
return isFollowing(preceding, node);
}
public boolean isParent(Node node, Node parent) {
AbstractNode absNode = checkNode(node);
AbstractNode absParent = checkNode(parent);
boolean res = absNode.parent == absParent;
return res;
}
public int getHeight() {
int res = structure.getTreeHeight() - 1;
return res;
}
public int getLevel(Node node) {
AbstractNode absNode = checkNode(node);
int res = absNode.level - 1;
return res;
}
public void moveToGroup(Node node, Node nodeGroup) {
AbstractNode absNode = checkNode(node);
AbstractNode absGroup = checkNode(nodeGroup);
if (isDescendant(absNode, absGroup)) {
throw new IllegalArgumentException("nodeGroup can't be a descendant of node");
}
view.getStructureModifier().moveToGroup(absNode, absGroup);
}
public void removeFromGroup(Node node) {
AbstractNode absNode = checkNode(node);
if (absNode.parent.parent == null) { //Equal root
throw new IllegalArgumentException("Node parent can't be the root of the tree");
}
view.getStructureModifier().moveToGroup(absNode, absNode.parent.parent);
}
public Node groupNodes(Node[] nodes) {
if (nodes == null || nodes.length == 0) {
throw new IllegalArgumentException("nodes can't be null or empty");
}
AbstractNode[] absNodes = new AbstractNode[nodes.length];
AbstractNode parent = null;
for (int i = 0; i < nodes.length; i++) {
AbstractNode node = checkNode(nodes[i]);
absNodes[i] = node;
if (parent == null) {
parent = node.parent;
} else if (parent != node.parent) {
throw new IllegalArgumentException("All nodes must have the same parent");
}
}
Node group = view.getStructureModifier().group(absNodes);
return group;
}
public void ungroupNodes(Node nodeGroup) {
AbstractNode absNode = checkNode(nodeGroup);
if (absNode.size == 0) {
throw new IllegalArgumentException("nodeGroup can't be empty");
}
view.getStructureModifier().ungroup(absNode);
}
public boolean expand(Node node) {
AbstractNode absNode = checkNode(node);
if (absNode.size == 0 || !absNode.isEnabled()) {
return false;
}
view.getStructureModifier().expand(absNode);
return true;
}
public boolean retract(Node node) {
AbstractNode absNode = checkNode(node);
if (absNode.size == 0 || absNode.isEnabled()) {
return false;
}
view.getStructureModifier().retract(absNode);
return true;
}
public boolean isInView(Node node) {
AbstractNode absNode = checkNode(node);
boolean res = absNode.isEnabled();
return res;
}
public void resetViewToLeaves() {
view.getStructureModifier().resetViewToLeaves();
}
public void resetViewToLevel(int level) {
readLock();
level += 1; //Because we ignore the virtual root
int height = structure.getTreeHeight();
if (level > height) {
readUnlock();
throw new IllegalArgumentException("Level must be between 0 and the height of the tree, currently height=" + (height - 1));
}
readUnlock();
view.getStructureModifier().resetViewToLevel(level);
}
public void resetViewToTopNodes() {
view.getStructureModifier().resetViewToTopNodes();
}
public void flatten() {
view.getStructureModifier().flatten();
}
}