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

com.github.moaxcp.graphs.AbstractGraph Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
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> 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 getProperty(String name) { return Optional.ofNullable(properties.get(name)); } @Override public void setProperty(String name, Object value) { requireNonNull(name, NAME_MUST_NOT_BE_NULL); requireNonNull(value, VALUE_MUST_NOT_BE_NULL); if(name.isEmpty()) { throw new IllegalArgumentException(NAME_MUST_NOT_BE_EMPTY); } properties.put(name, value); } @Override public Graph property(String name, Object value) { setProperty(name, value); return this; } @Override public Graph removeProperty(String name) { requireNonNull(name, NAME_MUST_NOT_BE_NULL); if(!properties.containsKey(name)) { throw new IllegalArgumentException("graph does not contain property named '" + name + "'."); } properties.remove(name); return this; } @Override public Optional getEdgeProperty(String name) { return Optional.ofNullable(edgeProperties.get(name)); } @Override public void setEdgeProperty(String name, Object value) { requireNonNull(name, NAME_MUST_NOT_BE_NULL); requireNonNull(value, VALUE_MUST_NOT_BE_NULL); if(name.isEmpty()) { throw new IllegalArgumentException(NAME_MUST_NOT_BE_EMPTY); } edgeProperties.put(name, value); } @Override public Graph edgeProperty(String name, Object value) { setEdgeProperty(name, value); return this; } @Override public Graph removeEdgeProperty(String name) { requireNonNull(name, NAME_MUST_NOT_BE_NULL); if(!edgeProperties.containsKey(name)) { throw new IllegalArgumentException("graph does not contain edge property named '" + name + "'."); } edgeProperties.remove(name); return this; } @Override public Optional getVertexProperty(String name) { return Optional.ofNullable(vertexProperties.get(name)); } @Override public void setVertexProperty(String name, Object value) { requireNonNull(name, NAME_MUST_NOT_BE_NULL); requireNonNull(value, VALUE_MUST_NOT_BE_NULL); if(name.isEmpty()) { throw new IllegalArgumentException(NAME_MUST_NOT_BE_EMPTY); } vertexProperties.put(name, value); } @Override public Graph vertexProperty(String name, Object value) { setVertexProperty(name, value); return this; } @Override public Graph removeVertexProperty(String name) { requireNonNull(name, NAME_MUST_NOT_BE_NULL); if(!vertexProperties.containsKey(name)) { throw new IllegalArgumentException("graph does not contain edge property named '" + name + "'."); } vertexProperties.remove(name); return this; } @Override public final boolean equals(Object o) { if (this == o) return true; if (!(o instanceof AbstractGraph)) return false; AbstractGraph that = (AbstractGraph) o; return Objects.equals(id, that.id) && Objects.equals(properties, that.properties) && Objects.equals(vertexProperties, that.vertexProperties) && Objects.equals(edgeProperties, that.edgeProperties) && Objects.equals(vertices, that.vertices) && Objects.equals(edges, that.edges); } @Override public final int hashCode() { return Objects.hash(id, properties, vertexProperties, edgeProperties, vertices, edges); } }