All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cdc.graphs.impl.BasicLightGraph Maven / Gradle / Ivy
package cdc.graphs.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import cdc.graphs.EdgeDirection;
import cdc.graphs.EdgeTip;
import cdc.graphs.GraphAdapter;
import cdc.graphs.GraphEdge;
import cdc.util.function.IterableUtils;
import cdc.util.lang.Checks;
import cdc.util.lang.InvalidStateException;
/**
* Basic and naive graph implementation using light nodes.
*
* Node can be any object.
*
* @author Damien Carbonne
*
* @param Node type.
* @param Edge type.
*/
public class BasicLightGraph> implements GraphAdapter {
private static final String INVALID_STATE = "Invalid state";
/** Set of edges. */
private final Set edges;
/** Map from nodes to associated edges. */
private final Map> nodeToEdges;
private boolean locked = false;
private boolean checks = true;
private final Function> newEdgeCollection;
public enum CollectionKind {
LIST,
SET
}
public BasicLightGraph(boolean sorted,
CollectionKind collectionKind) {
if (sorted) {
this.edges = new LinkedHashSet<>();
this.nodeToEdges = new LinkedHashMap<>();
} else {
this.edges = new HashSet<>();
this.nodeToEdges = new HashMap<>();
}
if (collectionKind == CollectionKind.LIST) {
newEdgeCollection = o -> new ArrayList<>();
} else {
newEdgeCollection = o -> new HashSet<>();
}
}
public BasicLightGraph(CollectionKind collectionKind) {
this(false, collectionKind);
}
public BasicLightGraph() {
this(CollectionKind.LIST);
}
public void setChecksEnabled(boolean enabled) {
this.checks = enabled;
}
/**
* Returns the collection of edges associated to a node.
*
* @param node The node.
* @return The set of edges associated to {@code node}.
* @throws IllegalArgumentException When {@code node} is {@code null}
* or is not contained in this graph.
*/
private Collection getNodeEdges(N node) {
if (checks) {
Checks.isNotNull(node, "node");
Checks.isTrue(containsNode(node), "Unknown node: {}", node);
}
final Collection tmp = nodeToEdges.get(node);
return tmp == null ? Collections.emptySet() : tmp;
}
/**
* Checks that this graph is not locked.
*
* @throws InvalidStateException When this graph is locked.
*/
private void checkIsUnlocked() {
if (locked) {
throw new InvalidStateException("Locked");
}
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public boolean isLocked() {
return locked;
}
/**
* Clears this graph.
*
* @throws InvalidStateException When this graph is locked.
*/
public void clear() {
if (checks) {
checkIsUnlocked();
}
edges.clear();
nodeToEdges.clear();
}
/**
* Clears all edges of this graph.
*
* @throws InvalidStateException When this graph is locked.
*/
public void clearEdges() {
if (checks) {
checkIsUnlocked();
}
edges.clear();
}
/**
* Adds a node to this graph.
*
* @param The node type.
* @param node The node.
* @return The passed {@code node}.
* @throws IllegalArgumentException When {@code node} is {@code null}
* or is already contained in this graph.
* @throws InvalidStateException When this graph is locked.
*/
public X addNode(X node) {
if (checks) {
checkIsUnlocked();
Checks.isNotNull(node, "node");
Checks.isTrue(!containsNode(node), "Node already declared: {}", node);
}
nodeToEdges.put(node, null);
return node;
}
public void addNodes(Collection extends N> nodes) {
for (final N node : nodes) {
addNode(node);
}
}
/**
* Ads a node if not already contained.
*
* @param The node type.
* @param node The node.
*/
public void addNodeIfMissing(X node) {
if (!containsNode(node)) {
addNode(node);
}
}
public void addNodesIfMissing(Collection extends N> nodes) {
for (final N node : nodes) {
addNodeIfMissing(node);
}
}
/**
* Remove a node from this graph.
*
* @param node The node.
* @throws IllegalArgumentException When {@code node} is {@code null}
* or is not contained in this graph.
* @throws InvalidStateException When this graph is locked.
*/
public void removeNode(N node) {
if (checks) {
checkIsUnlocked();
Checks.isNotNull(node, "node");
Checks.isTrue(containsNode(node), "Node does not belong to graph: {}", node);
}
// Remove edges attached to node
final Set tmp = new HashSet<>();
for (final E edge : getEdges(node)) {
tmp.add(edge);
}
for (final E edge : tmp) {
removeEdge(edge);
}
// Remove node
// Associated edges must have been removed by above code
if (checks) {
Checks.assertTrue(getNodeEdges(node).isEmpty(), INVALID_STATE);
}
nodeToEdges.remove(node);
}
public void removeNodes(Collection extends N> nodes) {
for (final N node : nodes) {
removeNode(node);
}
}
/**
* Adds an edge to this graph.
*
* @param The edge type.
* @param edge The edge.
* @return The passed {@code edge}.
* @throws IllegalArgumentException When {@code edge} is {@code null}
* or is already contained in this graph,
* or {@code edge} source or target don't not belong to this graph.
* @throws InvalidStateException When this graph is locked.
*/
public X addEdge(X edge) {
if (checks) {
checkIsUnlocked();
Checks.isNotNull(edge, "edge");
Checks.isTrue(!containsEdge(edge), "Edge already declared: {}", edge);
Checks.isTrue(containsNode(edge.getSource()), "edge source {} does not belong to graph.", edge.getSource());
Checks.isTrue(containsNode(edge.getTarget()), "edge target {} does not belong to graph.", edge.getTarget());
}
edges.add(edge);
final Collection sourceEdges = nodeToEdges.computeIfAbsent(edge.getSource(), newEdgeCollection);
sourceEdges.add(edge);
if (!edge.getSource().equals(edge.getTarget())) {
final Collection targetEdges = nodeToEdges.computeIfAbsent(edge.getTarget(), newEdgeCollection);
targetEdges.add(edge);
}
return edge;
}
public void addEdges(Collection extends E> edges) {
for (final E edge : edges) {
addEdge(edge);
}
}
/**
* Adds an edge if not already contained.
*
* @param The edge type.
* @param edge The edge.
*/
public void addEdgeIfMissing(X edge) {
if (!containsEdge(edge)) {
addEdge(edge);
}
}
public void addEdgesIfMissing(Collection extends E> edges) {
for (final E edge : edges) {
addEdgeIfMissing(edge);
}
}
/**
* Removes an edge from this graph.
*
* @param edge The edge.
* @throws IllegalArgumentException When {@code edge} is {@code null}
* or is not contained in this graph.
* @throws InvalidStateException When this graph is locked.
*/
public void removeEdge(E edge) {
if (checks) {
checkIsUnlocked();
}
// Remove from edges
final boolean removed = edges.remove(edge);
if (checks) {
Checks.assertTrue(removed, "Failed to remove {} from edges", edge);
}
// Remove from source node edges
final Collection sourceEdges = nodeToEdges.get(edge.getSource());
if (checks) {
Checks.assertTrue(sourceEdges != null, INVALID_STATE);
}
sourceEdges.remove(edge);
// Remove from target node edges
if (!edge.getSource().equals(edge.getTarget())) {
final Collection targetEdges = nodeToEdges.get(edge.getTarget());
if (checks) {
Checks.assertTrue(targetEdges != null, INVALID_STATE);
}
targetEdges.remove(edge);
}
}
public void removeEdges(Collection extends E> edges) {
for (final E edge : edges) {
removeEdge(edge);
}
}
@Override
public final Set getNodes() {
return nodeToEdges.keySet();
}
@Override
public final boolean containsNode(N node) {
return nodeToEdges.containsKey(node);
}
@Override
public final Set getEdges() {
return edges;
}
@Override
public final boolean containsEdge(E edge) {
return edges.contains(edge);
}
/**
* Return {@code true} if this graph contains at least one edge between a source node and a target node.
*
* @param source The source node.
* @param target The target node.
* @return {@code true} if this graph contains at least one edge between {@code source} and {@code target}.
*/
public boolean containsEdge(N source,
N target) {
final Collection sourceEdges = nodeToEdges.get(source);
final Collection targetEdges = nodeToEdges.get(target);
if (sourceEdges == null || targetEdges == null) {
return false;
} else if (sourceEdges.size() <= targetEdges.size()) {
for (final E edge : sourceEdges) {
if (edge.getTarget().equals(target) && edge.getSource().equals(source)) {
return true;
}
}
return false;
} else {
for (final E edge : targetEdges) {
if (edge.getSource().equals(source) && edge.getTarget().equals(target)) {
return true;
}
}
return false;
}
}
@Override
public final Iterable extends E> getEdges(N node,
EdgeDirection direction) {
final Collection nodeEdges = getNodeEdges(node);
if (direction == null) {
return nodeEdges;
} else if (direction == EdgeDirection.INGOING) {
return IterableUtils.filter(nodeEdges, e -> e.getTarget().equals(node));
} else {
return IterableUtils.filter(nodeEdges, e -> e.getSource().equals(node));
}
}
@Override
public final N getTip(E edge,
EdgeTip tip) {
if (checks) {
Checks.isNotNull(edge, "edge");
Checks.isNotNull(tip, "tip");
}
if (tip == EdgeTip.SOURCE) {
return edge.getSource();
} else {
return edge.getTarget();
}
}
}