blob: a49210e43a7e81dbe61a4a9015f2dccbb8805d27 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Francis Giraldeau - Initial implementation and API
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.core.synchronization.graph;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
/**
* Minimal graph implementation to compute timestamps transforms of a trace from
* a given synchronized set of traces. The graph is implemented as an adjacency
* list and is directed. To create undirected graph, add the edge in both
* directions.
*
* @author Francis Giraldeau <francis.giraldeau@gmail.com>
* @param <V>
* The vertices type
* @param <E>
* The edge annotation type
*/
public class SyncGraph<V, E> {
private Multimap<V, Edge<V, E>> fAdjacentEdges;
private Set<V> fVertices;
/**
* Construct empty graph
*/
public SyncGraph() {
fAdjacentEdges = ArrayListMultimap.create();
fVertices = new HashSet<>();
}
/**
* Add edge from v to w and annotation label
*
* @param from
* from vertex
* @param to
* to vertex
* @param label
* the edge label
*/
public void addEdge(V from, V to, E label) {
fAdjacentEdges.put(from, new Edge<>(from, to, label));
fVertices.add(from);
fVertices.add(to);
}
/**
* Get the number of edges
*
* @return number of edges
*/
public int getNbEdges() {
return fAdjacentEdges.entries().size();
}
/**
* Get the number of vertices
*
* @return number of vertices
*/
public int getNbVertices() {
return fVertices.size();
}
/**
* Returns the adjacent edges of the given vertex
*
* @param v
* the vertex
* @return the adjacent vertices
*/
public Collection<Edge<V, E>> getAdjacentEdges(V v) {
return fAdjacentEdges.get(v);
}
/**
* Returns a path between start and end vertices.
*
* @param start
* vertex
* @param end
* vertex
* @return the list of edges between start and end vertices
*/
public List<Edge<V, E>> path(V start, V end) {
ArrayList<Edge<V, E>> path = new ArrayList<>();
HashMap<V, Edge<V, E>> hist = new HashMap<>();
HashSet<V> visited = new HashSet<>();
Queue<V> queue = new LinkedList<>();
queue.offer(start);
/**
* Build the map of nodes reachable from the start node, by recursively
* visiting all accessible nodes. It is a breadth-first search, so the
* edges kept for each node will be the shortest path to that node.
*/
while (!queue.isEmpty()) {
V node = queue.poll();
visited.add(node);
for (Edge<V, E> e : getAdjacentEdges(node)) {
V to = e.getTo();
if (!visited.contains(to)) {
queue.offer(e.getTo());
if (!hist.containsKey(e.getTo())) {
hist.put(e.getTo(), e);
}
}
}
}
/*
* Find path from start to end by traversing the edges backward, from
* the end node
*/
V node = end;
Edge<V, E> edge = hist.get(node);
while (edge != null && node != start) {
path.add(edge);
node = edge.getFrom();
edge = hist.get(node);
}
Collections.reverse(path);
return path;
}
/**
* Check if this graph is connected, ie there are no partitions, all
* vertices are reachable from every other one. It is a depth-first visit of
* all vertices reachable from the first vertex of the graph.
*
* @return true if the graph is connected, false otherwise
*/
public boolean isConnected() {
HashSet<V> visited = new HashSet<>();
Deque<V> stack = new ArrayDeque<>();
stack.push(fVertices.iterator().next());
while (!stack.isEmpty()) {
V node = stack.removeFirst();
visited.add(node);
for (Edge<V, E> edge : getAdjacentEdges(node)) {
if (!visited.contains(edge.getTo())) {
stack.addFirst(edge.getTo());
}
}
}
return visited.size() == fVertices.size();
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
for (V key : fAdjacentEdges.keySet()) {
str.append(key + ": " + fAdjacentEdges.get(key) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
return str.toString();
}
}