com.github.moaxcp.graphs.AbstractGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of graphs Show documentation
Show all versions of graphs Show documentation
dynamic graphs supporting streams and an EventBus
package com.github.moaxcp.graphs;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;
import java.util.*;
/**
* This class provides a partial implementation of the {@link Graph} interface.
*
* Vertices and edges are stored in insertion order.
* @param type of all identifiers in graph
*/
public abstract class AbstractGraph implements Graph {
private static final String NAME_MUST_NOT_BE_NULL = "name must not be null.";
private static final String VALUE_MUST_NOT_BE_NULL = "value must not be null.";
private static final String NAME_MUST_NOT_BE_EMPTY = "name must not be empty.";
private static final String ID_MUST_NOT_BE_NULL = "id must not be null.";
public class SimpleEdge extends InheritingElement> implements Edge {
private ID id;
private ID from;
private ID to;
protected SimpleEdge(ID from, ID to, Map inherited) {
super(inherited);
this.from = from;
this.to = to;
}
private void check() {
EdgeKey key = newEdgeKey(from, to);
if(!edges.keySet().contains(key)) {
throw new IllegalStateException("Edge is not in graph.");
}
}
@Override
public final Optional getId() {
return Optional.ofNullable(id);
}
@Override
public void setId(ID id) {
check();
edgeIds.remove(this.id);
if(id != null) {
edgeIds.put(id, this);
}
this.id = id;
}
@Override
public final Edge id(ID id) {
setId(id);
return self();
}
@Override
public final ID getFrom() {
return from;
}
@Override
public void setFrom(ID from) {
check();
requireNonNull(from, "from must not be null.");
EdgeKey oldKey = newEdgeKey(this.from, this.to);
edges.remove(oldKey);
vertex(from);
this.from = from;
EdgeKey key = newEdgeKey(this.from, this.to);
edges.put(key, this);
}
@Override
public Edge from(ID from) {
setFrom(from);
return this;
}
@Override
public final ID from() {
return getFrom();
}
@Override
public final ID getTo() {
return to;
}
@Override
public void setTo(ID to) {
check();
Objects.requireNonNull(to, "to must not be null.");
EdgeKey oldKey = newEdgeKey(this.from, this.to);
edges.remove(oldKey);
vertex(to);
this.to = to;
EdgeKey key = newEdgeKey(this.from, this.to);
edges.put(key, this);
}
@Override
public final Edge to(ID to) {
setTo(to);
return this;
}
@Override
public final ID to() {
return getTo();
}
@Override
public final boolean isDirected() {
return AbstractGraph.this.isDirected();
}
@Override
public final List endpoints() {
return List.of(from, to);
}
@Override
public final Vertex fromVertex() {
check();
return vertex(getFrom());
}
@Override
public final Vertex toVertex() {
check();
return vertex(getTo());
}
@Override
public void setProperty(String name, Object value) {
check();
super.setProperty(name, value);
}
@Override
public Edge removeProperty(String name) {
check();
return super.removeProperty(name);
}
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AbstractGraph>.SimpleEdge)) return false;
SimpleEdge that = (SimpleEdge) o;
return Objects.equals(id, that.id) &&
Objects.equals(from, that.from) &&
Objects.equals(to, that.to) &&
Objects.equals(local(), that.local()) &&
Objects.equals(inherited(), that.inherited());
}
@Override
public final int hashCode() {
return Objects.hash(id, from, to, local(), inherited());
}
}
public class SimpleVertex extends InheritingElement> implements Vertex {
private ID id;
protected SimpleVertex(ID id, Map inherited) {
super(inherited);
Objects.requireNonNull(id, ID_MUST_NOT_BE_NULL);
this.id = id;
}
private void check() {
if(!vertices.containsKey(getId())) {
throw new IllegalStateException("Vertex is not in graph.");
}
}
public ID getId() {
return id;
}
@Override
public void setId(ID id) {
check();
Objects.requireNonNull(id, ID_MUST_NOT_BE_NULL);
Set extends Edge> adjacent = adjacentEdges();
Object oldId = getId();
vertices.remove(this.getId());
this.id = id;
vertices.put(id, this);
for (Edge edge : adjacent) {
if (edge.getFrom().equals(oldId)) {
edge.setFrom(id);
}
if (edge.getTo().equals(oldId)) {
edge.setTo(id);
}
}
}
@Override
public Vertex id(ID id) {
setId(id);
return self();
}
@Override
public void setProperty(String name, Object value) {
check();
super.setProperty(name, value);
}
@Override
public Vertex removeProperty(String name) {
check();
super.removeProperty(name);
return self();
}
@Override
public Vertex connectsTo(ID to) {
check();
edge(getId(), to);
return this;
}
@Override
public Vertex connectsFrom(ID s) {
check();
edge(s, getId());
return this;
}
@Override
public Edge edgeTo(ID to) {
check();
return edge(getId(), to);
}
@Override
public Edge edgeFrom(ID from) {
check();
return edge(from, getId());
}
@Override
public Vertex toVertex(ID id) {
check();
return edgeTo(id).toVertex();
}
@Override
public Vertex fromVertex(ID id) {
check();
return edgeFrom(id).fromVertex();
}
@Override
public Set> adjacentEdges() {
check();
Set> edges = adjacentEdges.get(id);
if(edges == null) {
return Collections.emptySet();
}
return unmodifiableSet(edges);
}
@Override
public Set> inEdges() {
check();
Set> edges = inEdges.get(id);
if(edges == null) {
return Collections.emptySet();
}
return unmodifiableSet(edges);
}
@Override
public Set> outEdges() {
check();
Set> edges = outEdges.get(id);
if(edges == null) {
return Collections.emptySet();
}
return unmodifiableSet(edges);
}
@Override
public final boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AbstractGraph>.SimpleVertex)) return false;
SimpleVertex that = (SimpleVertex) o;
return Objects.equals(id, that.id) &&
Objects.equals(local(), that.local()) &&
Objects.equals(inherited(), that.inherited());
}
@Override
public final int hashCode() {
return Objects.hash(id, local(), inherited());
}
}
private ID id;
private Map properties;
private Map vertexProperties;
private Map edgeProperties;
private Map> vertices;
private Map, Edge> edges;
private Map> edgeIds;
private Map>> adjacentEdges;
private Map>> inEdges;
private Map>> outEdges;
protected AbstractGraph() {
vertices = new LinkedHashMap<>();
edges = new LinkedHashMap<>();
edgeIds = new LinkedHashMap<>();
adjacentEdges = new LinkedHashMap<>();
inEdges = new LinkedHashMap<>();
outEdges = new LinkedHashMap<>();
vertexProperties = new LinkedHashMap<>();
edgeProperties = new LinkedHashMap<>();
properties = new LinkedHashMap<>();
}
protected AbstractGraph(ID id) {
this();
this.id = id;
}
@Override
public Map> getVertices() {
return Collections.unmodifiableMap(vertices);
}
@Override
public Collection> getEdges() {
return Collections.unmodifiableCollection(edges.values());
}
@Override
public Map> getEdgeIds() { return Collections.unmodifiableMap(edgeIds); }
@Override
public Optional> findVertex(ID id) {
return Optional.ofNullable(vertices.get(id));
}
@Override
public Vertex vertex(ID id) {
return findVertex(id).orElseGet(() -> addVertex(id));
}
protected Vertex addVertex(ID id) {
var vertex = newVertex(id, vertexProperties);
vertices.put(id, vertex);
return vertex;
}
@Override
public void removeVertex(ID id) {
Objects.requireNonNull(id, ID_MUST_NOT_BE_NULL);
var optional = findVertex(id);
if (!optional.isPresent()) {
throw new IllegalArgumentException("vertex '" + id + "' not found.");
}
var adjacent = optional.get().adjacentEdges().stream()
.map(e-> newEdgeKey(e.getFrom(), e.getTo()))
.collect(toSet());
for (var edge : adjacent) {
removeEdge(edge.getFrom(), edge.getTo());
}
vertices.remove(id);
}
@Override
public Optional> findEdge(ID from, ID to) {
requireNonNull(from, "from must not be null.");
requireNonNull(to, "to must not be null.");
var key = newEdgeKey(from, to);
var edge = edges.get(key);
return Optional.ofNullable(edge);
}
@Override
public Optional> findEdge(ID id) {
requireNonNull(id, "id must not be null.");
return Optional.ofNullable(edgeIds.get(id));
}
@Override
public Edge edge(ID from, ID to) {
return findEdge(from, to).orElseGet(() -> addEdge(from, to));
}
protected Edge newEdge(ID from, ID to, Map inherited) {
return new SimpleEdge(from, to, inherited);
}
protected abstract EdgeKey newEdgeKey(ID from, ID to);
protected Vertex newVertex(ID id, Map inherited) {
return new SimpleVertex(id, inherited);
}
protected Edge addEdge(ID from, ID to) {
vertex(from);
vertex(to);
var edge = newEdge(from, to, edgeProperties);
var edgeKey = newEdgeKey(from, to);
edges.put(edgeKey, edge);
adjacentEdges.compute(edgeKey.getFrom(), (k, v) -> mergeSet(edge, v));
adjacentEdges.compute(edgeKey.getTo(), (k, v) -> mergeSet(edge, v));
inEdges.compute(edgeKey.getTo(), (k, v) -> mergeSet(edge, v));
outEdges.compute(edgeKey.getFrom(), (k, v) -> mergeSet(edge, v));
return edge;
}
private Set> mergeSet(Edge edge, Set> set) {
if(set == null) {
set = new LinkedHashSet<>();
set.add(edge);
}
else {
set.add(edge);
}
return set;
}
@Override
public void removeEdge(ID from, ID to) {
requireNonNull(from, "from must not be null.");
requireNonNull(to, "to must not be null.");
EdgeKey key = newEdgeKey(from, to);
var edge = edges.remove(key);
if(edge == null) {
throw new IllegalArgumentException("edge from '" + from + "' to '" + to + "' not found.");
}
edge.getId().ifPresent(edgeIds::remove);
var fromSet = adjacentEdges.get(from);
fromSet.remove(edge);
if(fromSet.isEmpty()) {
adjacentEdges.remove(from);
}
var toSet = adjacentEdges.get(to);
toSet.remove(edge);
if(toSet.isEmpty()) {
adjacentEdges.remove(to);
}
var outSet = outEdges.get(from);
outSet.remove(edge);
if(outSet.isEmpty()) {
outEdges.remove(from);
}
var inSet = inEdges.get(to);
inSet.remove(edge);
if(inSet.isEmpty()) {
inEdges.remove(to);
}
}
@Override
public void removeEdge(ID id) {
requireNonNull(id, ID_MUST_NOT_BE_NULL);
var optional = findEdge(id);
optional.ifPresent(edge -> removeEdge(edge.getFrom(), edge.getTo()));
if(!optional.isPresent()) {
throw new IllegalArgumentException("edge with id '" + id + "' not found.");
}
}
@Override
public Optional getId() {
return Optional.ofNullable(id);
}
@Override
public void setId(ID id) {
this.id = id;
}
@Override
public Graph id(ID id) {
setId(id);
return this;
}
@Override
public Optional
© 2015 - 2025 Weber Informatics LLC | Privacy Policy