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.
* It could be used with the algorithm of the visit for set some useful data. * * @param the vertex of the visit * @author Berack96 */ public class VisitInfo { private final Map discovered; private final Map visited; private final Map 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.
* For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}
* 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
* * @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
* For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}
* The time starts at 0 and for each vertex discovered or visited is increased by one
* * @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.
* Next this call it will be possible to get the time of visit of that vertex
* 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
* 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
* The parent of a vertex is the one that has discovered it
* If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called
* * @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.
* The parent of a vertex is the one that has discovered it
* 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 getVisited() { return visited.keySet(); } /** * Get all the discovered vertices so far. * * @return the discovered vertices */ public Set getDiscovered() { return discovered.keySet(); } /** * Iterate through all the vertices that are discovered.
* The vertices will be ordered by the time of their discover. * * @param consumer the function to apply to each */ public void forEachDiscovered(Consumer consumer) { Set vertices = getDiscovered(); Queue 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.
* The vertices will be ordered by the time of their visit. * * @param consumer the function to apply to each */ public void forEachVisited(Consumer consumer) { Set vertices = getVisited(); Queue 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.
* 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 consumer) { Set discovered = getDiscovered(); Set visited = getVisited(); Queue 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 { 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); } } }