diff --git a/.gitignore b/.gitignore
index 408ed1e..786237e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,3 +129,4 @@ local.properties
.project
*.iml
.classpath
+/.gradle/
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..b01d19f
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,15 @@
+apply plugin: 'java'
+apply plugin: 'eclipse'
+
+version='1.0-SNAPSHOT'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
+
+ compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
+ testCompile group: 'junit', name: 'junit', version: '4.+'
+}
\ No newline at end of file
diff --git a/src/berack96/sim/util/graph/Graph.java b/src/berack96/sim/util/graph/Graph.java
index 5ae7afa..276ca86 100644
--- a/src/berack96/sim/util/graph/Graph.java
+++ b/src/berack96/sim/util/graph/Graph.java
@@ -1,13 +1,19 @@
package berack96.sim.util.graph;
-import berack96.sim.util.graph.visit.VisitInfo;
-import berack96.sim.util.graph.visit.VisitStrategy;
-
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
+import com.google.gson.Gson;
+
+import berack96.sim.util.graph.visit.VisitInfo;
+import berack96.sim.util.graph.visit.VisitStrategy;
+
/**
* An interface for the graphs.
* This interface is used for the graphs with Directed edges.
@@ -508,15 +514,15 @@ public interface Graph extends Iterable {
Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException;
/**
- * Get a sub-graph of the current one with only the vertex marked with the selected marker.
+ * Get a sub-graph of the current one with only the vertex marked with the selected markers.
* Each vertex will have all his edges, but only the ones with the destination marked with the same marker.
* If the marker is null then the returning graph will have all the vertices that are not marked by any marker.
* If the graph doesn't contain any vertex with that marker then an empty graph is returned.
*
- * @param marker the marker
+ * @param marker one or more markers
* @return a sub-graph of the current graph
*/
- Graph subGraph(Object marker);
+ Graph subGraph(Object...marker);
/**
* Get the minimum path from the source vertex to the destination vertex.
@@ -541,6 +547,35 @@ public interface Graph extends Iterable {
*/
Map>> distance(V source) throws NullPointerException, IllegalArgumentException;
+ /*
+ static void save(Graph, ?> graph, String file) throws IOException {
+ Map map = new HashMap<>();
+ map.put("vertices", graph.vertices());
+ map.put("edges", graph.edges());
+
+ Gson gson = new Gson();
+ FileWriter writer = new FileWriter(file);
+ writer.write(gson.toJson(map));
+ writer.close();
+ }
+
+ @SuppressWarnings("unchecked")
+ static void load(Graph graph, String file) throws IOException {
+ Gson gson = new Gson();
+ FileReader reader = new FileReader(file);
+ StringBuilder fileContent = new StringBuilder();
+ int c;
+
+ while((c = reader.read()) != -1)
+ fileContent.append(c);
+ reader.close();
+
+ Map map = gson.fromJson(fileContent.toString(), Map.class);
+ graph.addAllVertices((Collection)map.get("vertices"));
+ graph.addAllEdges((Collection>)map.get("edges"));
+ }
+ */
+
// TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser
// TODO maybe, but i don't think so... STATIC DISTANCE V* -> V*
}
diff --git a/src/berack96/sim/util/graph/MapGraph.java b/src/berack96/sim/util/graph/MapGraph.java
index 6e50cce..1cd47d1 100644
--- a/src/berack96/sim/util/graph/MapGraph.java
+++ b/src/berack96/sim/util/graph/MapGraph.java
@@ -30,9 +30,11 @@ public class MapGraph implements Graph {
private final Map> edges = new HashMap<>();
/**
- * Map that contains the vertex as key and all the marker as the value associated with that vertex.
+ * Map that contains the marker as key and a set of all the vertices that has it as the value.
+ * This map is build like this for performance in creating the marker for multiple vertices.
+ * If you flip the parameters (object and set) then has more performance over the single vertex.
*/
- private final Map> marked = new HashMap<>();
+ private final Map> markers = new HashMap<>();
/**
* Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change
@@ -42,7 +44,7 @@ public class MapGraph implements Graph {
/**
* 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> dijkstra = null;
+ private Map> dijkstra = new HashMap<>();
@Override
public boolean isCyclic() {
@@ -63,8 +65,8 @@ public class MapGraph implements Graph {
@Override
public void addVertex(V vertex) throws NullPointerException {
checkNull(vertex);
- graphChanged();
edges.put(vertex, new HashMap<>());
+ graphChanged();
}
@Override
@@ -84,16 +86,18 @@ public class MapGraph implements Graph {
@Override
public void removeVertex(V vertex) throws NullPointerException {
if (contains(vertex)) {
- graphChanged();
edges.remove(vertex);
edges.forEach((v, map) -> map.remove(vertex));
+ markers.forEach((mark, set) -> set.remove(vertex));
+ graphChanged();
}
}
@Override
public void removeAllVertex() {
- graphChanged();
edges.clear();
+ markers.clear();
+ graphChanged();
}
@Override
@@ -107,50 +111,51 @@ public class MapGraph implements Graph {
checkNullAndExist(vertex);
checkNull(mark);
- Set set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
- set.add(mark);
+ Set 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);
- marked.get(vertex).remove(mark);
+ markers.get(mark).remove(vertex);
}
@Override
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
- marked.get(vertex).clear();
+ markers.forEach( (mark, set) -> set.remove(vertex) );
}
@Override
public Collection getMarkedWith(Object mark) throws NullPointerException {
checkNull(mark);
- Collection ret = new HashSet();
- marked.forEach((v, set) -> {
- if(set.contains(mark))
- ret.add(v);
- });
-
- return ret;
+ return markers.computeIfAbsent(mark, (v) -> new HashSet<>());
}
@Override
- public Set getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
+ public Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
- return marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
+
+ Collection marks = new HashSet<>();
+ markers.forEach( (mark, set) -> {
+ if (set.contains(vertex))
+ marks.add(mark);
+ });
+
+ return marks;
}
@Override
public void unMarkAll(Object mark) {
checkNull(mark);
- marked.forEach((v, m) -> m.remove(mark));
+ markers.remove(mark);
}
@Override
public void unMarkAll() {
- marked.clear();
+ markers.clear();
}
@Override
@@ -159,8 +164,9 @@ public class MapGraph implements Graph {
checkNullAndExist(vertex2);
checkNull(weight);
+ W old = edges.get(vertex1).put(vertex2, weight);
graphChanged();
- return edges.get(vertex1).put(vertex2, weight);
+ return old;
}
@Override
@@ -198,24 +204,24 @@ public class MapGraph implements Graph {
checkNullAndExist(vertex1);
checkNullAndExist(vertex2);
- graphChanged();
edges.get(vertex1).remove(vertex2);
+ graphChanged();
}
@Override
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
- graphChanged();
edges.forEach((v, map) -> map.remove(vertex));
+ graphChanged();
}
@Override
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
- graphChanged();
edges.put(vertex, new HashMap<>());
+ graphChanged();
}
@Override
@@ -227,8 +233,8 @@ public class MapGraph implements Graph {
@Override
public void removeAllEdge() {
- graphChanged();
edges.forEach((v, map) -> map.clear());
+ graphChanged();
}
@Override
@@ -237,19 +243,19 @@ public class MapGraph implements Graph {
}
@Override
- public Set vertices() {
+ public Collection vertices() {
return new HashSet<>(edges.keySet());
}
@Override
- public Set> edges() {
+ public Collection> edges() {
Set> allEdges = new HashSet<>();
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
return allEdges;
}
@Override
- public Set> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
+ public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Set> set = new HashSet<>();
@@ -282,14 +288,14 @@ public class MapGraph implements Graph {
}
@Override
- public Set getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
+ public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
return new HashSet<>(edges.get(vertex).keySet());
}
@Override
- public Set getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
+ public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Set set = new HashSet<>();
@@ -406,32 +412,36 @@ public class MapGraph implements Graph {
}
@Override
- public Graph subGraph(final Object marker) {
- final Graph graph = new MapGraph<>();
+ public Graph subGraph(final Object...marker) {
+ final Graph sub = new MapGraph<>();
final Set allVertices = new HashSet<>();
+ final Set allMarkers = new HashSet<>();
+
+ if (marker != null)
+ for (Object mark: marker)
+ allMarkers.add(mark);
- marked.forEach((vertex, mark) -> {
- if (mark.contains(marker) || (marker == null && !mark.isEmpty()))
- allVertices.add(vertex);
+ markers.forEach( (mark, set) -> {
+ if (marker == null || allMarkers.contains(mark))
+ allVertices.addAll(set);
});
-
+
if (marker == null) {
- Collection toAdd = graph.vertices();
+ Collection toAdd = vertices();
toAdd.removeAll(allVertices);
allVertices.clear();
allVertices.addAll(toAdd);
}
- graph.addAllVertices(allVertices);
- for (V vertex : graph.vertices())
- getEdgesOut(vertex).forEach((edge) -> {
+ sub.addAllVertices(allVertices);
+ for (V vertex : sub.vertices())
+ edges.get(vertex).forEach( (dest, weight) -> {
try {
- graph.addEdge(edge);
- } catch (Exception ignored) {
- }
+ sub.addEdge(vertex, dest, weight);
+ } catch (Exception ignored) {}
});
- return graph;
+ return sub;
}
@Override
@@ -439,7 +449,7 @@ public class MapGraph implements Graph {
checkNullAndExist(source);
checkNullAndExist(destination);
- Dijkstra dijkstra = getDijkstra(source);
+ Dijkstra dijkstra = getDijkstra(source); /* Cached */
List> path = dijkstra.getLastDistance().get(destination);
if (path == null)
throw new UnsupportedOperationException(NOT_CONNECTED);
@@ -449,7 +459,7 @@ public class MapGraph implements Graph {
@Override
public Map>> distance(V source) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(source);
- return new HashMap<>(getDijkstra(source).getLastDistance());
+ return new HashMap<>(getDijkstra(source).getLastDistance()); /* Cached */
}
@Override
@@ -459,16 +469,20 @@ public class MapGraph implements Graph {
/**
- * Simple function that set all the memory vars at null if the graph changed
+ * Simple function that reset all the caching variables if the graph changed
*/
private void graphChanged() {
tarjan = null;
- dijkstra = null;
+ dijkstra.clear();
}
+ /**
+ * Simple function that return the result of the Dijkstra visit, with the starting point as source.
+ * 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 getDijkstra(V source) {
- if (dijkstra == null)
- dijkstra = new HashMap<>();
if (dijkstra.get(source) == null) {
Dijkstra newDijkstra = new Dijkstra<>();
newDijkstra.visit(this, source, null);
@@ -478,6 +492,11 @@ public class MapGraph implements Graph {
return dijkstra.get(source);
}
+ /**
+ * Simple function that return the result of the Tarjan visit.
+ * It also cache it, so multiple call will return always the same value unless the graph has changed.
+ * @return the tarjan visit
+ */
private Tarjan getTarjan() {
if (tarjan == null) {
tarjan = new Tarjan<>();
@@ -487,11 +506,21 @@ public class MapGraph implements Graph {
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))
diff --git a/src/berack96/sim/util/graph/view/GraphInfo.java b/src/berack96/sim/util/graph/view/GraphInfo.java
index 7794dca..e6e2530 100644
--- a/src/berack96/sim/util/graph/view/GraphInfo.java
+++ b/src/berack96/sim/util/graph/view/GraphInfo.java
@@ -1,17 +1,34 @@
package berack96.sim.util.graph.view;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridLayout;
+import java.awt.event.ItemEvent;
+import java.io.File;
+import java.io.FileWriter;
+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 com.google.gson.Gson;
+
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.view.edge.EdgeListener;
import berack96.sim.util.graph.view.vertex.VertexListener;
import berack96.sim.util.graph.visit.VisitStrategy;
-import javax.swing.*;
-import javax.swing.border.BevelBorder;
-import java.awt.*;
-import java.awt.event.ItemEvent;
-import java.util.*;
-import java.util.List;
-
public class GraphInfo extends JPanel {
private static final long serialVersionUID = 1L;
@@ -122,10 +139,40 @@ public class GraphInfo extends JPanel {
panelVertex.setOpaque(false);
panelVertex.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelVertex::add);
+ components.clear();
- /* Save/Load
- JPanel panelSave = new JPanel(); */
-
+ /* SAVE/LOAD */
+ /*
+ JTextField fileText = new JTextField();
+ JButton saveB = new JButton("Save");
+ saveB.addActionListener(a -> {
+ try {
+ Graph.save(graphPanel.getGraph(), fileText.getText());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ });
+ JButton loadB = new JButton("Load");
+ loadB.addActionListener(a -> {
+ try {
+ Graph graph = graphPanel.getGraph();
+ Graph.load((Graph) graph, fileText.getText());
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ });
+ 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);
@@ -135,7 +182,7 @@ public class GraphInfo extends JPanel {
this.add(panelDescription);
this.add(panelInfo);
this.add(panelVertex);
- this.add(Box.createVerticalGlue());
+ /*this.add(panelSave);*/
modVertex.doClick();
diff --git a/src/berack96/sim/util/graph/view/GraphWindow.java b/src/berack96/sim/util/graph/view/GraphWindow.java
index f64c821..10bfb29 100644
--- a/src/berack96/sim/util/graph/view/GraphWindow.java
+++ b/src/berack96/sim/util/graph/view/GraphWindow.java
@@ -50,7 +50,6 @@ public class GraphWindow extends JFrame {
this.infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats);
this.graphPanel = graphPanel;
-
this.add(infoPanel, BorderLayout.EAST);
this.add(graphPanel);
}
diff --git a/test/berack96/test/sim/TestGraph.java b/test/berack96/test/sim/TestGraph.java
index a5334a5..b47c2c1 100644
--- a/test/berack96/test/sim/TestGraph.java
+++ b/test/berack96/test/sim/TestGraph.java
@@ -1022,11 +1022,11 @@ public class TestGraph {
/*
* This graph should be like this
*
- * 1 -> 2 <- 6
- * ^
- * | | |
+ * 1 -> 2 <- 6 7
+ * ^ ^
+ * | | | |
* v v
- * 3 <- 5 -> 4
+ * 3 <- 5 -> 4 8
*/
graph.addVertexIfAbsent("1");
@@ -1035,6 +1035,8 @@ public class TestGraph {
graph.addVertexIfAbsent("4");
graph.addVertexIfAbsent("5");
graph.addVertexIfAbsent("6");
+ graph.addVertexIfAbsent("7");
+ graph.addVertexIfAbsent("8");
graph.addEdge("1", "2", 1);
graph.addEdge("1", "3", 1);
@@ -1043,6 +1045,7 @@ public class TestGraph {
graph.addEdge("5", "3", 2);
graph.addEdge("5", "4", 5);
graph.addEdge("6", "2", 2);
+ graph.addEdge("8", "7", 9);
graph.mark("1", "blue");
graph.mark("3", "blue");
@@ -1104,14 +1107,14 @@ public class TestGraph {
/* MARKED */
sub = graph.subGraph("z");
- shouldContain(sub.vertices(), "1", "2", "5", "4");
+ shouldContain(sub.vertices(), "1", "2", "4", "5");
shouldContain(sub.edges(),
new Edge<>("1", "2", 1),
new Edge<>("2", "5", 4),
new Edge<>("5", "4", 5));
sub = graph.subGraph("circle");
- shouldContain(sub.vertices(), "2", "5", "4", "6");
+ shouldContain(sub.vertices(), "2", "4", "5", "6");
shouldContain(sub.edges(),
new Edge<>("2", "5", 4),
new Edge<>("4", "6", 6),
@@ -1129,6 +1132,21 @@ public class TestGraph {
shouldContain(sub.edges(),
new Edge<>("4", "6", 6),
new Edge<>("6", "2", 2));
+
+ sub = graph.subGraph("blue", "circle");
+ shouldContain(sub.vertices(), "1", "2", "3", "4", "5", "6");
+ shouldContain(sub.edges(),
+ new Edge<>("1", "2", 1),
+ new Edge<>("1", "3", 1),
+ new Edge<>("2", "5", 4),
+ new Edge<>("4", "6", 6),
+ new Edge<>("5", "3", 2),
+ new Edge<>("5", "4", 5),
+ new Edge<>("6", "2", 2));
+
+ sub = graph.subGraph(null);
+ shouldContain(sub.vertices(), "7", "8");
+ shouldContain(sub.edges(), new Edge<>("8", "7", 9));
}
@Test