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;
@Deprecated
private EdgeFactory edgeFactory;
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);
this.edgeFactory = new BackwardsCompatibleEdgeFactory(null);
}
/**
* Construct a new graph. The graph can either be directed or undirected, depending on the
* specified edge factory.
*
* @param ef the edge factory of the new graph.
* @param directed if true the graph will be directed, otherwise undirected
* @param allowMultipleEdges whether to allow multiple (parallel) edges or not.
* @param allowLoops whether to allow edges that are self-loops or not.
* @param weighted whether the graph is weighted, i.e. the edges support a weight attribute
*
* @throws NullPointerException if the specified edge factory is
* null
.
* @deprecated Use suppliers instead
*/
@Deprecated
protected AbstractBaseGraph(
EdgeFactory ef, boolean directed, boolean allowMultipleEdges, boolean allowLoops,
boolean weighted)
{
this(
null, null,
new DefaultGraphType.Builder(directed, !directed)
.allowMultipleEdges(allowMultipleEdges).allowSelfLoops(allowLoops)
.weighted(weighted).build());
this.edgeFactory = new BackwardsCompatibleEdgeFactory(ef);
}
/**
* {@inheritDoc}
*/
@Override
public Set getAllEdges(V sourceVertex, V targetVertex)
{
return specifics.getAllEdges(sourceVertex, targetVertex);
}
/**
* Returns true
if and only if self-loops are allowed in this graph. A self loop is
* an edge that its source and target vertices are the same.
*
* @return true
if and only if graph loops are allowed.
* @deprecated Use type instead
*/
@Deprecated
public boolean isAllowingLoops()
{
return type.isAllowingSelfLoops();
}
/**
* Returns true
if and only if multiple (parallel) edges are allowed in this graph.
* The meaning of multiple edges is that there can be many edges going from vertex v1 to vertex
* v2.
*
* @return true
if and only if multiple (parallel) edges are allowed.
* @deprecated Use type instead
*/
@Deprecated
public boolean isAllowingMultipleEdges()
{
return type.isAllowingMultipleEdges();
}
/**
* Returns true
if and only if the graph supports edge weights.
*
* @return true
if the graph supports edge weights, false
otherwise.
* @deprecated Use type instead
*/
@Deprecated
public boolean isWeighted()
{
return type.isWeighted();
}
/**
* Returns true
if the graph is directed, false if undirected.
*
* @return true
if the graph is directed, false if undirected.
* @deprecated Use type instead
*/
@Deprecated
public boolean isDirected()
{
return type.isDirected();
}
/**
* {@inheritDoc}
*
* @deprecated Use supplier instead
*/
@Deprecated
@Override
public EdgeFactory getEdgeFactory()
{
return edgeFactory;
}
@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);
}
E e = edgeFactory.createEdge(sourceVertex, targetVertex);
//@formatter:off
/*
* After next release, replace above call with code below:
*
* if (edgeSupplier == null) {
* throw new UnsupportedOperationException("The graph contains no edge supplier");
* }
*
* E e = edgeSupplier.get();
*/
//@formatter:on
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.edgeFactory = this.edgeFactory;
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<>();
}
}
@Deprecated
private class BackwardsCompatibleEdgeFactory
implements
EdgeFactory,
Serializable
{
private EdgeFactory ef;
public BackwardsCompatibleEdgeFactory(EdgeFactory ef)
{
this.ef = ef;
}
@Override
public E createEdge(V sourceVertex, V targetVertex)
{
if (edgeSupplier == null && this.ef == null) {
throw new UnsupportedOperationException("The graph contains no edge supplier");
}
if (edgeSupplier != null) {
return edgeSupplier.get();
} else {
return ef.createEdge(sourceVertex, targetVertex);
}
}
}
}
// End AbstractBaseGraph.java