/**
* Global Sensor Networks (GSN) Source Code
* Copyright (c) 2006-2016, Ecole Polytechnique Federale de Lausanne (EPFL)
*
* This file is part of GSN.
*
* GSN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GSN 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GSN. If not, see <http://www.gnu.org/licenses/>.
*
* File: src/ch/epfl/gsn/utils/graph/Graph.java
*
* @author Mehdi Riahi
* @author gsn_devs
*
*/
package ch.epfl.gsn.utils.graph;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class Graph<T> implements Serializable{
private static final long serialVersionUID = 9015284213829329797L;
private ArrayList<Node<T>> nodes;
private ArrayList<Node<T>> rootNodes;
public Graph() {
nodes = new ArrayList<Node<T>>();
rootNodes = new ArrayList<Node<T>>();
}
public List<Node<T>> getDescendingNodes(Node<T> node) {
resetVisitingStatus();
ArrayList<Node<T>> list = new ArrayList<Node<T>>();
dfs(node, list);
return list;
}
public List<T> getNodesByDFSSearch() {
ArrayList<Node<T>> list = new ArrayList<Node<T>>();
for (Node<T> node : rootNodes) {
dfs(node, list);
}
ArrayList<T> objectList = new ArrayList<T>();
for (Node<T> node : list) {
objectList.add(node.getObject());
}
return objectList;
}
private List<Node<T>> getAscendingNodes(Node<T> node) {
resetVisitingStatus();
ArrayList<Node<T>> list = new ArrayList<Node<T>>();
rdfs(node, list);
return list;
}
private void rdfs(Node<T> node, ArrayList<Node<T>> list) {
if (node == null)
return;
node.setVisited(true);
for (Edge<T> edge : node.getInputEdges()) {
if (edge.getStartNode().isVisited() == false) {
rdfs(edge.getStartNode(), list);
}
}
list.add(node);
}
/**
* Returns list of nodes that are ascendings of the <code>node</code> including the <code>node</code> itself
* @param node
* @return
*/
public List<Node<T>> nodesAffectedByRemoval(Node<T> node) {
return getAscendingNodes(node);
}
public boolean hasCycle() {
resetVisitingStatus();
for (Node<T> node : rootNodes) {
if (isNodeInCycle(node))
return true;
}
return false;
}
private boolean isNodeInCycle(Node<T> node) {
if (node.isVisited())
return true;
node.setVisited(true);
for (Edge<T> edge : node.getOutputEdges()) {
if (isNodeInCycle(edge.getEndNode()))
return true;
}
node.setVisited(false);
return false;
}
public Node<T> addNode(T object) {
if (findNode(object) == null) {
Node<T> node = new Node<T>(object);
nodes.add(node);
rootNodes.add(node);
return node;
}
return null;
}
public void addEdge(T startObject, T endObject)
throws NodeNotExistsExeption {
Node<T> startNode = findNode(startObject);
if (startNode == null)
throw new NodeNotExistsExeption(startObject == null ? "null" : startObject.toString());
Node<T> endNode = findNode(endObject);
if (endNode == null)
throw new NodeNotExistsExeption(endObject == null ? "null" : endObject.toString());
try {
startNode.addEdge(endNode);
if(!endNode.equals(findRootNode(startNode)))
rootNodes.remove(endNode);
} catch (EdgeExistsException e) {
// TODO Auto-generated catch block
}
}
public Node<T> findRootNode(Node<T> startNode) {
List<Node<T>> ascendingNodes = getAscendingNodes(startNode);
for (Node<T> node : ascendingNodes) {
if(rootNodes.contains(node))
return node;
}
return null;
}
/**
* Removes node having <code>object</code> as node's object and also
* removes all ascending nodes of it, except root node.
*
* @param Object
* @return a boolean indicating whether the node is removed
* @throws NodeNotExistsExeption
*/
public boolean removeNode(T object) throws NodeNotExistsExeption {
Node<T> node = findNode(object);
if (node == null)
throw new NodeNotExistsExeption(object == null ? "null" : object.toString());
List<Node<T>> ascendingNodes = getAscendingNodes(node);
for (Node<T> ascendingNode : ascendingNodes) {
ArrayList<Edge<T>> outputEdges = ascendingNode.getOutputEdges();
ArrayList<Node<T>> nodesToRemove = new ArrayList<Node<T>>(
outputEdges.size());
for (Edge<T> edge : outputEdges) {
nodesToRemove.add(edge.getEndNode());
}
for (Node<T> node2 : nodesToRemove) {
ascendingNode.removeEdge(node2);
}
nodes.remove(ascendingNode);
rootNodes.remove(ascendingNode);
}
nodes.remove(node);
rootNodes.remove(node);
for (Node<T> remainedNode : nodes) {
if (remainedNode.getInputEdges().isEmpty()
&& rootNodes.contains(remainedNode) == false)
rootNodes.add(remainedNode);
}
return true;
}
public Node<T> findNode(T object) {
for (Node<T> node : nodes) {
if (node.getObject() == null && object == null)
return null;
if (node.getObject() != null && node.getObject().equals(object))
return node;
}
return null;
}
private void dfs(Node<T> node, List<Node<T>> list) {
if (node == null)
return;
for (Edge<T> edge : node.getOutputEdges()) {
if (edge.getEndNode().isVisited() == false)
dfs(edge.getEndNode(), list);
}
if (node.isRoot() == false) {
list.add(node);
node.setVisited(true);
}
}
private void resetVisitingStatus() {
for (Node<T> node : nodes) {
node.setVisited(false);
}
}
public ArrayList<Node<T>> getNodes() {
return nodes;
}
public ArrayList<Node<T>> getRootNodes() {
return rootNodes;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("[Graph]\n");
for (Node<T> node : nodes) {
if (node.getOutputEdges().isEmpty() && rootNodes.contains(node))
stringBuilder.append("\t").append(node).append("\n");
for (Edge<T> edge : node.getOutputEdges()) {
stringBuilder.append("\t").append(node).append(" -- > ")
.append(edge.getEndNode()).append("\n");
}
}
return stringBuilder.toString();
}
}