All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.graphs.impl.BasicLightGraph Maven / Gradle / Ivy

There is a newer version: 0.71.2
Show newest version
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 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 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 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 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 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 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 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(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy