/* Copyright 2008-2010 Gephi Authors : Eduardo Ramos <eduramiba@gmail.com> 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.datalab.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.gephi.data.attributes.api.AttributeOrigin; import org.gephi.data.attributes.api.AttributeRow; import org.gephi.datalab.api.GraphElementsController; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphController; import org.gephi.graph.api.HierarchicalGraph; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeData; import org.gephi.graph.api.UndirectedGraph; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; /** * Implementation of the GraphElementsController interface * declared in the Data Laboratory API * @author Eduardo Ramos <eduramiba@gmail.com> * @see GraphElementsController */ @ServiceProvider(service = GraphElementsController.class) public class GraphElementsControllerImpl implements GraphElementsController { private static final float DEFAULT_NODE_SIZE=10f; private static final float DEFAULT_EDGE_WEIGHT=1f; public Node createNode(String label) { Node newNode = buildNode(label); getGraph().addNode(newNode); return newNode; } public Node createNode(String label, String id) { Graph graph = getGraph(); if (graph.getNode(id) == null) { Node newNode = buildNode(label, id); graph.addNode(newNode); return newNode; } else { return null; } } public Node duplicateNode(Node node) { if (isNodeInGraph(node)) { HierarchicalGraph hg = getHierarchicalGraph(); Node copy = copyNodeRecursively(node, hg.getParent(node), hg);//Add copy to the same level as the original node return copy; } else { return null; } } public void duplicateNodes(Node[] nodes) { for (Node n : nodes) { duplicateNode(n); } } public Edge createEdge(Node source, Node target, boolean directed) { Edge newEdge; if (directed) { newEdge = buildEdge(source, target, true); if (getDirectedGraph().addEdge(newEdge)) {//The edge will be created if it does not already exist. return newEdge; } else { return null; } } else { newEdge = buildEdge(source, target, false); if (getUndirectedGraph().addEdge(newEdge)) {//The edge will be created if it does not already exist. return newEdge; } else { return null; } } } public Edge createEdge(String id, Node source, Node target, boolean directed) { Edge newEdge; if (source != target) {//Cannot create self-loop if (directed) { newEdge = buildEdge(id, source, target, true); if (getDirectedGraph().addEdge(newEdge)) {//The edge will be created if it does not already exist. return newEdge; } else { return null; } } else { newEdge = buildEdge(id, source, target, false); if (getUndirectedGraph().addEdge(newEdge)) {//The edge will be created if it does not already exist. return newEdge; } else { return null; } } } else { return null; } } public void createEdges(Node source, Node[] allNodes, boolean directed) { if (isNodeInGraph(source) && areNodesInGraph(allNodes)) { for (Node n : allNodes) { if (n != source) { createEdge(source, n, directed); } } } } public void deleteNode(Node node) { removeNode(node, getGraph()); } public void deleteNodes(Node[] nodes) { Graph graph = getGraph(); for (Node node : nodes) { removeNode(node, graph); } } public void deleteEdge(Edge edge) { removeEdge(edge, getGraph()); } public void deleteEdges(Edge[] edges) { Graph graph = getGraph(); for (Edge edge : edges) { removeEdge(edge, graph); } } public void deleteEdgeWithNodes(Edge edge, boolean deleteSource, boolean deleteTarget) { if (deleteSource) { deleteNode(edge.getSource()); } if (deleteTarget) { deleteNode(edge.getTarget()); } removeEdge(edge, getGraph());//If no node is deleted, we need to remove the edge. } public void deleteEdgesWithNodes(Edge[] edges, boolean deleteSource, boolean deleteTarget) { for (Edge edge : edges) { deleteEdgeWithNodes(edge, deleteSource, deleteTarget); } } public boolean groupNodes(Node[] nodes) { if (canGroupNodes(nodes)) { HierarchicalGraph hg = getHierarchicalGraph(); Node group = hg.groupNodes(nodes); //Set the group node label to the same used int visualization module: group.getNodeData().setLabel(NbBundle.getMessage(GraphElementsControllerImpl.class, "Group.nodeCount.label", getNodeChildrenCount(hg, group))); return true; } else { return false; } } public boolean canGroupNodes(Node[] nodes) { if (!areNodesInGraph(nodes)) { return false; } HierarchicalGraph hg = getHierarchicalGraph(); Node parent = hg.getParent(nodes[0]); for (Node n : nodes) { if (hg.getParent(n) != parent) { return false; } } return true; } public boolean ungroupNode(Node node) { if (canUngroupNode(node)) { HierarchicalGraph hg = getHierarchicalGraph(); hg.ungroupNodes(node); return true; } else { return false; } } public void ungroupNodes(Node[] nodes) { for (Node n : nodes) { ungroupNode(n); } } public boolean ungroupNodeRecursively(Node node) { if (canUngroupNode(node)) { HierarchicalGraph hg = getHierarchicalGraph(); //We can get directly all descendant nodes withoud using recursion and break the groups: ungroupNodes(hg.getDescendant(node).toArray()); ungroupNode(node); return true; } else { return false; } } public void ungroupNodesRecursively(Node[] nodes) { for (Node n : nodes) { ungroupNodeRecursively(n); } } public boolean canUngroupNode(Node node) { if (!isNodeInGraph(node)) { return false; } boolean canUngroup; HierarchicalGraph hg = getHierarchicalGraph(); canUngroup = getNodeChildrenCount(hg, node) > 0;//The node has children return canUngroup; } public boolean moveNodeToGroup(Node node, Node group) { if (canMoveNodeToGroup(node, group)) { getHierarchicalGraph().moveToGroup(node, group); return true; } else { return false; } } public void moveNodesToGroup(Node[] nodes, Node group) { for (Node n : nodes) { moveNodeToGroup(n, group); } } public Node[] getAvailableGroupsToMoveNodes(Node[] nodes) { if (canGroupNodes(nodes)) { HierarchicalGraph hg = getHierarchicalGraph(); Set<Node> nodesSet = new HashSet<Node>(); nodesSet.addAll(Arrays.asList(nodes)); //All have the same parent, get children and check what of them are groups and are not in the nodes array: Node parent = hg.getParent(nodes[0]); Node[] possibleGroups; //If no parent, get nodes at level 0: if (parent != null) { possibleGroups = hg.getChildren(parent).toArray(); } else { possibleGroups = hg.getNodes(0).toArray(); } ArrayList<Node> availableGroups = new ArrayList<Node>(); for (Node n : possibleGroups) { if (!nodesSet.contains(n) && getNodeChildrenCount(hg, n) > 0) { availableGroups.add(n); } } return availableGroups.toArray(new Node[0]); } else { return null; } } public boolean canMoveNodeToGroup(Node node, Node group) { HierarchicalGraph hg = getHierarchicalGraph(); return node != group && hg.getParent(node) == hg.getParent(group) && canUngroupNode(group) && isNodeInGraph(node); } public boolean removeNodeFromGroup(Node node) { if (isNodeInGroup(node)) { HierarchicalGraph hg = getHierarchicalGraph(); Node parent = hg.getParent(node); hg.readLock(); int childrenCount = hg.getChildrenCount(parent); hg.readUnlock(); if (childrenCount == 1) { hg.ungroupNodes(parent);//Break group when the last child is removed. } else { hg.removeFromGroup(node); } return true; } else { return false; } } public void removeNodesFromGroup(Node[] nodes) { for (Node n : nodes) { removeNodeFromGroup(n); } } public boolean isNodeInGroup(Node node) { if (!isNodeInGraph(node)) { return false; } HierarchicalGraph hg = getHierarchicalGraph(); return hg.getParent(node) != null; } public void setNodeFixed(Node node, boolean fixed) { if (isNodeInGraph(node)) { node.getNodeData().setFixed(fixed); } } public void setNodesFixed(Node[] nodes, boolean fixed) { for (Node n : nodes) { setNodeFixed(n, fixed); } } public boolean isNodeFixed(Node node) { return node.getNodeData().isFixed(); } public Node[] getNodeNeighbours(Node node) { return getGraph().getNeighbors(node).toArray(); } public Edge[] getNodeEdges(Node node) { return getGraph().getEdges(node).toArray(); } public int getNodesCount() { Graph graph = getGraph(); graph.readLock(); int nodesCount = graph.getNodeCount(); graph.readUnlock(); return nodesCount; } public int getEdgesCount() { Graph graph = getGraph(); graph.readLock(); int edgesCount = graph.getEdgeCount(); graph.readUnlock(); return edgesCount; } public boolean isNodeInGraph(Node node) { return getGraph().contains(node); } public boolean areNodesInGraph(Node[] nodes) { Graph graph = getGraph(); for (Node n : nodes) { if (!graph.contains(n)) { return false; } } return true; } public boolean isEdgeInGraph(Edge edge) { return getGraph().contains(edge); } public boolean areEdgesInGraph(Edge[] edges) { Graph graph = getGraph(); for (Edge e : edges) { if (!graph.contains(e)) { return false; } } return true; } /************Private methods : ************/ private Graph getGraph() { return Lookup.getDefault().lookup(GraphController.class).getModel().getGraph(); } private DirectedGraph getDirectedGraph() { return Lookup.getDefault().lookup(GraphController.class).getModel().getDirectedGraph(); } private UndirectedGraph getUndirectedGraph() { return Lookup.getDefault().lookup(GraphController.class).getModel().getUndirectedGraph(); } private HierarchicalGraph getHierarchicalGraph() { return Lookup.getDefault().lookup(GraphController.class).getModel().getHierarchicalGraph(); } private Node buildNode(String label) { Node newNode = Lookup.getDefault().lookup(GraphController.class).getModel().factory().newNode(); newNode.getNodeData().setSize(DEFAULT_NODE_SIZE); newNode.getNodeData().setLabel(label); return newNode; } private Node buildNode(String label, String id) { Node newNode = buildNode(label); getGraph().setId(newNode, id); return newNode; } private Edge buildEdge(Node source, Node target, boolean directed) { Edge newEdge = Lookup.getDefault().lookup(GraphController.class).getModel().factory().newEdge(source, target, DEFAULT_EDGE_WEIGHT, directed); return newEdge; } private Edge buildEdge(String id, Node source, Node target, boolean directed) { Edge newEdge = Lookup.getDefault().lookup(GraphController.class).getModel().factory().newEdge(id, source, target, DEFAULT_EDGE_WEIGHT, directed); return newEdge; } private Node copyNodeRecursively(Node node, Node parent, HierarchicalGraph hg) { NodeData nodeData = node.getNodeData(); Node copy = buildNode(nodeData.getLabel()); NodeData copyData = copy.getNodeData(); //Copy properties (position, size and color): copyData.setX(nodeData.x()); copyData.setY(nodeData.y()); copyData.setZ(nodeData.z()); copyData.setSize(nodeData.getSize()); copyData.setColor(nodeData.r(), nodeData.g(), nodeData.b()); copyData.setAlpha(nodeData.alpha()); //Copy attributes: AttributeRow row = (AttributeRow) nodeData.getAttributes(); for (int i = 0; i < row.countValues(); i++) { if (row.getValues()[i].getColumn().getOrigin() == AttributeOrigin.DATA) { copyData.getAttributes().setValue(i, row.getValue(i)); } } if (parent != null) { hg.addNode(copy, parent); } else { hg.addNode(copy); } //Copy the children of the original node if any: Node[] children = hg.getChildren(node).toArray(); if (children != null) { for (Node child : children) { copyNodeRecursively(child, copy, hg); } } return copy; } private void removeNode(Node node, Graph graph) { if (isNodeInGraph(node)) { graph.removeNode(node); } } private void removeEdge(Edge edge, Graph graph) { if (isEdgeInGraph(edge)) { graph.removeEdge(edge); } } private int getNodeChildrenCount(HierarchicalGraph hg, Node n) { hg.readLock(); int childrenCount = hg.getChildrenCount(n); hg.readUnlock(); return childrenCount; } }