Refactoring

* package refactoring
* new matrix implementation (todo)
* new adj list implemetation (todo)
* fixed tests
This commit is contained in:
2019-06-15 15:15:13 +02:00
parent a8596331e7
commit 0de35fd4a1
35 changed files with 772 additions and 118 deletions

View File

@@ -0,0 +1,83 @@
package berack96.lib.graph;
/**
* Class used for retrieving the edges of the graph.
*
* @param <V> the vertices
* @param <W> the weight of the edge
* @author Berack96
*/
public class Edge<V, W extends Number> {
/**
* The source vertex
*/
private final V source;
/**
* The destination vertex
*/
private final V destination;
/**
* The weight of this edge
*/
private final W weight;
/**
* Create an final version of this object
*
* @param source the source of the edge
* @param destination the destination of the edge
* @param weight the weight of the edge
*/
public Edge(V source, V destination, W weight) {
this.source = source;
this.destination = destination;
this.weight = weight;
}
/**
* The vertex where the edge goes
*
* @return the vertex
*/
public V getDestination() {
return destination;
}
/**
* The vertex where the edge starts from
*
* @return the vertex
*/
public V getSource() {
return source;
}
/**
* The weight of the edge
*
* @return the weight
*/
public W getWeight() {
return weight;
}
@Override
public String toString() {
return "[" + source + " -> " + destination + ", " + weight + "]";
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
try {
return obj.getClass().equals(getClass()) && obj.toString().equals(toString());
} catch (Exception e) {
return false;
}
}
}

View File

@@ -0,0 +1,639 @@
package berack96.lib.graph;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import berack96.lib.graph.models.GraphSaveStructure;
import berack96.lib.graph.visit.VisitStrategy;
import berack96.lib.graph.visit.impl.VisitInfo;
/**
* An interface for the graphs.<br>
* This interface is used for the graphs with Directed edges.<br>
* A directed edge between V1 and V2 is an edge that has V1 as source and V2 as destination.<br>
*
* @param <V> The Object that represent a vertex
* @param <W> The Object that represent the edge (more specifically the weight of the edge)
* @author Berack96
*/
public interface Graph<V, W extends Number> extends Iterable<V> {
final String NOT_DAG = "The graph is not a DAG";
final String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination";
final String PARAM_NULL = "The parameter must not be null";
final String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph";
final Gson GSON = new Gson();
/**
* Tells if the graph has some cycle.<br>
* A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G),
* the visit can return to V1 in any point.
*
* @return true if has cycle, false otherwise
*/
boolean isCyclic();
/**
* Tells if the graph has the property of DAG (Directed Acyclic Graph).<br>
* A graph is a DAG only if absent of any cycle. ( see {@link #isCyclic()} )
*
* @return true if is a DAG, false otherwise
*/
boolean isDAG();
/**
* Get an instance of the vertex linked with this graph.<br>
* For more info see {@link Vertex}
*
* @param vertex the vertex
* @return a vertex
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Add the vertex to the graph. If it's already in the graph it will be replaced.<br>
* Of course the vertex added will have no edge to any other vertex nor form any other vertex.
*
* @param vertex the vertex to add
* @throws NullPointerException if the vertex is null
*/
void addVertex(V vertex) throws NullPointerException;
/**
* Add the specified vertex to the graph only if the graph doesn't contains it.<br>
* The graph contains a vertex only if the method {@link #contains(V)} returns true.
*
* @param vertex the vertex to add
* @return true if the vertex is added, false if the graph contains the vertex and therefore the new one is not added
* @throws NullPointerException if the vertex is null
*/
boolean addVertexIfAbsent(V vertex) throws NullPointerException;
/**
* Add all the vertices contained in the collection to the graph.<br>
* If a vertex is contained in the collection and in the graph is ignored and it will not be replaced.<br>
* Null vertices will be ignored and they will not be added to the graph.
*
* @param vertices a collection of the vertices to add
* @throws NullPointerException if the set is null
*/
void addAllVertices(Collection<V> vertices) throws NullPointerException;
/**
* Remove the selected vertex from the graph.<br>
* After this method's call the vertex will be no longer present in the graph, and nether all his edges.
*
* @param vertex the vertex to remove
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained
*/
void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Remove all the vertex contained in the graph.<br>
* After this method's call the graph will be empty; no vertices nor edges.
*/
void removeAllVertex();
/**
* Check if the vertex passed is contained in the graph or not.<br>
* The vertex V1 is contained in the graph G, if and only if:<br>
* exist V2 in G such that V2.equals(V1)
*
* @param vertex the vertex to check
* @return true if the vertex is contained, false otherwise
* @throws NullPointerException if the vertex is null
*/
boolean contains(V vertex) throws NullPointerException;
/**
* Add to the specified vertex the mark passed.<br>
* A vertex can have multiple marker.
*
* @param vertex the vertex to mark
* @param mark the mark to add
* @throws NullPointerException if one of the param is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException;
/**
* Remove the selected mark from the vertex.<br>
*
* @param vertex the vertex where remove the mark
* @param mark the mark to remove
* @throws NullPointerException if a param is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException;
/**
* Unmark the vertex selected.<br>
* After this call the vertex will not have any marked object to himself.
*
* @param vertex the vertex
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
void unMark(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Get all the vertices that are marked with the specific mark passed.<br>
* If there aren't vertices with that mark then it is returned an empty set.<br>
* Note: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param mark the mark
* @return all the vertices that are marked with that specific mark
* @throws NullPointerException if the mark is null
*/
Collection<V> getMarkedWith(Object mark) throws NullPointerException;
/**
* Get all the marker of this vertex.<br>
* If the vertex doesn't have any mark, then it will return an empty set.<br>
* Note: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param vertex the vertex
* @return all the mark to the vertex or an empty collection if none
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
Collection<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Remove the selected mark from all the vertices
*
* @param mark the mark to remove
* @throws NullPointerException if the mark is null
*/
void unMarkAll(Object mark) throws NullPointerException;
/**
* Remove all the marker to all the vertex.<br>
* After this call the {@link #getMarks(Object)} applied to any vertex will return an empty set
*/
void unMarkAll();
/**
* Add an edge between the two vertex.<br>
* The edge will be created from the vertex V1 and the vertex V2<br>
* This method will overwrite any existing edge between the two vertex.<br>
* If there was a previous edge then it is returned
*
* @param vertex1 a vertex of the graph
* @param vertex2 a vertex of the graph
* @param weight the weight of the edge
* @return null or the previous weight of the edge if there was already one
* @throws NullPointerException if one of the parameter is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException;
/**
* Add an edge between the two vertex.<br>
* The edge will be created from the vertex source of the edge and the vertex destination of it<br>
* This method will overwrite any existing edge between the two vertex.<br>
* If there was a previous edge then it is returned
*
* @param edge the edge to add
* @return null or the previous weight of the edge if there was already one
* @throws NullPointerException if one of the parameter is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException;
/**
* This particular function add an edge to the graph.<br>
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.<br>
* The edge will be created from the vertex V1 and the vertex V2<br>
* This method will overwrite any existing edge between the two vertex.<br>
* If there was a previous edge then it is returned
*
* @param vertex1 a vertex of the graph
* @param vertex2 a vertex of the graph
* @param weight the weight of the edge
* @return null or the previous weight of the edge if there was already one
* @throws NullPointerException if one of the parameter is null
*/
W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException;
/**
* This particular function add an edge to the graph.<br>
* If one of the two, or both vertices of the edge aren't contained in the graph, then the vertices will be added.<br>
* The edge will be created from the vertex source of the edge and the vertex destination of it<br>
* This method will overwrite any existing edge between the two vertex.<br>
* If there was a previous edge then it is returned
*
* @param edge the edge to add
* @return null or the previous weight of the edge if there was already one
* @throws NullPointerException if one of the parameter is null
*/
W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException;
/**
* Add all the edges of the collection to the graph.<br>
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.<br>
* Any null edges will be ignored.<br>
* This method will overwrite any existing edge between the two vertex.
*
* @param edges the edges to add
* @throws NullPointerException if the set is null
*/
void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException;
/**
* Get the weight of the selected edge.<br>
* If the edge doesn't exist, then null is returned
*
* @param vertex1 a vertex of the graph
* @param vertex2 a vertex of the graph
* @return the weight previously set, or null if the edge doesn't exist
* @throws NullPointerException if one of the parameters is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException;
/**
* Remove the edge between the two vertex.<br>
* If the edge doesn't exist, then this call does nothing.<br>
* After this method's call it will be no longer possible to travel from V1 to V2, nether from V2 to V1.
*
* @param vertex1 a vertex of the graph
* @param vertex2 a vertex of the graph
* @throws NullPointerException if one of the parameters is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException;
/**
* Remove all the edges that goes in the vertex.<br>
* After this method's call it will be no longer possible travel to this vertex.
*
* @param vertex a vertex of the graph
* @throws NullPointerException if one of the parameters is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Remove all the edges that start from this vertex.<br>
* After this method's call it will be no longer possible travel to any vertex from this one.
*
* @param vertex a vertex of the graph
* @throws NullPointerException if one of the parameters is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Remove all edges form a particular vertex of the graph.<br>
* After this method's call the selected vertex will have 0 edges.<br>
* It will be no longer possible to reach this vertex from any other vertex, and vice versa.
*
* @param vertex a vertex of the graph
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Remove all the edges of the graph.<br>
* After this method's call the graph will have only vertices, and no edge.
*/
void removeAllEdge();
/**
* Check if the edge between the two vertex passed is contained in the graph or not.<br>
* An edge between V1 and V2 is contained in the graph if and only if i can travel from V1 to V2.<br>
* If one of the two vertices is not contained in the graph, then even the edge isn't
*
* @param vertex1 a vertex of the graph
* @param vertex2 a vertex of the graph
* @return true if the edge is contained, false otherwise
* @throws NullPointerException if one of the parameters is null
*/
boolean containsEdge(V vertex1, V vertex2) throws NullPointerException;
/**
* Get all the vertices in the graph.<br>
* If the graph doesn't contains vertices, it'll return an empty collection.<br>
* Note: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @return an array that include all the vertices
*/
Collection<V> vertices();
/**
* Get all the edges in the graph.<br>
* If the graph doesn't contains edges, it'll return an empty collection.<br>
* Note: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @return a collection that include all the edges
*/
Collection<Edge<V, W>> edges();
/**
* Retrieve all the edges of a particular vertex.<br>
* Note: the edges that are returned are the one that goes IN this vertex AND the edges that goes OUT of it.<br>
* Note2: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param vertex a vertex of the graph
* @return a collection of edges
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
Collection<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Retrieve all the edges of a particular vertex.<br>
* Note: the edges that are returned are the one that have this vertex as destination and another as source.<br>
* Note2: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param vertex a vertex of the graph
* @return a collection of edges
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Retrieve all the edges that goes OUT of a particular vertex.<br>
* Note: the edges that are returned are the one that have this vertex as source and another one as destination.<br>
* Note2: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param vertex a vertex of the graph
* @return a collection of edges
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Get all the vertices that are children of the vertex passed as parameter.<br>
* The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge
* where V1 is the source of that edge.<br>
* Note: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param vertex the source vertex
* @return an array of vertices
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
Collection<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Get all the vertices that have the vertex passed as their child.<br>
* Basically is the opposite of {@link #getChildren(Object)}<br>
* Note: depending on the implementation, modifying the returned collection<br>
* could affect the graph behavior and the changes could be reflected to the graph.
*
* @param vertex a vertex of the graph
* @return an array of ancestors of the vertex
* @throws NullPointerException if one of the parameters is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
Collection<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Tells the degree of all the edges that goes to this vertex.<br>
* Basically, it'll count how many edge towards himself it have.
*
* @param vertex a vertex of the graph
* @return the in degree of the vertex
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Tells the degree of all the edges that goes form this vertex to others.<br>
* Basically, it'll count how many edge towards any other vertex it have.
*
* @param vertex a vertex of the graph
* @return the out degree of the vertex
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Tells the degree of a vertex.<br>
* The degree of a vertex is the quantity of edges that have.<br>
* Basically, it'll count how many edge it have.
*
* @param vertex a vertex of the graph
* @return the degree of the vertex
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained in the graph
*/
int degree(V vertex) throws NullPointerException, IllegalArgumentException;
/**
* Tells how many vertices are in the graph.
*
* @return the number of vertices
*/
int numberOfVertices();
/**
* Tells how many edges are in the graph.
*
* @return the number of edges
*/
int numberOfEdges();
/**
* Visit the graph accordingly to the strategy that is passed.<br>
* This method visit the graph from the source to all the vertex that are reachable form the source.<br>
* Some strategy can accept a source vertex null, because they visit all the graph anyway.
*
* @param source the source vertex of the visit
* @param strategy the algorithm for visiting the graph
* @param visit the function to apply at each vertex
* @throws NullPointerException if one of the parameter is null (except the consumer)
* @throws IllegalArgumentException if the vertex is not in the graph
*/
VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException;
/**
* This method will create a new Graph that is the transposed version of the original.<br>
* At the end of this method the new graph will have all the edges inverted in orientation.<br>
* Example: if the graph G contains (V1, V2, V3) as vertex, and (V1->V2, V3->V2) as edges,
* the transpose graph G' will contain (V1, V2, V3) as vertex, and (V2->V1, V2->V3) as edges.
*
* @return a transposed graph of this instance
*/
Graph<V, W> transpose();
/**
* If the current graph is a DAG, it returns a topological sort of this graph.<br>
* A topological ordering of a graph is a linear ordering of its vertices such that for
* every directed edge (V1, V2) from vertex V1 to vertex V2, V2 comes before V1 in the ordering.
*
* @return an array containing the topological order of the vertices
* @throws UnsupportedOperationException if the graph is not a DAG (see {@link #isDAG()})
*/
List<V> topologicalSort() throws UnsupportedOperationException;
/**
* The strongly connected components or disconnected components of an arbitrary directed graph
* form a partition into subgraphs that are themselves strongly connected.
*
* @return a collection containing the strongly connected components
*/
Collection<Collection<V>> stronglyConnectedComponents();
/**
* Get a sub-graph of the current one based on the maximum depth that is given.<br>
* If the depth is 1 then only the source and it's children will be in the sub-graph.<br>
* If the depth is 2 then only the source, it's children and it's children of it's children will be in the sub-graph.<br>
* And so on.<br>
* Of course the sub-graph will contain the edges that link the vertices, but only the one selected.
*
* @param source the source vertex
* @param depth the maximum depth (must be a positive number, if >=0 a graph containing only the source is returned)
* @return a sub-graph of the original
* @throws NullPointerException if the vertex is null
* @throws IllegalArgumentException if the vertex is not contained
*/
Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException;
/**
* Get a sub-graph of the current one with only the vertex marked with the selected markers.<br>
* Each vertex will have all his edges, but only the ones with the destination marked with the same marker.<br>
* If the marker is not specified or is null then the returning graph will have all the vertices that are not marked by any marker.<br>
* If the graph doesn't contain any vertex with that marker then an empty graph is returned.
*
* @param marker one or more markers
* @return a sub-graph of the current graph
*/
Graph<V, W> subGraph(Object...marker);
/**
* Get the minimum path from the source vertex to the destination vertex.<br>
* If the source vertex can't reach the destination, then an exception is thrown.
*
* @param source the vertex where to start
* @param destination the destination chosen
* @return an ordered list of edges from source to destination that represent the minimum path between the two vertices
* @throws NullPointerException if one of the parameter is null (except the consumer)
* @throws IllegalArgumentException if the vertex is not in the graph
* @throws UnsupportedOperationException if from the source it's not possible to reach the destination
*/
List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException;
/**
* Get the minimum path from the source vertex to all the possible reachable vertices.
*
* @param source the vertex where to start
* @return a map containing all the possible reachable vertices from the source and the minimum path to reach them
* @throws NullPointerException if one of the parameter is null (except the consumer)
* @throws IllegalArgumentException if the vertex is not in the graph
*/
Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException;
/**
* Save the Graph passed as input to a file inserted as parameter.<br>
* The resulting file is a Json string representing all the graph.<br>
* If the directory for getting through the file do not exist,<br>
* then it is created.
*
* @param graph the graph to save
* @param file the name of the file
* @throws IOException for various reason that appear in the message, but the most common is that the file is not found.
*/
static void save(Graph<?, ?> graph, String file) throws IOException {
save(graph, "", file);
}
/**
* Save the Graph passed as input to a file inserted as parameter.<br>
* The resulting file is a Json string representing all the graph.<br>
* If the directory for getting through the file do not exist,<br>
* then it is created.<br>
* The additional parameter is used if you want to save other as well as the graph.
*
* @param graph the graph to save
* @param other other things to save
* @param file the name of the file
* @throws IOException for various reason that appear in the message, but the most common is that the file is not found.
*/
static void save(Graph<?, ?> graph, String other, String file) throws IOException {
GraphSaveStructure save = new GraphSaveStructure(graph, other);
int slash = file.lastIndexOf("\\");
if(slash == -1)
slash = file.lastIndexOf("/");
if(slash != -1) {
String dir = file.substring(0, slash);
File fDir = new File(dir);
fDir.mkdirs();
}
FileWriter writer = new FileWriter(file);
GSON.toJson(save, writer);
writer.close();
}
/**
* Load an already saved graph in an instance of a graph.
* Before loading the graph, it is emptied.
*
* @param <V> the parameter needed for the vertex
* @param <W> the parameter needed for the weight
* @param graph the graph to load with
* @param file the file where the graph is saved
* @param classV the class used for the Vertex
* @param classW the class used for the Weight
* @return the string saved in other, if any
* @throws IOException for any possible reason, the most common: the file doesn't exist
* @throws NullPointerException if the graph is null
* @throws JsonSyntaxException if the file is malformed or corrupted
*/
static <V, W extends Number> String load(Graph<V, W> graph, String file, Class<V> classV, Class<W> classW) throws IOException, NullPointerException, JsonSyntaxException {
FileReader reader = new FileReader(file);
StringBuilder fileContent = new StringBuilder();
int c;
while((c = reader.read()) != -1)
fileContent.append((char)c);
reader.close();
GraphSaveStructure save = GSON.fromJson(fileContent.toString(), GraphSaveStructure.class);
graph.removeAllVertex();
for(String str : save.vertices)
graph.addVertex(GSON.fromJson(str, classV));
for(int i = 0; i<save.edges.length; i++)
graph.addEdge(
GSON.fromJson(save.edges[i].src, classV),
GSON.fromJson(save.edges[i].dest, classV),
GSON.fromJson(save.edges[i].weight, classW));
/*
for(int i = 0; i<save.marks.length; i++)
graph.mark(GSON.fromJson(save.marks[i].vert, classV), save.marks[i].mark);
*/
return save.other;
}
// TODO maybe, but i don't think so... STATIC DISTANCE V* -> V*
}

View File

@@ -0,0 +1,276 @@
package berack96.lib.graph;
import java.util.Collection;
import java.util.HashSet;
import java.util.function.Consumer;
import berack96.lib.graph.visit.VisitStrategy;
import berack96.lib.graph.visit.impl.VisitInfo;
/**
* Class used for represent a vertex of the graph.<br>
* The vertex contained is linked with the graph, so if any changes are made to
* it, then they will be reflected here.
*
* @param <V> the vertex
* @author Berack96
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class Vertex<V> {
public static final String REMOVED = "The vertex is no longer in the graph";
/**
* The vertex associated
*/
private final V vertex;
/**
* The graph associated
*/
private final Graph<V, Number> graph;
/**
* Get a Vertex linked with the graph
*
* @param graph the graph of the vertex
* @param vertex the vertex
* @throws NullPointerException if one of the param is null
*/
public Vertex(Graph<V, ?> graph, V vertex) throws NullPointerException {
if (graph == null || vertex == null)
throw new NullPointerException();
this.graph = (Graph<V, Number>) graph;
this.vertex = vertex;
}
/**
* Get the vertex
*
* @return the vertex
*/
public V getValue() {
return vertex;
}
/**
* Mark the vertex with the associated string
*
* @param mark the marker
* @throws NullPointerException if the marker is null
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public void mark(Object mark) throws NullPointerException, UnsupportedOperationException {
throwIfNotContained();
graph.mark(vertex, mark);
}
/**
* Remove the specified mark from this vertex
*
* @param mark the marker
* @throws NullPointerException if the mark is null
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public void unMark(Object mark) throws UnsupportedOperationException {
throwIfNotContained();
graph.unMark(vertex, mark);
}
/**
* Remove all the marker from the vertex
*
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public void unMark() throws UnsupportedOperationException {
throwIfNotContained();
graph.unMark(vertex);
}
/**
* Get all the marks that are associated with this vertex
*
* @return a set of marks
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<Object> getMarks() throws UnsupportedOperationException {
throwIfNotContained();
return graph.getMarks(vertex);
}
/**
* Get all the vertex children of the current vertex
*
* @return all the children
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<V> getChildren() throws UnsupportedOperationException {
throwIfNotContained();
return graph.getChildren(vertex);
}
/**
* Get all the children of this vertex like {@link #getChildren()}, but as {@link Vertex}.<br>
* In this way they are linked to the graph as this one.<br>
* * This method allocate a new object for each vertex, so it is more heavy.
*
* @return a collection of vertices that are children of the current one
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<Vertex<V>> getChildrenAsVertex() throws UnsupportedOperationException {
Collection<V> children = getChildren();
Collection<Vertex<V>> toReturn = new HashSet<>();
for (V vertex : children)
toReturn.add(new Vertex<>(graph, vertex));
return toReturn;
}
/**
* Get all the vertex ancestor of this vertex.<br>
* The ancestors are all the vertices that have as destination this vertex.
*
* @return a collection of vertices
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<V> getAncestors() throws UnsupportedOperationException {
throwIfNotContained();
return graph.getAncestors(vertex);
}
/**
* Get all the ancestors of this vertex like {@link #getAncestors()}, but as {@link Vertex}.<br>
* In this way they are linked to the graph as this one.<br>
* This method allocate a new object for each vertex, so it is more heavy.
*
* @return a collection of vertices that are children of the current one
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<Vertex<V>> getAncestorsAsVertex() throws UnsupportedOperationException {
Collection<V> ancestors = getAncestors();
Collection<Vertex<V>> toReturn = new HashSet<>();
for (V vertex : ancestors)
toReturn.add(new Vertex<>(graph, vertex));
return toReturn;
}
/**
* Get all the edge that goes OUT of this vertex
*
* @return a collection of edges with source this one
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<Edge<V, Number>> getEdgesOut() throws UnsupportedOperationException {
throwIfNotContained();
return graph.getEdgesOut(vertex);
}
/**
* Get all the edge that goes INTO this vertex
*
* @return a collection of edges with destination this one
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public Collection<Edge<V, Number>> getEdgesIn() throws UnsupportedOperationException {
throwIfNotContained();
return graph.getEdgesIn(vertex);
}
/**
* Add a child to this vertex.<br>
* The added child must be in the graph or it will return an exception.
*
* @param child the destination vertex of this edge
* @param weight the weight of the edge
* @throws NullPointerException if the param is null
* @throws IllegalArgumentException if the child vertex is not contained in the graph
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public void addChild(V child, Number weight) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
throwIfNotContained();
graph.addEdge(vertex, child, weight);
}
/**
* Removes a child of this vertex.
* If the vertex passed as param is not a child, then this call does nothing.
*
* @param child the child of the current vertex
* @throws NullPointerException if the param is null
* @throws IllegalArgumentException if the child vertex is not contained in the graph
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public void removeChild(V child) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
throwIfNotContained();
graph.removeEdge(vertex, child);
}
/**
* This call tell if the current vertex is still contained in the graph linked.<br>
* While this function return false all the other methods will throw an exception.
*
* @return true if it is, false otherwise
*/
public boolean isStillContained() {
return graph.contains(vertex);
}
/**
* Add the vertex to the graph only if it's not already in the graph.
*/
public void addIfAbsent() {
graph.addVertexIfAbsent(vertex);
}
/**
* Remove the vertex from the graph.<br>
* After this call all the other methods will throw an exception
*/
public void remove() {
if (graph.contains(vertex))
graph.removeVertex(vertex);
}
/**
* Visit the graph from this current vertex with the strategy assigned
*
* @param strategy the strategy of the visit
* @param visit the function to apply at each vertex (can be null)
* @return an info of the visit if supported by the strategy
* @throws NullPointerException if the strategy is null
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
*/
public VisitInfo<V> visit(final VisitStrategy strategy, final Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
throwIfNotContained();
return graph.visit(vertex, (VisitStrategy<V, Number>) strategy, visit);
}
@Override
public String toString() {
return vertex.toString();
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
try {
return obj instanceof Vertex && (this.vertex.equals(obj) || this.vertex.equals(((Vertex) obj).vertex));
} catch (Exception e) {
return false;
}
}
/**
* Used for throwing the UnsupportedOperationException if the vertex is not contained anymore
*
* @throws UnsupportedOperationException if IllegalArgumentException is thrown by the runnable
*/
private void throwIfNotContained() throws UnsupportedOperationException {
if (!graph.contains(vertex))
throw new UnsupportedOperationException(REMOVED);
}
}

View File

@@ -0,0 +1,313 @@
package berack96.lib.graph.impl;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
import berack96.lib.graph.Vertex;
import berack96.lib.graph.visit.VisitStrategy;
import berack96.lib.graph.visit.impl.VisitInfo;
public class AdjGraph<V, W extends Number> implements Graph<V, W> {
@Override
public Iterator<V> iterator() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isCyclic() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isDAG() {
// TODO Auto-generated method stub
return false;
}
@Override
public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void addVertex(V vertex) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
// TODO Auto-generated method stub
return false;
}
@Override
public void addAllVertices(Collection<V> vertices) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public void removeVertex(V vertex) throws IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllVertex() {
// TODO Auto-generated method stub
}
@Override
public boolean contains(V vertex) throws NullPointerException {
// TODO Auto-generated method stub
return false;
}
@Override
public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public Collection<V> getMarkedWith(Object mark) throws NullPointerException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void unMarkAll(Object mark) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public void unMarkAll() {
// TODO Auto-generated method stub
}
@Override
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
// TODO Auto-generated method stub
return null;
}
@Override
public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllEdge() {
// TODO Auto-generated method stub
}
@Override
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
// TODO Auto-generated method stub
return false;
}
@Override
public Collection<V> vertices() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> edges() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int numberOfVertices() {
// TODO Auto-generated method stub
return 0;
}
@Override
public int numberOfEdges() {
// TODO Auto-generated method stub
return 0;
}
@Override
public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit)
throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Graph<V, W> transpose() {
// TODO Auto-generated method stub
return null;
}
@Override
public List<V> topologicalSort() throws UnsupportedOperationException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Collection<V>> stronglyConnectedComponents() {
// TODO Auto-generated method stub
return null;
}
@Override
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Graph<V, W> subGraph(Object... marker) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Edge<V, W>> distance(V source, V destination)
throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -0,0 +1,533 @@
package berack96.lib.graph.impl;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
import berack96.lib.graph.Vertex;
import berack96.lib.graph.visit.VisitStrategy;
import berack96.lib.graph.visit.impl.Dijkstra;
import berack96.lib.graph.visit.impl.Tarjan;
import berack96.lib.graph.visit.impl.VisitInfo;
/**
* Graph that uses HashMap for vertices and edges<br>
* More specifically it utilizes a Map containing all the vertices mapped to all their edges<br>
* Technically this version of the graph combine the fast adding/removing of the edges of the Matrix implementation,
* with the low memory and fast adding/removing of vertices of the Linked List implementation.<br>
* This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n)
*
* @param <V> the vertices
* @param <W> the weight of the edges
* @author Berack96
*/
public class MapGraph<V, W extends Number> implements Graph<V, W> {
/**
* Map that contains the edges from a vertex to another<br>
* The first vertex is the vertex where start the edge, the second one is where the edge goes<br>
* If an edge exist, then it's weight is returned
*/
private final Map<V, Map<V, W>> edges = new HashMap<>();
/**
* Map that contains the marker as key and a set of all the vertices that has it as the value.<br>
* This map is build like this for performance in creating the marker for multiple vertices.<br>
* If you flip the parameters (object and set) then has more performance over the single vertex.
*/
private final Map<Object, Set<V>> markers = new HashMap<>();
/**
* Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change
*/
private Tarjan<V, W> tarjan = null;
/**
* Need this variable for not calculating each time the distance from a vertex to all his destinations if the graph doesn't change
*/
private Map<V, Dijkstra<V, W>> dijkstra = new HashMap<>();
@Override
public boolean isCyclic() {
return stronglyConnectedComponents().size() != numberOfVertices();
}
@Override
public boolean isDAG() {
return !isCyclic();
}
@Override
public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
return new Vertex<>(this, vertex);
}
@Override
public void addVertex(V vertex) throws NullPointerException {
checkNull(vertex);
edges.put(vertex, new HashMap<>());
graphChanged();
}
@Override
public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
if (contains(vertex))
return false;
addVertex(vertex);
return true;
}
@Override
public void addAllVertices(Collection<V> vertices) throws NullPointerException {
checkNull(vertices);
vertices.forEach(this::addVertexIfAbsent);
}
@Override
public void removeVertex(V vertex) throws NullPointerException {
if (contains(vertex)) {
edges.remove(vertex);
edges.forEach((v, map) -> map.remove(vertex));
markers.forEach((mark, set) -> set.remove(vertex));
graphChanged();
}
}
@Override
public void removeAllVertex() {
edges.clear();
markers.clear();
graphChanged();
}
@Override
public boolean contains(V vertex) throws NullPointerException {
checkNull(vertex);
return edges.containsKey(vertex);
}
@Override
public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
checkNull(mark);
Set<V> set = markers.computeIfAbsent(mark, (v) -> new HashSet<>());
set.add(vertex);
}
@Override
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
checkNull(mark);
markers.get(mark).remove(vertex);
}
@Override
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
markers.forEach( (mark, set) -> set.remove(vertex) );
}
@Override
public Collection<V> getMarkedWith(Object mark) throws NullPointerException {
checkNull(mark);
return markers.computeIfAbsent(mark, (v) -> new HashSet<>());
}
@Override
public Collection<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Collection<Object> marks = new HashSet<>();
markers.forEach( (mark, set) -> {
if (set.contains(vertex))
marks.add(mark);
});
return marks;
}
@Override
public void unMarkAll(Object mark) {
checkNull(mark);
markers.remove(mark);
}
@Override
public void unMarkAll() {
markers.clear();
}
@Override
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex1);
checkNullAndExist(vertex2);
checkNull(weight);
W old = edges.get(vertex1).put(vertex2, weight);
graphChanged();
return old;
}
@Override
public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight());
}
@Override
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
addVertexIfAbsent(vertex1);
addVertexIfAbsent(vertex2);
return addEdge(vertex1, vertex2, weight);
}
@Override
public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight());
}
@Override
public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException {
edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()));
}
@Override
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex1);
checkNullAndExist(vertex2);
return edges.get(vertex1).get(vertex2);
}
@Override
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex1);
checkNullAndExist(vertex2);
edges.get(vertex1).remove(vertex2);
graphChanged();
}
@Override
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
edges.forEach((v, map) -> map.remove(vertex));
graphChanged();
}
@Override
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
edges.put(vertex, new HashMap<>());
graphChanged();
}
@Override
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
removeVertex(vertex);
addVertex(vertex);
}
@Override
public void removeAllEdge() {
edges.forEach((v, map) -> map.clear());
graphChanged();
}
@Override
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null;
}
@Override
public Collection<V> vertices() {
return new HashSet<>(edges.keySet());
}
@Override
public Collection<Edge<V, W>> edges() {
Set<Edge<V, W>> allEdges = new HashSet<>();
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
return allEdges;
}
@Override
public Collection<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Set<Edge<V, W>> set = new HashSet<>();
edges.forEach((source, map) -> map.forEach((destination, weight) -> {
if (destination.equals(vertex) || source.equals(vertex))
set.add(new Edge<>(source, destination, weight));
}));
return set;
}
@Override
public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Collection<Edge<V, W>> collection = new HashSet<>();
edges.forEach((source, edge) -> {
if (edge.get(vertex) != null)
collection.add(new Edge<>(source, vertex, edge.get(vertex)));
});
return collection;
}
@Override
public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Collection<Edge<V, W>> collection = new HashSet<>();
edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight)));
return collection;
}
@Override
public Collection<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
return new HashSet<>(edges.get(vertex).keySet());
}
@Override
public Collection<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Set<V> set = new HashSet<>();
edges.forEach((v, map) -> {
if (map.containsKey(vertex)) set.add(v);
});
return set;
}
@Override
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
AtomicInteger sum = new AtomicInteger();
edges.forEach((v, map) -> {
if (map.containsKey(vertex))
sum.getAndIncrement();
});
return sum.get();
}
@Override
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
return edges.get(vertex).size();
}
@Override
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
return degreeIn(vertex) + degreeOut(vertex);
}
@Override
public int numberOfVertices() {
return edges.size();
}
@Override
public int numberOfEdges() {
AtomicInteger sum = new AtomicInteger(0);
edges.forEach((v, map) -> sum.getAndAdd(map.size()));
return sum.get();
}
@Override
public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
return strategy.visit(this, source, visit);
}
@Override
public Graph<V, W> transpose() {
Graph<V, W> graph = new MapGraph<>();
for (V vertex : edges.keySet())
graph.addVertex(vertex);
edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight)));
return graph;
}
@Override
public List<V> topologicalSort() throws UnsupportedOperationException {
if (!isDAG())
throw new UnsupportedOperationException(NOT_DAG);
return getTarjan().getTopologicalSort();
}
@Override
public Collection<Collection<V>> stronglyConnectedComponents() {
return getTarjan().getSCC();
}
@Override
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
Graph<V, W> sub = new MapGraph<>();
Set<V> vertices = new HashSet<>();
int finalDepth = depth > 0 ? depth : 0;
VisitStrategy<V, W> strategy = (graph, sourceVertex, visit) -> {
int currentDepth = 0;
final LinkedList<Map.Entry<V, Integer>> toVisitChildren = new LinkedList<>();
toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0));
vertices.add(source);
while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) {
final Map.Entry<V, Integer> current = toVisitChildren.removeFirst();
currentDepth = current.getValue() + 1;
final int finalCurrentDepth = currentDepth;
for (V child : graph.getChildren(current.getKey()))
if (!vertices.contains(child)) {
toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth));
vertices.add(child);
}
}
return null;
};
strategy.visit(this, source, null);
sub.addAllVertices(vertices);
for (V vertex : vertices)
getEdgesOut(vertex).forEach((edge) -> {
try {
sub.addEdge(edge);
} catch (Exception ignored) {
}
});
return sub;
}
@Override
public Graph<V, W> subGraph(final Object...marker) {
final Graph<V, W> sub = new MapGraph<>();
final Set<V> allVertices = new HashSet<>();
final Set<Object> allMarkers = new HashSet<>();
final boolean isEmpty = (marker == null || marker.length == 0);
if (!isEmpty)
for (Object mark: marker)
allMarkers.add(mark);
markers.forEach( (mark, set) -> {
if (isEmpty || allMarkers.contains(mark))
allVertices.addAll(set);
});
if (isEmpty) {
Collection<V> toAdd = vertices();
toAdd.removeAll(allVertices);
allVertices.clear();
allVertices.addAll(toAdd);
}
sub.addAllVertices(allVertices);
for (V vertex : sub.vertices())
edges.get(vertex).forEach( (dest, weight) -> {
try {
sub.addEdge(vertex, dest, weight);
} catch (Exception ignored) {}
});
return sub;
}
@Override
public List<Edge<V, W>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
checkNullAndExist(source);
checkNullAndExist(destination);
Dijkstra<V, W> dijkstra = getDijkstra(source); /* Cached */
List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination);
if (path == null)
throw new UnsupportedOperationException(NOT_CONNECTED);
return new ArrayList<>(path);
}
@Override
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(source);
return new HashMap<>(getDijkstra(source).getLastDistance()); /* Cached */
}
@Override
public Iterator<V> iterator() {
return edges.keySet().iterator();
}
/**
* Simple function that reset all the caching variables if the graph changed
*/
private void graphChanged() {
tarjan = null;
dijkstra.clear();
}
/**
* Simple function that return the result of the Dijkstra visit, with the starting point as source.<br>
* It also cache it, so multiple call will return always the same value unless the graph has changed.
* @param source the source of the visit
* @return the complete visit
*/
private Dijkstra<V, W> getDijkstra(V source) {
if (dijkstra.get(source) == null) {
Dijkstra<V, W> newDijkstra = new Dijkstra<>();
newDijkstra.visit(this, source, null);
dijkstra.put(source, newDijkstra);
}
return dijkstra.get(source);
}
/**
* Simple function that return the result of the Tarjan visit.<br>
* It also cache it, so multiple call will return always the same value unless the graph has changed.
* @return the tarjan visit
*/
private Tarjan<V, W> getTarjan() {
if (tarjan == null) {
tarjan = new Tarjan<>();
tarjan.visit(this, null, null);
}
return tarjan;
}
/**
* Test if the object passed is null.
* If it is throw an exception.
* @param object the object to test
*/
private void checkNull(Object object) {
if (object == null)
throw new NullPointerException(PARAM_NULL);
}
/**
* Check if the vertex passed is null and if exist in the graph.
* If not then throws eventual exception
* @param vertex the vertex to test
*/
private void checkNullAndExist(V vertex) {
checkNull(vertex);
if (!edges.containsKey(vertex))
throw new IllegalArgumentException(VERTEX_NOT_CONTAINED);
}
}

View File

@@ -0,0 +1,313 @@
package berack96.lib.graph.impl;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
import berack96.lib.graph.Vertex;
import berack96.lib.graph.visit.VisitStrategy;
import berack96.lib.graph.visit.impl.VisitInfo;
public class MatrixGraph<V, W extends Number> implements Graph<V, W> {
@Override
public Iterator<V> iterator() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isCyclic() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isDAG() {
// TODO Auto-generated method stub
return false;
}
@Override
public Vertex<V> getVertex(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void addVertex(V vertex) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public boolean addVertexIfAbsent(V vertex) throws NullPointerException {
// TODO Auto-generated method stub
return false;
}
@Override
public void addAllVertices(Collection<V> vertices) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public void removeVertex(V vertex) throws IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllVertex() {
// TODO Auto-generated method stub
}
@Override
public boolean contains(V vertex) throws NullPointerException {
// TODO Auto-generated method stub
return false;
}
@Override
public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public Collection<V> getMarkedWith(Object mark) throws NullPointerException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void unMarkAll(Object mark) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public void unMarkAll() {
// TODO Auto-generated method stub
}
@Override
public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public W addEdge(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException {
// TODO Auto-generated method stub
return null;
}
@Override
public W addEdgeAndVertices(Edge<V, W> edge) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void addAllEdges(Collection<Edge<V, W>> edges) throws NullPointerException {
// TODO Auto-generated method stub
}
@Override
public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
}
@Override
public void removeAllEdge() {
// TODO Auto-generated method stub
}
@Override
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
// TODO Auto-generated method stub
return false;
}
@Override
public Collection<V> vertices() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> edges() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Edge<V, W>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int numberOfVertices() {
// TODO Auto-generated method stub
return 0;
}
@Override
public int numberOfEdges() {
// TODO Auto-generated method stub
return 0;
}
@Override
public VisitInfo<V> visit(V source, VisitStrategy<V, W> strategy, Consumer<V> visit)
throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Graph<V, W> transpose() {
// TODO Auto-generated method stub
return null;
}
@Override
public List<V> topologicalSort() throws UnsupportedOperationException {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<Collection<V>> stronglyConnectedComponents() {
// TODO Auto-generated method stub
return null;
}
@Override
public Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
@Override
public Graph<V, W> subGraph(Object... marker) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Edge<V, W>> distance(V source, V destination)
throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -0,0 +1,20 @@
package berack96.lib.graph.models;
/**
* Support class used for saving a Graph in a file.
*
* @author Berack96
*
*/
public class EdgeSaveStructure {
public EdgeSaveStructure() {}
protected EdgeSaveStructure(String s, String d, String w) {
this.src = s;
this.dest = d;
this.weight = w;
}
public String src;
public String dest;
public String weight;
}

View File

@@ -0,0 +1,41 @@
package berack96.lib.graph.models;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
/**
* Support class used for saving a Graph in a file.
*
* @author Berack96
*
*/
public class GraphSaveStructure {
public GraphSaveStructure() {}
public GraphSaveStructure(Graph<?, ?> graph, String other) {
this.vertices = new String[graph.numberOfVertices()];
int i = 0;
for(Object o: graph.vertices()) {
this.vertices[i] = Graph.GSON.toJson(o);
i++;
}
this.edges = new EdgeSaveStructure[graph.numberOfEdges()];
i = 0;
for (Edge<?, ?> edge : graph.edges()) {
this.edges[i] = new EdgeSaveStructure(
Graph.GSON.toJson(edge.getSource()),
Graph.GSON.toJson(edge.getDestination()),
Graph.GSON.toJson(edge.getWeight())
);
i++;
}
this.other = other;
}
public String[] vertices;
public EdgeSaveStructure[] edges;
//public MarkSaveStructure[] marks;
public String other;
}

View File

@@ -0,0 +1,18 @@
package berack96.lib.graph.models;
/**
* Support class used for saving a Graph in a file.
*
* @author Berack96
*
*/
public class MarkSaveStructure {
public MarkSaveStructure() {}
protected MarkSaveStructure(String v, Object m) {
this.vert = v;
this.mark = m;
}
public String vert;
public Object mark;
}

View File

@@ -0,0 +1,214 @@
package berack96.lib.graph.view;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
import berack96.lib.graph.Graph;
import berack96.lib.graph.view.edge.EdgeListener;
import berack96.lib.graph.view.vertex.VertexListener;
import berack96.lib.graph.visit.VisitStrategy;
public class GraphInfo<V, W extends Number> extends JPanel {
private static final long serialVersionUID = 1L;
private final Map<String, VisitListener<V>> visits;
public GraphInfo(GraphPanel<V, W> graphPanel, VertexListener<V> vListener, EdgeListener<V, W> eListener, Set<VisitStrategy<V, W>> visits) {
this.visits = new HashMap<>();
/* ZERO (DESCRIPTION) */
JLabel listenerDescription = new JLabel();
JPanel panelDescription = new JPanel();
panelDescription.setOpaque(false);
panelDescription.add(listenerDescription);
/* FIRST (GRAPH INFO) */
JLabel vNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfVertices()));
JLabel eNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfEdges()));
JLabel gCyclic = new JLabel(String.valueOf(graphPanel.getGraph().isCyclic()));
List<Component> components = new LinkedList<>();
JLabel selected = new JLabel();
JComboBox<String> comboBox = new JComboBox<>();
comboBox.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
try {
String clazz = (String) e.getItem();
VisitListener<V> listener = this.visits.get(clazz);
selected.setText(listener != null? "visit":"nothing");
listenerDescription.setText(listener != null? listener.getDescription():"");
graphPanel.getGraph().unMarkAll();
graphPanel.repaint();
graphPanel.setGraphListener(listener);
} catch (Exception ignore) {}
}
});
comboBox.addItem("None");
for(VisitStrategy<V, W> strategy: visits) {
String clazz = strategy.getClass().getSimpleName();
VisitListener<V> visit = new VisitListener<>(graphPanel, strategy);
comboBox.addItem(clazz);
this.visits.put(clazz, visit);
}
components.add(new JLabel("Visit Strategy: "));
components.add(comboBox);
components.add(new JLabel("Selected modality: "));
components.add(selected);
components.add(new JLabel("Vertex Number: "));
components.add(vNumber);
components.add(new JLabel("Edge Number: "));
components.add(eNumber);
components.add(new JLabel("Is Cyclic: "));
components.add(gCyclic);
JPanel panelInfo = new JPanel();
panelInfo.setOpaque(false);
panelInfo.setBorder(BorderFactory.createLineBorder(Color.RED));
panelInfo.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelInfo::add);
components.clear();
/* SECOND (VERTEX) */
JLabel vVertex = new JLabel();
JLabel vEdgesNumber = new JLabel();
JLabel vEdgesInNumber = new JLabel();
JLabel vEdgesOutNumber = new JLabel();
JButton modVertex = new JButton("Modify Vertices");
modVertex.addActionListener(a -> {
comboBox.setSelectedIndex(0);
listenerDescription.setText(vListener.getDescription());
graphPanel.setGraphListener(vListener);
graphPanel.getGraph().unMarkAll();
graphPanel.repaint();
selected.setText("vertices");
});
JButton modEdge = new JButton("Modify Edges");
modEdge.addActionListener(a -> {
comboBox.setSelectedIndex(0);
listenerDescription.setText(eListener.getDescription());
graphPanel.setGraphListener(eListener);
graphPanel.getGraph().unMarkAll();
graphPanel.repaint();
selected.setText("edges");
});
components.add(modVertex);
components.add(modEdge);
components.add(new JLabel("Vertex name: "));
components.add(vVertex);
components.add(new JLabel("Edges: "));
components.add(vEdgesNumber);
components.add(new JLabel("Edges IN: "));
components.add(vEdgesInNumber);
components.add(new JLabel("Edges OUT: "));
components.add(vEdgesOutNumber);
JPanel panelVertex = new JPanel();
panelVertex.setOpaque(false);
panelVertex.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelVertex::add);
components.clear();
/* SAVE/LOAD errors */
JLabel textResult = new JLabel();
textResult.setForeground(Color.RED);
JPanel panelErrors = new JPanel();
panelErrors.setOpaque(false);
panelErrors.add(textResult);
/* SAVE/LOAD */
JTextField fileText = new JTextField();
JButton saveB = new JButton("Save");
saveB.addActionListener(a -> {
try {
graphPanel.save(fileText.getText());
textResult.setText("");
} catch (IOException e1) {
textResult.setText(e1.getMessage());
}
});
JButton loadB = new JButton("Load");
loadB.addActionListener(a -> {
try {
graphPanel.load(fileText.getText());
textResult.setText("");
} catch (IOException e1) {
textResult.setText(e1.getMessage());
}
});
components.add(new JLabel("File to save/load: "));
components.add(fileText);
components.add(saveB);
components.add(loadB);
JPanel panelSave = new JPanel();
panelSave.setOpaque(false);
panelSave.setBorder(BorderFactory.createLineBorder(Color.RED));
panelSave.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelSave::add);
components.clear();
/* ADDING COMPONENTS */
this.setBackground(Color.LIGHT_GRAY);
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.setOpaque(true);
this.setBorder(BorderFactory.createSoftBevelBorder(BevelBorder.RAISED, Color.GRAY, Color.DARK_GRAY));
this.add(panelDescription);
this.add(panelInfo);
this.add(panelVertex);
this.add(panelErrors);
this.add(panelSave);
/*this.add(panelSave);*/
modVertex.doClick();
graphPanel.addObserver((o, arg) -> {
Graph<V, W> graph = graphPanel.getGraph();
if(arg.equals(graph)) {
vNumber.setText(String.valueOf(graph.numberOfVertices()));
eNumber.setText(String.valueOf(graph.numberOfEdges()));
gCyclic.setText(String.valueOf(graph.isCyclic()));
/* There should be only one */
for(V v : graph.getMarkedWith("selected")) {
int inE = graph.getEdgesIn(v).size();
int outE = graph.getEdgesOut(v).size();
vEdgesInNumber.setText(String.valueOf(inE));
vEdgesOutNumber.setText(String.valueOf(outE));
vEdgesNumber.setText(String.valueOf(inE + outE));
vVertex.setText(v.toString());
}
}
});
}
}

View File

@@ -0,0 +1,27 @@
package berack96.lib.graph.view;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
/**
* An interface for creating a listener of the Graph.
*
* @author Berack96
*/
public interface GraphListener extends MouseListener, MouseMotionListener, KeyListener {
/**
* Remove the listener to the graph.
* This function is called when the listener is removed to the graph.
* Here you could remove any other thing that you have done.
*/
public void remove();
/**
* Get the description of this listener, in a way to interact with the user.
*
* @return a string describing the functionalities of this listener
*/
public String getDescription();
}

View File

@@ -0,0 +1,265 @@
package berack96.lib.graph.view;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Observer;
import java.util.Set;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
import berack96.lib.graph.Vertex;
import berack96.lib.graph.impl.MapGraph;
import berack96.lib.graph.view.edge.EdgeComponent;
import berack96.lib.graph.view.vertex.VertexComponent;
@SuppressWarnings({"unchecked", "deprecation"})
public class GraphPanel<V, W extends Number> extends Component {
private static final long serialVersionUID = 1L;
private final GraphicalView<VertexComponent<V>> vertexRender;
private final GraphicalView<EdgeComponent<V, W>> edgeRender;
private final Class<V> classV;
private final Class<W> classW;
private final Container vertices = new Container();
private final Container edges = new Container();
private final Graph<V, W> graph = new MapGraph<>();
private final Set<Observer> observers = new HashSet<>();
private GraphListener old = null;
public GraphPanel(GraphicalView<VertexComponent<V>> vertexRender, GraphicalView<EdgeComponent<V, W>> edgeRender, Class<V> classV, Class<W> classW) {
this.vertexRender = vertexRender;
this.edgeRender = edgeRender;
this.classV = classV;
this.classW = classW;
}
public Graph<V, W> getGraph() {
return graph;
}
public void setGraphListener(GraphListener listener) {
if(old != null)
old.remove();
for (MouseListener l : getMouseListeners())
removeMouseListener(l);
for (MouseMotionListener l : getMouseMotionListeners())
removeMouseMotionListener(l);
for (KeyListener l : getKeyListeners())
removeKeyListener(l);
old = listener;
addMouseListener(listener);
addMouseMotionListener(listener);
addKeyListener(listener);
}
public void addVertex(Point center, V vertex) {
VertexComponent<V> component = getVertexAt(center);
if (component == null) {
VertexComponent<V> v = new VertexComponent<>(new Vertex<>(graph, vertex));
v.vertex.addIfAbsent();
boolean isContained = false;
for(Component comp : vertices.getComponents())
if (comp.equals(v))
isContained = true;
if (!isContained) {
v.setBounds(vertexRender.getBox(v, center));
vertices.add(v);
}
}
}
public void removeVertex(Point center) {
try {
VertexComponent<V> component = getVertexAt(center);
component.vertex.remove();
vertices.remove(component);
} catch (Exception ignore) {}
}
public void moveVertex(VertexComponent<V> vertex, Point destination) {
Rectangle rectangle = vertexRender.getBox(vertex, destination);
vertex.setLocation(rectangle.x, rectangle.y);
}
public void addEdge(Edge<V, W> edge) {
VertexComponent<V> vSource = null;
VertexComponent<V> vDest = null;
for (Component comp : vertices.getComponents()) {
VertexComponent<V> temp = (VertexComponent<V>) comp;
V vTemp = temp.vertex.getValue();
if (vSource == null && vTemp.equals(edge.getSource()))
vSource = temp;
if (vDest == null && vTemp.equals(edge.getDestination()))
vDest = temp;
}
addEdge(vSource, vDest, edge.getWeight());
}
public void addEdge(VertexComponent<V> source, VertexComponent<V> dest, W weight) {
try {
Point center = new Point(Math.abs(source.getX() - dest.getY()), Math.abs(source.getY() - dest.getY()));
EdgeComponent<V, W> edgeComponent = new EdgeComponent<>(source, dest, weight);
edgeComponent.setBounds(edgeRender.getBox(edgeComponent, center));
edges.add(edgeComponent);
graph.addEdge(edgeComponent.edge);
} catch (Exception ignore) {
ignore.printStackTrace();
}
}
public void removeEdge(VertexComponent<V> source, VertexComponent<V> dest) {
try {
graph.removeEdge(source.vertex.getValue(), dest.vertex.getValue());
EdgeComponent<V, W> toRemove = null;
for (Component c : edges.getComponents()) {
EdgeComponent<V, W> edge = (EdgeComponent<V, W>) c;
if (edge.source.equals(source) && edge.destination.equals(dest))
toRemove = edge;
}
edges.remove(toRemove);
} catch (Exception ignore) {}
}
public void modEdge(VertexComponent<V> source, VertexComponent<V> dest, W weight) {
removeEdge(source, dest);
addEdge(source, dest, weight);
}
public VertexComponent<V> getVertexAt(Point point) {
Component component = vertices.getComponentAt(point);
return component instanceof VertexComponent ? (VertexComponent<V>) component : null;
}
public EdgeComponent<V, W> getEdgeAt(Point point) {
Component component = edges.getComponentAt(point);
return component instanceof EdgeComponent ? (EdgeComponent<V, W>) component : null;
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void save(String fileName) throws IOException {
GraphGraphicalSave save = new GraphGraphicalSave(vertices);
Graph.save(graph, Graph.GSON.toJson(save), fileName);
}
public void load(String fileName) throws IOException {
String saveContent = Graph.load(graph, fileName, classV, classW);
GraphGraphicalSave save = Graph.GSON.fromJson(saveContent, GraphGraphicalSave.class);
vertices.removeAll();
edges.removeAll();
for(int i = 0; i<save.vertices.length; i++) {
V v = Graph.GSON.fromJson(save.vertices[i], classV);
Point p = save.points[i];
addVertex(p, v);
}
for(String v : save.vertices)
graph.getEdgesOut(Graph.GSON.fromJson(v, classV)).forEach(e -> addEdge(e));
repaint();
}
@Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
vertices.setBounds(x, y, width, height);
edges.setBounds(x, y, width, height);
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Collection<EdgeComponent<V, W>> toRemove = new HashSet<>();
for (Component component : edges.getComponents()) {
EdgeComponent<V, W> edge = (EdgeComponent<V, W>) component;
Vertex<V> source = edge.source.vertex;
Vertex<V> dest = edge.destination.vertex;
if (source.isStillContained() && dest.isStillContained() && graph.containsEdge(source.getValue(), dest.getValue())) {
Point center = new Point(edge.getX() + edge.getWidth() / 2, edge.getY() + edge.getHeight() / 2);
edge.setBounds(edgeRender.getBox(edge, center));
edgeRender.paint((Graphics2D) g2.create(), edge, center);
}
else
toRemove.add(edge);
}
toRemove.forEach(edges::remove);
for (Component component : vertices.getComponents()) {
VertexComponent<V> vertex = (VertexComponent<V>) component;
if (graph.contains(vertex.vertex.getValue())) {
Point center = new Point(vertex.getX() + vertex.getWidth() / 2, vertex.getY() + vertex.getHeight() / 2);
vertexRender.paint((Graphics2D) g2.create(), vertex, center);
}
}
updateObservers();
}
private void updateObservers() {
observers.forEach(observer -> observer.update(null, this.graph));
}
class GraphGraphicalSave {
public GraphGraphicalSave() {}
protected GraphGraphicalSave(Container vertices) {
List<String> v = new LinkedList<>();
List<Point> p = new LinkedList<>();
for(Component vertex : vertices.getComponents()) {
Point temp = new Point(vertex.getX(), vertex.getY());
temp.x += vertex.getWidth() / 2;
temp.y += vertex.getHeight() / 2;
p.add(temp);
v.add(Graph.GSON.toJson(((VertexComponent<V>) vertex).vertex.getValue()));
}
int i = 0;
this.vertices = new String[v.size()];
for(String s : v) {
this.vertices[i] = s;
i++;
}
i = 0;
this.points = new Point[p.size()];
for(Point pt : p) {
this.points[i] = pt;
i++;
}
}
public String[] vertices;
public Point[] points;
}
}

View File

@@ -0,0 +1,68 @@
package berack96.lib.graph.view;
import berack96.lib.graph.view.edge.EdgeIntListener;
import berack96.lib.graph.view.edge.EdgeListener;
import berack96.lib.graph.view.edge.EdgeView;
import berack96.lib.graph.view.vertex.VertexIntListener;
import berack96.lib.graph.view.vertex.VertexListener;
import berack96.lib.graph.view.vertex.VertexView;
import berack96.lib.graph.visit.*;
import berack96.lib.graph.visit.impl.BFS;
import berack96.lib.graph.visit.impl.DFS;
import berack96.lib.graph.visit.impl.Dijkstra;
import berack96.lib.graph.visit.impl.Tarjan;
import javax.swing.*;
import java.awt.*;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* This class is the Window that appear for building the graph and playing around with it
*
* @author Berack96
*/
public class GraphWindow<V, W extends Number> extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
GraphPanel<Integer, Integer> panel = new GraphPanel<>(new VertexView<>(), new EdgeView<>(), Integer.class, Integer.class);
GraphWindow<Integer, Integer> win = new GraphWindow<>(panel, new VertexIntListener(panel), new EdgeIntListener<>(panel));
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); // full screen
dim.setSize(dim.width / 2, dim.height / 2);
win.setSize(dim);
win.setLocationRelativeTo(null); //centered
win.visitRefresh(500);
win.setVisible(true);
}
private final GraphPanel<V, W> graphPanel;
private final GraphInfo<V, W> infoPanel;
private GraphWindow(GraphPanel<V, W> graphPanel, VertexListener<V> vListener, EdgeListener<V, W> eListener) {
this.setTitle("Grafo");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new BorderLayout());
Set<VisitStrategy<V, W>> strats = new LinkedHashSet<>();
strats.add(new DFS<>());
strats.add(new BFS<>());
strats.add(new Dijkstra<>());
strats.add(new Tarjan<>());
this.infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats);
this.graphPanel = graphPanel;
this.add(infoPanel, BorderLayout.EAST);
this.add(graphPanel);
}
public void visitRefresh(int millisec) {
VisitListener.changeRefresh(millisec);
}
public GraphPanel<V, W> getGraphPanel() {
return graphPanel;
}
}

View File

@@ -0,0 +1,30 @@
package berack96.lib.graph.view;
import java.awt.*;
/**
* An interface for divide the "hitbox" and the "paint" of the various items
*
* @param <O> the object to paint
* @author Berack96
*/
public interface GraphicalView<O> {
/**
* Box where the object is sensible at listeners (like Hitbox)
*
* @param obj the object to draw
* @param center the center point of the object
* @return a rectangle where the object is sensible to the listeners
*/
Rectangle getBox(O obj, Point center);
/**
* The paint function, aka the part where you can draw things (like Mesh)
*
* @param g2 the graphics object used for painting
* @param obj the object to paint
* @param center the center point of the object
*/
void paint(Graphics2D g2, O obj, Point center);
}

View File

@@ -0,0 +1,112 @@
package berack96.lib.graph.view;
import berack96.lib.graph.Graph;
import berack96.lib.graph.view.vertex.VertexComponent;
import berack96.lib.graph.visit.VisitStrategy;
import berack96.lib.graph.visit.impl.VisitInfo;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public class VisitListener<V> implements GraphListener {
private final GraphPanel<V, ?> panel;
private final VisitStrategy<V, ?> strategy;
private final Set<Timer> timers = new HashSet<>();
private static int refreshTime = 1000;
public VisitListener(GraphPanel<V, ?> panel, VisitStrategy<V, ?> strategy) {
this.panel = panel;
this.strategy = strategy;
}
public static void changeRefresh(int millisec) {
refreshTime = millisec;
}
@Override
public void remove() {
timers.forEach(t -> t.stop());
timers.clear();
}
@Override
public String getDescription() {
return "<html>"
+ "Start a visit by pressing<br />"
+ "with the mouse SX on the root vertex<br />"
+ "</html>";
}
@Override
public void mousePressed(MouseEvent e) {
this.remove();
Graph<V, ?> graph = panel.getGraph();
graph.unMarkAll();
panel.repaint();
if (e.getButton() == MouseEvent.BUTTON1)
try {
VertexComponent<V> vertex = panel.getVertexAt(e.getPoint());
AtomicInteger count = new AtomicInteger(0);
VisitInfo<V> info = vertex.vertex.visit(strategy, null);
info.forEach(v -> {
final boolean visited = v.timeVisited == count.get();
Timer timer = new Timer(count.getAndIncrement() * refreshTime, e1 -> {
if (visited && v.parent !=null)
graph.mark(v.vertex, v.parent);
graph.mark(v.vertex, visited ? "visited" : "discovered");
panel.repaint();
});
timers.add(timer);
timer.setRepeats(false);
timer.start();
});
} catch (Exception ignore) {}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@@ -0,0 +1,22 @@
package berack96.lib.graph.view.edge;
import berack96.lib.graph.Edge;
import berack96.lib.graph.view.vertex.VertexComponent;
import java.awt.*;
public class EdgeComponent<V, W extends Number> extends Component {
private static final long serialVersionUID = 1L;
public final VertexComponent<V> source;
public final VertexComponent<V> destination;
public final W weight;
public final Edge<V, W> edge;
public EdgeComponent(VertexComponent<V> source, VertexComponent<V> destination, W weight) {
this.source = source;
this.destination = destination;
this.weight = weight;
this.edge = new Edge<>(source.vertex.getValue(), destination.vertex.getValue(), weight);
}
}

View File

@@ -0,0 +1,24 @@
package berack96.lib.graph.view.edge;
import berack96.lib.graph.Vertex;
import berack96.lib.graph.view.GraphPanel;
public class EdgeIntListener<V> extends EdgeListener<V, Integer> {
public EdgeIntListener(GraphPanel<V, Integer> graphPanel) {
super(graphPanel);
}
@Override
public void remove() {}
@Override
protected Integer buildNewEdge(Vertex<V> vertex, Vertex<V> vertex1) {
return (int) (Math.random() * 100);
}
@Override
protected Integer buildEdgeFrom(String string) {
return Integer.parseInt(string.replaceAll("[^0-9]+", ""));
}
}

View File

@@ -0,0 +1,128 @@
package berack96.lib.graph.view.edge;
import berack96.lib.graph.Vertex;
import berack96.lib.graph.view.GraphListener;
import berack96.lib.graph.view.GraphPanel;
import berack96.lib.graph.view.vertex.VertexComponent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.concurrent.atomic.AtomicReference;
public abstract class EdgeListener<V, W extends Number> implements GraphListener {
private final GraphPanel<V, W> graphPanel;
private final AtomicReference<VertexComponent<V>> componentPressed = new AtomicReference<>();
private final AtomicReference<Integer> buttonPressed = new AtomicReference<>();
private final AtomicReference<EdgeComponent<V, W>> edge = new AtomicReference<>();
private final StringBuilder string = new StringBuilder();
public EdgeListener(GraphPanel<V, W> graphPanel) {
this.graphPanel = graphPanel;
}
protected abstract W buildNewEdge(Vertex<V> vertex, Vertex<V> vertex1);
protected abstract W buildEdgeFrom(String string);
@Override
public String getDescription() {
return "<html>"
+ "Modify edges with:<br />"
+ "mouse SX on vertex to another ==> add<br />"
+ "mouse DX on edge ==> change weigth<br />"
+ "(only numbers allowed)"
+ "</html>";
}
@Override
public void mousePressed(MouseEvent e) {
buttonPressed.set(e.getButton());
componentPressed.set(graphPanel.getVertexAt(e.getPoint()));
}
@Override
public void mouseReleased(MouseEvent e) {
try {
edge.get().source.vertex.unMark("modS");
edge.get().destination.vertex.unMark("modD");
edge.set(null);
} catch (Exception ignored) {}
if (buttonPressed.get() == MouseEvent.BUTTON1) {
try {
VertexComponent<V> source = componentPressed.get();
VertexComponent<V> destination = graphPanel.getVertexAt(e.getPoint());
if (!graphPanel.getGraph().containsEdge(source.vertex.getValue(), destination.vertex.getValue())
&& !source.vertex.equals(destination.vertex))
graphPanel.addEdge(source, destination, buildNewEdge(source.vertex, destination.vertex));
} catch (Exception ignore) {
}
} else {
edge.set(graphPanel.getEdgeAt(e.getPoint()));
try {
edge.get().source.vertex.mark("modS");
edge.get().destination.vertex.mark("modD");
graphPanel.setFocusTraversalKeysEnabled(false);
graphPanel.requestFocusInWindow();
} catch (Exception ignored) {}
}
string.delete(0, string.length());
componentPressed.set(null);
graphPanel.repaint();
}
@Override
public void keyPressed(KeyEvent e) {
if (edge.get() != null && Character.isDigit(e.getKeyChar())) {
string.append(e.getKeyChar());
try {
graphPanel.modEdge(edge.get().source, edge.get().destination, buildEdgeFrom(string.toString()));
graphPanel.repaint();
} catch (Exception ignored) {
}
} else {
try {
edge.get().source.vertex.unMark("modS");
edge.get().destination.vertex.unMark("modD");
} catch (Exception ignored) {}
edge.set(null);
string.delete(0, string.length());
}
graphPanel.repaint();
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@@ -0,0 +1,109 @@
package berack96.lib.graph.view.edge;
import java.awt.*;
import java.awt.geom.Point2D;
import java.util.Collection;
import berack96.lib.graph.view.GraphicalView;
import berack96.lib.graph.view.stuff.Arrow;
public class EdgeView<V, W extends Number> implements GraphicalView<EdgeComponent<V, W>> {
private static final Font FONT = new Font("Papyrus", Font.BOLD, 14);
@Override
public Rectangle getBox(EdgeComponent<V, W> edge, Point center) {
/* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */
Point srcLoc = edge.source.getLocation();
Point desLoc = edge.destination.getLocation();
/* getting the radius */
int srcRadius = edge.source.getHeight() / 2;
int desRadius = edge.destination.getHeight() / 2;
/* centering the location */
srcLoc.translate(srcRadius, srcRadius);
desLoc.translate(desRadius, desRadius);
/* using vector for moving to the edge of the circumference and finding location of int box */
final Point.Double vector = getVector(srcLoc, desLoc);
/* CALCULATING THE NUMBER SPACE */
int boxDistance = (int) (srcLoc.distance(desLoc) / 2.7);
FontMetrics metrics = edge.getFontMetrics(FONT);
int dimString = metrics.stringWidth(edge.weight.toString());
int dimRect = Math.max(dimString, metrics.getHeight());
return new Rectangle(
(int) ((desLoc.x - (vector.x * boxDistance)) - (dimRect / 2)),
(int) ((desLoc.y - (vector.y * boxDistance)) - (dimRect / 2)),
dimRect, dimRect);
}
@Override
public void paint(Graphics2D g2, EdgeComponent<V, W> edge, Point center) {
/* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */
Point srcLoc = edge.source.getLocation();
Point desLoc = edge.destination.getLocation();
/* getting the radius */
int srcRadius = edge.source.getHeight() / 2;
int desRadius = edge.destination.getHeight() / 2;
/* centering the location */
srcLoc.translate(srcRadius, srcRadius);
desLoc.translate(desRadius, desRadius);
/* using vector for moving to the edge of the circumference and finding location of int box */
final Point.Double vector = getVector(srcLoc, desLoc);
/* CALCULATING THE NUMBER SPACE */
int boxDistance = (int) (srcLoc.distance(desLoc) / 2.7);
FontMetrics metrics = edge.getFontMetrics(FONT);
int dimString = metrics.stringWidth(edge.weight.toString());
int dimRect = Math.max(dimString, metrics.getHeight());
Rectangle integerRect = new Rectangle(
(int) ((desLoc.x - (vector.x * boxDistance)) - (dimRect / 2)),
(int) ((desLoc.y - (vector.y * boxDistance)) - (dimRect / 2)),
dimRect, dimRect);
/* moving to a distance R to the center */
srcLoc.translate((int) (vector.x * srcRadius), (int) (vector.y * srcRadius));
desLoc.translate((int) (-vector.x * desRadius), (int) (-vector.y * desRadius));
/* THE COLOR OF THE ARROW */
Collection<Object> marksD = edge.destination.vertex.getMarks();
Collection<Object> marksS = edge.source.vertex.getMarks();
boolean isChild = marksD.contains(edge.source.vertex.getValue());
boolean selected = marksS.contains("selected");
boolean isMod = marksD.contains("modD") && marksS.contains("modS");
Color arrowColor = isChild || selected ? Color.RED : Color.BLACK;
Color boxColor = isChild || selected || isMod ? Color.ORANGE : Color.BLUE;
Color stringColor = isChild || selected || isMod ? Color.BLACK : Color.CYAN;
g2.setFont(FONT);
/* draw all */
g2.setColor(arrowColor);
//g2d.drawLine(arrowStart.x, arrowStart.y, arrowEnd.x, arrowEnd.y);
Polygon arrow = new Arrow(srcLoc, desLoc, 1, 10);
//g2d.draw(arrow);
g2.fillPolygon(arrow);
/* draw the integer space */
g2.setColor(boxColor);
g2.fill(integerRect);
g2.setColor(arrowColor);
g2.draw(integerRect);
g2.setColor(stringColor);
g2.drawString(edge.weight.toString(), (float) (integerRect.x + (dimRect - dimString) / 2), (float) (integerRect.y + (dimRect + metrics.getHeight() / 2) / 2));
}
private Point.Double getVector(Point a, Point b) {
final Point.Double vector = new Point2D.Double(b.x - a.x, b.y - a.y);
/* normalizing vector */
double length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
if(length != 0) {
vector.x = vector.x / length;
vector.y = vector.y / length;
}
return vector;
}
}

View File

@@ -0,0 +1,59 @@
package berack96.lib.graph.view.stuff;
import java.awt.*;
/**
* Class that create a Polygon that has a shape of an arrow
*
* @author Berack96
*/
public class Arrow extends Polygon {
private static final long serialVersionUID = 1L;
/**
* Create an arrow
*
* @param start the starting point of your arrow (the base)
* @param end the ending point of your arrow (the head)
* @param size the size of the arrow base
* @param headSize the size of the arrow's head
*/
public Arrow(Point start, Point end, final int size, final int headSize) {
final Point.Double vector = new Point.Double(end.x - start.x, end.y - start.y);
/* vectors normalization */
double length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
vector.x = vector.x / length;
vector.y = vector.y / length;
final Point headStart = new Point((int) (end.x - vector.x * headSize), (int) (end.y - vector.y * headSize));
/* rotating vector for the parallels */
double cs = Math.cos(Math.PI / 2);
double sn = Math.sin(Math.PI / 2);
double x = vector.x * cs - vector.y * sn;
double y = vector.x * sn + vector.y * cs;
vector.setLocation(x, y);
/* TODO here use some magic for create a curve arrow if vector.x == vector.y && vector.x == 0 */
/* building arrow starting from A to G */
/*
C
|\
| \
A--------B \
| D
G--------F /
| /
|/
E
*/
addPoint((int) (start.x - vector.x * size), (int) (start.y - vector.y * size));
addPoint((int) (start.x + vector.x * size), (int) (start.y + vector.y * size));
addPoint((int) (headStart.x + vector.x * size), (int) (headStart.y + vector.y * size));
addPoint((int) (headStart.x + vector.x * headSize), (int) (headStart.y + vector.y * headSize));
addPoint(end.x, end.y);
addPoint((int) (headStart.x - vector.x * headSize), (int) (headStart.y - vector.y * headSize));
addPoint((int) (headStart.x - vector.x * size), (int) (headStart.y - vector.y * size));
}
}

View File

@@ -0,0 +1,31 @@
package berack96.lib.graph.view.vertex;
import java.awt.*;
import berack96.lib.graph.Vertex;
public class VertexComponent<V> extends Component {
private static final long serialVersionUID = 1L;
public final Vertex<V> vertex;
public VertexComponent(Vertex<V> vertex) {
this.vertex = vertex;
}
@Override
public String toString() {
return "[" + vertex + " {" + getX() + "," + getY() + "}]";
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
try {
return obj.getClass().equals(getClass()) && ((VertexComponent<V>)obj).vertex.equals(vertex);
} catch (Exception e) {
return false;
}
}
}

View File

@@ -0,0 +1,30 @@
package berack96.lib.graph.view.vertex;
import java.util.Arrays;
import berack96.lib.graph.Graph;
import berack96.lib.graph.view.GraphPanel;
public class VertexIntListener extends VertexListener<Integer> {
public VertexIntListener(GraphPanel<Integer, ?> panel) {
super(panel);
}
@Override
public void remove() {}
@Override
protected Integer buildNewVertex(Graph<Integer, ?> graph) {
int counter = 0;
Integer[] vertices = graph.vertices().toArray(new Integer[graph.numberOfVertices()]);
Arrays.sort(vertices);
for(int i = 0; i<vertices.length; i++) {
if(!vertices[i].equals(counter))
return counter;
counter++;
}
return counter;
}
}

View File

@@ -0,0 +1,91 @@
package berack96.lib.graph.view.vertex;
import berack96.lib.graph.Graph;
import berack96.lib.graph.view.GraphListener;
import berack96.lib.graph.view.GraphPanel;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.concurrent.atomic.AtomicReference;
public abstract class VertexListener<V> implements GraphListener {
protected final GraphPanel<V, ?> panel;
private final AtomicReference<VertexComponent<V>> componentPressed = new AtomicReference<>();
public VertexListener(GraphPanel<V, ?> panel) {
this.panel = panel;
}
protected abstract V buildNewVertex(Graph<V, ?> graph);
@Override
public String getDescription() {
return "<html>"
+ "Modify vertex with:<br />"
+ "mouse SX ==> add<br />"
+ "mouse SX on vertex ==> move<br />"
+ "mouse DX ==> remove<br />"
+ "</html>";
}
@Override
public void mousePressed(MouseEvent e) {
try {
if (e.getButton() == MouseEvent.BUTTON1)
componentPressed.set(panel.getVertexAt(e.getPoint()));
} catch (Exception ignore) {}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && componentPressed.get() == null)
panel.addVertex(e.getPoint(), buildNewVertex(panel.getGraph()));
else if (e.getButton() == MouseEvent.BUTTON3)
panel.removeVertex(e.getPoint());
panel.getGraph().unMarkAll("selected");
if(componentPressed.get() != null)
componentPressed.get().vertex.mark("selected");
panel.repaint();
componentPressed.set(null);
}
@Override
public void mouseDragged(MouseEvent e) {
if (componentPressed.get() != null) {
panel.moveVertex(componentPressed.get(), e.getPoint());
panel.repaint();
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@@ -0,0 +1,42 @@
package berack96.lib.graph.view.vertex;
import java.awt.*;
import berack96.lib.graph.view.GraphicalView;
public class VertexView<V> implements GraphicalView<VertexComponent<V>> {
private static final Font FONT = new Font("Comic Sans MS", Font.BOLD, 17);
private static final int PADDING = 6;
@Override
public Rectangle getBox(VertexComponent<V> obj, Point center) {
FontMetrics metrics = obj.getFontMetrics(FONT);
int stringPixels = metrics.stringWidth(obj.vertex.getValue().toString());
int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING;
return new Rectangle(center.x - size / 2, center.y - size / 2, size, size);
}
@Override
public void paint(Graphics2D g2, VertexComponent<V> obj, Point center) {
boolean discovered = obj.vertex.getMarks().contains("discovered");
boolean visited = obj.vertex.getMarks().contains("visited");
boolean selected = obj.vertex.getMarks().contains("selected");
FontMetrics metrics = obj.getFontMetrics(FONT);
int stringPixels = metrics.stringWidth(obj.vertex.getValue().toString());
int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING;
center.x = center.x - size / 2;
center.y = center.y - size / 2;
g2.setFont(FONT);
g2.setColor(visited || selected ? Color.RED : Color.ORANGE);
g2.fillOval(center.x, center.y, size, size);
g2.setColor(visited || discovered || selected ? Color.ORANGE : Color.YELLOW);
g2.fillOval(center.x + PADDING / 2, center.y + PADDING / 2, size - PADDING, size - PADDING);
g2.setColor(Color.BLACK);
g2.drawString(obj.vertex.getValue().toString(), center.x + PADDING + (size - 2 * PADDING - stringPixels) / 2, center.y + (size) / 2 + PADDING);
}
}

View File

@@ -0,0 +1,29 @@
package berack96.lib.graph.visit;
import java.util.List;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
/**
* Interface that is helpful for implements visit that needs to retrieve the distance between a vertex to all the others
*
* @param <V> the vertex
* @param <W> the weight
* @author Berack96
*/
public interface VisitDistSourceDest<V, W extends Number> extends VisitStrategy<V, W> {
/**
* Get the distance from the source to the destination<br>
* The list contains the minimum path from the vertex marked as source to the destination vertex
*
* @param graph the graph were to find the min path
* @param source the source vertex
* @param destination the destination vertex
* @return the distance
* @throws NullPointerException if one of the vertex is null
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
*/
List<Edge<V, W>> distance(Graph<V, W> graph, V source, V destination) throws NullPointerException, IllegalArgumentException;
}

View File

@@ -0,0 +1,35 @@
package berack96.lib.graph.visit;
import java.util.List;
import java.util.Map;
import berack96.lib.graph.Edge;
/**
* Interface that is helpful for implements visit that needs to retrieve the distance between a vertex to all the others
*
* @param <V> the vertex
* @param <W> the weight
* @author Berack96
*/
public interface VisitDistance<V, W extends Number> extends VisitStrategy<V, W> {
/**
* Get the last calculated distance to all the possible destinations<br>
* The map contains all the possible vertices that are reachable from the source set in the visit<br>
* If there is no path between the destination and the source, then null is returned as accordingly to the map interface<br>
* If the visit is not already been done, then the map is null.
*
* @return the last distance
* @throws NullPointerException if the visit is not already been done
*/
Map<V, List<Edge<V, W>>> getLastDistance() throws NullPointerException;
/**
* Get the last source vertex of the visit for calculating the destinations.<br>
* Returns null if the visit is not already been done
*
* @return the last vertex
*/
V getLastSource();
}

View File

@@ -0,0 +1,21 @@
package berack96.lib.graph.visit;
import java.util.Collection;
/**
* Interface that is helpful for implements visit that needs to retrieve the SCC
*
* @param <V> the vertex
* @param <W> the weight
* @author Berack96
*/
public interface VisitSCC<V, W extends Number> extends VisitStrategy<V, W> {
/**
* Return the latest calculated strongly connected components of the graph.
*
* @return the latest SCC
* @throws NullPointerException if there is no last calculated SCC
*/
Collection<Collection<V>> getSCC();
}

View File

@@ -0,0 +1,31 @@
package berack96.lib.graph.visit;
import java.util.function.Consumer;
import berack96.lib.graph.Graph;
import berack96.lib.graph.visit.impl.VisitInfo;
/**
* This class is used for define some strategy for the visit of a graph.
*
* @param <V> The Object that represent a vertex
* @param <W> The Object that represent the edge (more specifically the weight of the edge)
* @author Berack96
*/
public interface VisitStrategy<V, W extends Number> {
/**
* With this the graph will be visited accordingly to the strategy of the visit.<br>
* Some strategy can accept a source vertex null, because they visit all the graph anyway.<br>
* If you want to stop the visit of the graph, you just have to throw any exception in the visit function, but be sure to catch it
*
* @param graph the graph to visit
* @param source the source of the visit
* @param visit the function to apply at each vertex when they are visited
* @return an info of the view
* @throws NullPointerException if one of the arguments is null (only the consumers can be null)
* @throws IllegalArgumentException if the source vertex is not in the graph
* @throws UnsupportedOperationException in the case that the visit algorithm cannot be applied to the graph
*/
VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException;
}

View File

@@ -0,0 +1,22 @@
package berack96.lib.graph.visit;
import java.util.List;
/**
* Interface that is helpful for implements visit that needs to retrieve the topological sort
*
* @param <V> the vertex
* @param <W> the weight
* @author Berack96
*/
public interface VisitTopological<V, W extends Number> extends VisitStrategy<V, W> {
/**
* Return the latest calculated Topological sort of the graph.<br>
* If the latest visited graph is not a DAG, it will return null.
*
* @return the topological order of the DAG
* @throws NullPointerException if there is no last calculated topological sort
*/
List<V> getTopologicalSort();
}

View File

@@ -0,0 +1,44 @@
package berack96.lib.graph.visit.impl;
import java.util.LinkedList;
import java.util.function.Consumer;
import berack96.lib.graph.Graph;
import berack96.lib.graph.visit.VisitStrategy;
/**
* Breadth-first search<br>
* The algorithm starts at the root node and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
*
* @param <V> the vertex of the graph
* @param <W> the weight of the graph
* @author Berack96
*/
public class BFS<V, W extends Number> implements VisitStrategy<V, W> {
@Override
public VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
VisitInfo<V> info = new VisitInfo<>(source);
final LinkedList<V> toVisitChildren = new LinkedList<>();
toVisitChildren.push(source);
if (visit != null)
visit.accept(source);
info.setVisited(source);
while (!toVisitChildren.isEmpty()) {
V current = toVisitChildren.removeFirst();
for (V child : graph.getChildren(current))
if (!info.isDiscovered(child)) {
toVisitChildren.addLast(child);
info.setVisited(child);
info.setParent(current, child);
if (visit != null)
visit.accept(child);
}
}
return info;
}
}

View File

@@ -0,0 +1,50 @@
package berack96.lib.graph.visit.impl;
import java.util.Iterator;
import java.util.Stack;
import java.util.function.Consumer;
import berack96.lib.graph.Graph;
import berack96.lib.graph.visit.VisitStrategy;
/**
* Depth-first search<br>
* The algorithm starts at the root node and explores as far as possible along each branch before backtracking.
*
* @param <V> the vertex of the graph
* @param <W> the weight of the graph
* @author Berack96
*/
public class DFS<V, W extends Number> implements VisitStrategy<V, W> {
@Override
public VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
VisitInfo<V> info = new VisitInfo<>(source);
final Stack<V> toVisit = new Stack<>();
toVisit.push(source);
while (!toVisit.isEmpty()) {
V current = toVisit.peek();
boolean hasChildToVisit = false;
Iterator<V> iter = graph.getChildren(current).iterator();
while (iter.hasNext() && !hasChildToVisit) {
V child = iter.next();
if (!info.isDiscovered(child)) {
hasChildToVisit = true;
toVisit.push(child);
info.setParent(current, child);
}
}
if (!hasChildToVisit) {
toVisit.pop();
info.setVisited(current);
if (visit != null)
visit.accept(current);
}
}
return info;
}
}

View File

@@ -0,0 +1,110 @@
package berack96.lib.graph.visit.impl;
import java.util.*;
import java.util.function.Consumer;
import berack96.lib.graph.Edge;
import berack96.lib.graph.Graph;
import berack96.lib.graph.visit.VisitDistance;
/**
* Class that implements the Dijkstra algorithm and uses it for getting all the distance from a source
*
* @param <V> vertex
* @param <W> weight
* @author Berack96
*/
public class Dijkstra<V, W extends Number> implements VisitDistance<V, W> {
private Map<V, List<Edge<V, W>>> distance = null;
private V source = null;
@Override
public Map<V, List<Edge<V, W>>> getLastDistance() {
return distance;
}
@Override
public V getLastSource() {
return source;
}
@Override
public VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
VisitInfo<V> info = new VisitInfo<>(source);
Queue<QueueEntry> queue = new PriorityQueue<>();
Map<V, Double> dist = new HashMap<>();
Map<V, V> prev = new HashMap<>();
this.source = source;
dist.put(source, 0.0); // Initialization
queue.add(new QueueEntry(source, 0.0));
while (!queue.isEmpty()) { // The main loop
QueueEntry u = queue.poll(); // Remove and return best vertex
info.setVisited(u.entry);
if (visit != null)
visit.accept(u.entry);
graph.getEdgesOut(u.entry).forEach((edge) -> {
V child = edge.getDestination();
info.setDiscovered(child);
double alt = dist.get(u.entry) + edge.getWeight().doubleValue();
Double distCurrent = dist.get(child);
if (distCurrent == null || alt < distCurrent) {
dist.put(child, alt);
prev.put(child, u.entry);
QueueEntry current = new QueueEntry(child, alt);
queue.remove(current);
queue.add(current);
}
});
}
/* Cleaning up the results */
distance = new HashMap<>();
for (V vertex : prev.keySet()) {
List<Edge<V, W>> path = new LinkedList<>();
V child = vertex;
V father = prev.get(child);
do {
Edge<V, W> edge = new Edge<>(father, child, graph.getWeight(father, child));
path.add(0, edge);
info.setParent(father, child);
child = father;
father = prev.get(child);
} while (father != null);
distance.put(vertex, new ArrayList<>(path));
}
return info;
}
private class QueueEntry implements Comparable<QueueEntry> {
final V entry;
final Double weight;
QueueEntry(V entry, Double weight) {
this.entry = entry;
this.weight = weight;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
try {
return ((QueueEntry) obj).entry.equals(entry);
} catch (Exception e) {
return false;
}
}
@Override
public int compareTo(QueueEntry queueEntry) {
double ret = this.weight - queueEntry.weight;
return ret==0? 0: ret<0? -1:1;
}
}
}

View File

@@ -0,0 +1,109 @@
package berack96.lib.graph.visit.impl;
import java.util.*;
import java.util.function.Consumer;
import berack96.lib.graph.Graph;
import berack96.lib.graph.visit.VisitSCC;
import berack96.lib.graph.visit.VisitTopological;
/**
* Class that implements the Tarjan algorithm and uses it for getting the SCC and the topological sort
*
* @param <V> vertex
* @param <W> weight
* @author Berack96
*/
public class Tarjan<V, W extends Number> implements VisitSCC<V, W>, VisitTopological<V, W> {
private Collection<Collection<V>> SCC = null;
private List<V> topologicalSort = null;
private Map<V, Integer> indices = null;
private Map<V, Integer> lowLink = null;
private Stack<V> stack = null;
private VisitInfo<V> info = null;
@Override
public Collection<Collection<V>> getSCC() {
return SCC;
}
@Override
public List<V> getTopologicalSort() {
return topologicalSort;
}
/**
* This particular visit strategy use only the graph and the visit, so the source param is not needed.
*
* @param graph the graph to visit
* @param source not needed
* @param visit the function to apply at each vertex when they are visited
* @throws NullPointerException if the graph is null
* @throws IllegalArgumentException doesn't throw this
*/
@Override
public VisitInfo<V> visit(Graph<V, W> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
SCC = new HashSet<>();
topologicalSort = new LinkedList<>();
info = null;
indices = new HashMap<>();
lowLink = new HashMap<>();
stack = new Stack<>();
Integer index = 0;
for (V vertex : graph) {
if (info == null)
info = new VisitInfo<>(vertex);
if (!indices.containsKey(vertex))
strongConnect(graph, vertex, index, visit);
}
topologicalSort = (graph.numberOfVertices() == SCC.size()) ? new ArrayList<>(topologicalSort) : null;
return info;
}
private void strongConnect(Graph<V, W> graph, V vertex, Integer index, Consumer<V> visit) {
// Set the depth index for v to the smallest unused index
indices.put(vertex, index);
lowLink.put(vertex, index);
index++;
stack.push(vertex);
info.setDiscovered(vertex);
// Consider successors of v
for (V child : graph.getChildren(vertex)) {
if (!indices.containsKey(child)) {
info.setParent(vertex, child);
strongConnect(graph, child, index, visit);
lowLink.put(vertex, Math.min(lowLink.get(vertex), lowLink.get(child)));
} else if (stack.contains(child)) {
// Successor w is in stack S and hence in the current SCC
// If w is not on stack, then (v, w) is a cross-edge in the DFS tree and must be ignored
// Note: The next line may look odd - but is correct.
// It says w.index not w.lowlink; that is deliberate and from the original paper
lowLink.put(vertex, Math.min(lowLink.get(vertex), indices.get(child)));
}
}
// If v is a root node, pop the stack and generate an SCC
if (lowLink.get(vertex).equals(indices.get(vertex))) {
Set<V> newComponent = new HashSet<>();
V temp;
do {
temp = stack.pop();
topologicalSort.add(0, temp);
newComponent.add(temp);
info.setVisited(temp);
if (visit != null)
visit.accept(temp);
} while (!temp.equals(vertex));
SCC.add(newComponent);
}
}
}

View File

@@ -0,0 +1,268 @@
package berack96.lib.graph.visit.impl;
import java.util.*;
import java.util.function.Consumer;
import berack96.lib.graph.visit.VisitStrategy;
/**
* The class used for getting the info of the visit.<br>
* It could be used with the algorithm of the visit for set some useful data.
*
* @param <V> the vertex of the visit
* @author Berack96
*/
public class VisitInfo<V> {
private final Map<V, Long> discovered;
private final Map<V, Long> visited;
private final Map<V, V> parent;
private final V source;
private long time;
/**
* Need a source for initialize the basic values
*
* @param source the source of the visit
* @throws NullPointerException if the source is null
*/
public VisitInfo(V source) {
if (source == null)
throw new NullPointerException();
discovered = new Hashtable<>();
visited = new Hashtable<>();
parent = new Hashtable<>();
this.time = 0;
this.source = source;
setDiscovered(source);
}
/**
* The time of the vertex when it is discovered in the visit.<br>
* For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}<br>
* The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time<br>
*
* @param vertex the vertex needed
* @return the time of it's discovery
* @throws IllegalArgumentException if the vertex is not discovered
* @throws NullPointerException if the vertex is null
*/
public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException {
Long time = discovered.get(vertex);
if (time == null)
throw new IllegalArgumentException();
return time;
}
/**
* The time when the vertex is visited by the algorithm<br>
* For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}<br>
* The time starts at 0 and for each vertex discovered or visited is increased by one<br>
*
* @param vertex the vertex needed
* @return the time of it's visit
* @throws IllegalArgumentException if the vertex is not visited
* @throws NullPointerException if the vertex is null
*/
public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException {
Long time = visited.get(vertex);
if (time == null)
throw new IllegalArgumentException();
return time;
}
/**
* Tells if a vertex is discovered or not
*
* @param vertex the vertex chosen
* @return true if is discovered
*/
public boolean isDiscovered(V vertex) throws NullPointerException {
try {
return discovered.containsKey(vertex);
} catch (NullPointerException e) {
return false;
}
}
/**
* Tells if the vertex is visited or not
*
* @param vertex the vertex chosen
* @return true if is visited
*/
public boolean isVisited(V vertex) throws NullPointerException {
try {
return visited.containsKey(vertex);
} catch (NullPointerException e) {
return false;
}
}
/**
* Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.<br>
* Next this call it will be possible to get the time of visit of that vertex<br>
* Does nothing if the vertex is already been visited.
*
* @param vertex the vertex that has been visited
*/
synchronized void setVisited(V vertex) {
setDiscovered(vertex);
if (!visited.containsKey(vertex))
visited.put(vertex, time++);
}
/**
* Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery<br>
* Does nothing if the vertex is already been discovered.
*
* @param vertex the vertex that has been discovered
*/
synchronized void setDiscovered(V vertex) {
if (!discovered.containsKey(vertex))
discovered.put(vertex, time++);
}
/**
* Set the parent of a particular vertex<br>
* The parent of a vertex is the one that has discovered it<br>
* If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called<br>
*
* @param parent the vertex that is the parent
* @param child the vertex discovered
* @throws IllegalArgumentException if the parent is not already discovered
*/
synchronized void setParent(V parent, V child) throws IllegalArgumentException {
if (!isDiscovered(parent))
throw new IllegalArgumentException(parent.toString());
setDiscovered(child);
this.parent.putIfAbsent(child, parent);
}
/**
* Get the source of the visit.
*
* @return the source vertex where it's started the visit
*/
public V getSource() {
return source;
}
/**
* Get the parent of a particular vertex.<br>
* The parent of a vertex is the one that has discovered it<br>
* If the vertex has no parent (it has not been set by the visit algorithm or it's the source) then null is returned.
*
* @param vertex the child vertex
* @return the parent of the child
* @throws IllegalArgumentException if the vertex has not been discovered yet
*/
public V getParentOf(V vertex) throws IllegalArgumentException {
if (isDiscovered(vertex))
return parent.get(vertex);
throw new IllegalArgumentException();
}
/**
* Get all the visited vertices so far.
*
* @return the visited vertices
*/
public Set<V> getVisited() {
return visited.keySet();
}
/**
* Get all the discovered vertices so far.
*
* @return the discovered vertices
*/
public Set<V> getDiscovered() {
return discovered.keySet();
}
/**
* Iterate through all the vertices that are discovered.<br>
* The vertices will be ordered by the time of their discover.
*
* @param consumer the function to apply to each
*/
public void forEachDiscovered(Consumer<VertexInfo> consumer) {
Set<V> vertices = getDiscovered();
Queue<VertexInfo> queue = new PriorityQueue<>();
vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), isVisited(vertex) ? getTimeVisit(vertex) : -1, false)));
while (!queue.isEmpty())
consumer.accept(queue.poll());
}
/**
* Iterate through all the vertices that are visited.<br>
* The vertices will be ordered by the time of their visit.
*
* @param consumer the function to apply to each
*/
public void forEachVisited(Consumer<VertexInfo> consumer) {
Set<V> vertices = getVisited();
Queue<VertexInfo> queue = new PriorityQueue<>();
vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true)));
while (!queue.isEmpty())
consumer.accept(queue.poll());
}
/**
* Iterate through all the vertices discovered and visited with the correct timeline.<br>
* The vertices will be visited in the order that they are discovered and visited, so a vertex can appear two times (one for the discovery, anc the other for the visit)
*
* @param consumer the function to apply at each vertex
*/
public void forEach(Consumer<VertexInfo> consumer) {
Set<V> discovered = getDiscovered();
Set<V> visited = getVisited();
Queue<VertexInfo> queue = new PriorityQueue<>();
discovered.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), false)));
visited.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true)));
while (!queue.isEmpty())
consumer.accept(queue.remove());
}
/**
* Class used mainly for storing the data of the visit
*/
public class VertexInfo implements Comparable<VertexInfo> {
public final V vertex;
public final V parent;
public final long timeDiscovered;
public final long timeVisited;
private final boolean compareVisited;
private VertexInfo(V vertex, V parent, long timeDiscovered, long timeVisited, boolean compareVisited) {
this.vertex = vertex;
this.parent = parent;
this.timeDiscovered = timeDiscovered;
this.timeVisited = timeVisited;
this.compareVisited = compareVisited;
}
@Override
public int compareTo(VertexInfo other) {
long compareThis = compareVisited ? timeVisited : timeDiscovered;
long compareOther = other.compareVisited ? other.timeVisited : other.timeDiscovered;
return (int) (compareThis - compareOther);
}
@Override
public String toString() {
return String.format("%s -> %s [D:%3d, V:%3d]", parent, vertex, timeDiscovered, timeVisited);
}
}
}