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