
org.jgrapht.graph.AbstractBaseGraph Maven / Gradle / Ivy
/*
* (C) Copyright 2003-2018, by Barak Naveh and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
package org.jgrapht.graph;
import org.jgrapht.*;
import org.jgrapht.graph.specifics.*;
import org.jgrapht.util.*;
import java.io.*;
import java.util.*;
import java.util.function.*;
/**
* The most general implementation of the {@link org.jgrapht.Graph} interface. Its subclasses add
* various restrictions to get more specific graphs. The decision whether it is directed or
* undirected is decided at construction time and cannot be later modified (see constructor for
* details).
*
*
* This graph implementation guarantees deterministic vertex and edge set ordering (via
* {@link LinkedHashMap} and {@link LinkedHashSet}).
*
*
* @param the graph vertex type
* @param the graph edge type
*
* @author Barak Naveh
* @author Dimitrios Michail
* @since Jul 24, 2003
*/
public abstract class AbstractBaseGraph
extends
AbstractGraph
implements
Graph,
Cloneable,
Serializable
{
private static final long serialVersionUID = -3582386521833998627L;
private static final String LOOPS_NOT_ALLOWED = "loops not allowed";
private static final String GRAPH_SPECIFICS_MUST_NOT_BE_NULL =
"Graph specifics must not be null";
private transient Set unmodifiableVertexSet = null;
private Supplier vertexSupplier;
private Supplier edgeSupplier;
private GraphType type;
private Specifics specifics;
private IntrusiveEdgesSpecifics intrusiveEdgesSpecifics;
/**
* Construct a new graph.
*
* @param vertexSupplier the vertex supplier, can be null
* @param edgeSupplier the edge supplier, can be null
* @param type the graph type
*
* @throws IllegalArgumentException if the graph type is mixed
*/
protected AbstractBaseGraph(
Supplier vertexSupplier, Supplier edgeSupplier, GraphType type)
{
this.vertexSupplier = vertexSupplier;
this.edgeSupplier = edgeSupplier;
this.type = Objects.requireNonNull(type);
if (type.isMixed()) {
throw new IllegalArgumentException("Mixed graph not supported");
}
this.specifics = Objects
.requireNonNull(createSpecifics(type.isDirected()), GRAPH_SPECIFICS_MUST_NOT_BE_NULL);
this.intrusiveEdgesSpecifics = Objects.requireNonNull(
createIntrusiveEdgesSpecifics(type.isWeighted()), GRAPH_SPECIFICS_MUST_NOT_BE_NULL);
}
/**
* {@inheritDoc}
*/
@Override
public Set getAllEdges(V sourceVertex, V targetVertex)
{
return specifics.getAllEdges(sourceVertex, targetVertex);
}
@Override
public Supplier getEdgeSupplier()
{
return edgeSupplier;
}
/**
* Set the edge supplier that the graph uses whenever it needs to create new edges.
*
*
* A graph uses the edge supplier to create new edge objects whenever a user calls method
* {@link Graph#addEdge(Object, Object)}. Users can also create the edge in user code and then
* use method {@link Graph#addEdge(Object, Object, Object)} to add the edge.
*
*
* In contrast with the {@link Supplier} interface, the edge supplier has the additional
* requirement that a new and distinct result is returned every time it is invoked. More
* specifically for a new edge to be added in a graph e
must not be equal to
* any other edge in the graph (even if the graph allows edge-multiplicity). More formally, the
* graph must not contain any edge e2
such that e2.equals(e)
.
*
* @param edgeSupplier the edge supplier
*/
public void setEdgeSupplier(Supplier edgeSupplier)
{
this.edgeSupplier = edgeSupplier;
}
@Override
public Supplier getVertexSupplier()
{
return vertexSupplier;
}
/**
* Set the vertex supplier that the graph uses whenever it needs to create new vertices.
*
*
* A graph uses the vertex supplier to create new vertex objects whenever a user calls method
* {@link Graph#addVertex()}. Users can also create the vertex in user code and then use method
* {@link Graph#addVertex(Object)} to add the vertex.
*
*
* In contrast with the {@link Supplier} interface, the vertex supplier has the additional
* requirement that a new and distinct result is returned every time it is invoked. More
* specifically for a new vertex to be added in a graph v
must not be equal
* to any other vertex in the graph. More formally, the graph must not contain any vertex
* v2
such that v2.equals(v)
.
*
* @param vertexSupplier the vertex supplier
*/
public void setVertexSupplier(Supplier vertexSupplier)
{
this.vertexSupplier = vertexSupplier;
}
/**
* {@inheritDoc}
*/
@Override
public E getEdge(V sourceVertex, V targetVertex)
{
return specifics.getEdge(sourceVertex, targetVertex);
}
/**
* {@inheritDoc}
*/
@Override
public E addEdge(V sourceVertex, V targetVertex)
{
assertVertexExist(sourceVertex);
assertVertexExist(targetVertex);
if (!type.isAllowingMultipleEdges() && containsEdge(sourceVertex, targetVertex)) {
return null;
}
if (!type.isAllowingSelfLoops() && sourceVertex.equals(targetVertex)) {
throw new IllegalArgumentException(LOOPS_NOT_ALLOWED);
}
if (edgeSupplier == null)
throw new UnsupportedOperationException("The graph contains no edge supplier");
E e = edgeSupplier.get();
if (intrusiveEdgesSpecifics.add(e, sourceVertex, targetVertex)) {
specifics.addEdgeToTouchingVertices(e);
return e;
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean addEdge(V sourceVertex, V targetVertex, E e)
{
if (e == null) {
throw new NullPointerException();
}
assertVertexExist(sourceVertex);
assertVertexExist(targetVertex);
if (!type.isAllowingMultipleEdges() && containsEdge(sourceVertex, targetVertex)) {
return false;
}
if (!type.isAllowingSelfLoops() && sourceVertex.equals(targetVertex)) {
throw new IllegalArgumentException(LOOPS_NOT_ALLOWED);
}
if (intrusiveEdgesSpecifics.add(e, sourceVertex, targetVertex)) {
specifics.addEdgeToTouchingVertices(e);
return true;
}
return false;
}
@Override
public V addVertex()
{
if (vertexSupplier == null) {
throw new UnsupportedOperationException("The graph contains no vertex supplier");
}
V v = vertexSupplier.get();
if (specifics.addVertex(v)) {
return v;
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public boolean addVertex(V v)
{
if (v == null) {
throw new NullPointerException();
} else if (containsVertex(v)) {
return false;
} else {
specifics.addVertex(v);
return true;
}
}
/**
* {@inheritDoc}
*/
@Override
public V getEdgeSource(E e)
{
return intrusiveEdgesSpecifics.getEdgeSource(e);
}
/**
* {@inheritDoc}
*/
@Override
public V getEdgeTarget(E e)
{
return intrusiveEdgesSpecifics.getEdgeTarget(e);
}
/**
* Returns a shallow copy of this graph instance. Neither edges nor vertices are cloned.
*
* @return a shallow copy of this graph.
*
* @throws RuntimeException in case the clone is not supported
*
* @see java.lang.Object#clone()
*/
@Override
public Object clone()
{
try {
AbstractBaseGraph newGraph = TypeUtil.uncheckedCast(super.clone());
newGraph.vertexSupplier = this.vertexSupplier;
newGraph.edgeSupplier = this.edgeSupplier;
newGraph.unmodifiableVertexSet = null;
// NOTE: it's important for this to happen in an object
// method so that the new inner class instance gets associated with
// the right outer class instance
newGraph.specifics = newGraph.createSpecifics(this.getType().isDirected());
newGraph.intrusiveEdgesSpecifics =
newGraph.createIntrusiveEdgesSpecifics(this.getType().isWeighted());
Graphs.addGraph(newGraph, this);
return newGraph;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsEdge(E e)
{
return intrusiveEdgesSpecifics.containsEdge(e);
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsVertex(V v)
{
return specifics.getVertexSet().contains(v);
}
/**
* {@inheritDoc}
*/
@Override
public int degreeOf(V vertex)
{
assertVertexExist(vertex);
return specifics.degreeOf(vertex);
}
/**
* {@inheritDoc}
*/
@Override
public Set edgeSet()
{
return intrusiveEdgesSpecifics.getEdgeSet();
}
/**
* {@inheritDoc}
*/
@Override
public Set edgesOf(V vertex)
{
assertVertexExist(vertex);
return specifics.edgesOf(vertex);
}
/**
* {@inheritDoc}
*/
@Override
public int inDegreeOf(V vertex)
{
assertVertexExist(vertex);
return specifics.inDegreeOf(vertex);
}
/**
* {@inheritDoc}
*/
@Override
public Set incomingEdgesOf(V vertex)
{
assertVertexExist(vertex);
return specifics.incomingEdgesOf(vertex);
}
/**
* {@inheritDoc}
*/
@Override
public int outDegreeOf(V vertex)
{
assertVertexExist(vertex);
return specifics.outDegreeOf(vertex);
}
/**
* {@inheritDoc}
*/
@Override
public Set outgoingEdgesOf(V vertex)
{
assertVertexExist(vertex);
return specifics.outgoingEdgesOf(vertex);
}
/**
* {@inheritDoc}
*/
@Override
public E removeEdge(V sourceVertex, V targetVertex)
{
E e = getEdge(sourceVertex, targetVertex);
if (e != null) {
specifics.removeEdgeFromTouchingVertices(e);
intrusiveEdgesSpecifics.remove(e);
}
return e;
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeEdge(E e)
{
if (containsEdge(e)) {
specifics.removeEdgeFromTouchingVertices(e);
intrusiveEdgesSpecifics.remove(e);
return true;
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeVertex(V v)
{
if (containsVertex(v)) {
Set touchingEdgesList = edgesOf(v);
// cannot iterate over list - will cause
// ConcurrentModificationException
removeAllEdges(new ArrayList<>(touchingEdgesList));
specifics.getVertexSet().remove(v); // remove the vertex itself
return true;
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public Set vertexSet()
{
if (unmodifiableVertexSet == null) {
unmodifiableVertexSet = Collections.unmodifiableSet(specifics.getVertexSet());
}
return unmodifiableVertexSet;
}
/**
* {@inheritDoc}
*/
@Override
public double getEdgeWeight(E e)
{
if (e == null) {
throw new NullPointerException();
}
return intrusiveEdgesSpecifics.getEdgeWeight(e);
}
/**
* Set an edge weight.
*
* @param e the edge
* @param weight the weight
* @throws UnsupportedOperationException if the graph is not weighted
*/
@Override
public void setEdgeWeight(E e, double weight)
{
if (e == null) {
throw new NullPointerException();
}
intrusiveEdgesSpecifics.setEdgeWeight(e, weight);
}
/**
* {@inheritDoc}
*/
@Override
public GraphType getType()
{
return type;
}
/**
* Create the specifics for this graph. Subclasses can override this method in order to adjust
* the specifics and thus the space-time tradeoffs of the graph implementation.
*
* @param directed if true the specifics should adjust the behavior to a directed graph
* otherwise undirected
* @return the specifics used by this graph
*/
protected Specifics createSpecifics(boolean directed)
{
if (directed) {
return new FastLookupDirectedSpecifics<>(this);
} else {
return new FastLookupUndirectedSpecifics<>(this);
}
}
/**
* Create the specifics for the edges set of the graph.
*
* @param weighted if true the specifics should support weighted edges
* @return the specifics used for the edge set of this graph
*/
protected IntrusiveEdgesSpecifics createIntrusiveEdgesSpecifics(boolean weighted)
{
if (weighted) {
return new WeightedIntrusiveEdgesSpecifics<>();
} else {
return new UniformIntrusiveEdgesSpecifics<>();
}
}
}
// End AbstractBaseGraph.java