diff --git a/src/berack96/sim/util/graph/Edge.java b/src/berack96/sim/util/graph/Edge.java index d002cef..c8f1965 100644 --- a/src/berack96/sim/util/graph/Edge.java +++ b/src/berack96/sim/util/graph/Edge.java @@ -27,7 +27,7 @@ public class Edge { * * @param source the source of the edge * @param destination the destination of the edge - * @param weight the weight od the edge + * @param weight the weight of the edge */ public Edge(V source, V destination, W weight) { this.source = source; diff --git a/src/berack96/sim/util/graph/Graph.java b/src/berack96/sim/util/graph/Graph.java index 63a141f..98d285a 100644 --- a/src/berack96/sim/util/graph/Graph.java +++ b/src/berack96/sim/util/graph/Graph.java @@ -4,6 +4,7 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -24,11 +25,12 @@ import berack96.sim.util.graph.visit.VisitStrategy; */ public interface Graph extends Iterable { - String NOT_DAG = "The graph is not a DAG"; - String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination"; - String PARAM_NULL = "The parameter must not be null"; - String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph"; - + 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.
* A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G), @@ -515,7 +517,7 @@ public interface Graph extends Iterable { /** * 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 marker is not specified or 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 one or more markers @@ -547,19 +549,21 @@ public interface Graph extends Iterable { Map>> distance(V source) throws NullPointerException, IllegalArgumentException; static void save(Graph graph, String file) throws IOException { - GraphSaveStructure save = new GraphSaveStructure<>(graph); - Gson gson = new Gson(); + save(graph, "", file); + } + + static void save(Graph graph, String other, String file) throws IOException { + GraphSaveStructure save = new GraphSaveStructure<>(graph, other); FileWriter writer = new FileWriter(file); - writer.write(gson.toJson(save)); + GSON.toJson(save, writer); writer.close(); } @SuppressWarnings("unchecked") - static void load(Graph graph, String file) throws IOException { + static String load(Graph graph, String file, Class classV, Class classW) throws IOException { graph.removeAllVertex(); - Gson gson = new Gson(); FileReader reader = new FileReader(file); StringBuilder fileContent = new StringBuilder(); int c; @@ -567,21 +571,53 @@ public interface Graph extends Iterable { while((c = reader.read()) != -1) fileContent.append((char)c); reader.close(); - GraphSaveStructure save = gson.fromJson(fileContent.toString(), GraphSaveStructure.class); + GraphSaveStructure save = GSON.fromJson(fileContent.toString(), GraphSaveStructure.class); - graph.addAllVertices(save.vertices); - graph.addAllEdges(save.edges); + for(String str : save.vertices) + graph.addVertex(GSON.fromJson(str, classV)); + + for(int i = 0; i { public GraphSaveStructure() {} - protected GraphSaveStructure(Graph graph) { - vertices = graph.vertices(); - edges = graph.edges(); + protected GraphSaveStructure(Graph graph, String other) { + vertices = new LinkedList<>(); + + for(V v: graph.vertices()) + vertices.add(GSON.toJson(v)); + edges = new EdgeSaveStructure<>(graph.edges()); + this.other = other; } - public Collection vertices; - public Collection> edges; + public List vertices; + public EdgeSaveStructure edges; + public String other; + } + + class EdgeSaveStructure { + public EdgeSaveStructure() {} + protected EdgeSaveStructure(Collection> edges) { + src = new LinkedList<>(); + dest = new LinkedList<>(); + weight = new LinkedList<>(); + + for(Edge ed : edges) { + src.add(GSON.toJson(ed.getSource())); + dest.add(GSON.toJson(ed.getDestination())); + weight.add(GSON.toJson(ed.getWeight())); + } + } + + public List src; + public List dest; + public List weight; } // TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser diff --git a/src/berack96/sim/util/graph/MapGraph.java b/src/berack96/sim/util/graph/MapGraph.java index 1cd47d1..ae0fa6d 100644 --- a/src/berack96/sim/util/graph/MapGraph.java +++ b/src/berack96/sim/util/graph/MapGraph.java @@ -416,17 +416,18 @@ public class MapGraph implements Graph { final Graph sub = new MapGraph<>(); final Set allVertices = new HashSet<>(); final Set allMarkers = new HashSet<>(); + final boolean isEmpty = (marker == null || marker.length == 0); - if (marker != null) + if (!isEmpty) for (Object mark: marker) allMarkers.add(mark); markers.forEach( (mark, set) -> { - if (marker == null || allMarkers.contains(mark)) + if (isEmpty || allMarkers.contains(mark)) allVertices.addAll(set); }); - - if (marker == null) { + + if (isEmpty) { Collection toAdd = vertices(); toAdd.removeAll(allVertices); allVertices.clear(); diff --git a/src/berack96/sim/util/graph/view/GraphInfo.java b/src/berack96/sim/util/graph/view/GraphInfo.java index e6e2530..f599f5e 100644 --- a/src/berack96/sim/util/graph/view/GraphInfo.java +++ b/src/berack96/sim/util/graph/view/GraphInfo.java @@ -4,8 +4,6 @@ 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; @@ -22,8 +20,6 @@ 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; @@ -141,24 +137,34 @@ public class GraphInfo extends JPanel { 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 { - Graph.save(graphPanel.getGraph(), fileText.getText()); + graphPanel.save(fileText.getText()); + textResult.setText(""); } catch (IOException e1) { e1.printStackTrace(); + textResult.setText(e1.getMessage()); } }); JButton loadB = new JButton("Load"); loadB.addActionListener(a -> { try { - Graph graph = graphPanel.getGraph(); - Graph.load((Graph) graph, fileText.getText()); + graphPanel.load(fileText.getText()); + textResult.setText(""); } catch (IOException e1) { e1.printStackTrace(); + textResult.setText(e1.getMessage()); } }); components.add(new JLabel("File to save/load: ")); @@ -172,7 +178,6 @@ public class GraphInfo extends JPanel { 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); @@ -182,6 +187,8 @@ public class GraphInfo extends JPanel { this.add(panelDescription); this.add(panelInfo); this.add(panelVertex); + this.add(panelErrors); + this.add(panelSave); /*this.add(panelSave);*/ modVertex.doClick(); diff --git a/src/berack96/sim/util/graph/view/GraphPanel.java b/src/berack96/sim/util/graph/view/GraphPanel.java index b547089..c15c7ee 100644 --- a/src/berack96/sim/util/graph/view/GraphPanel.java +++ b/src/berack96/sim/util/graph/view/GraphPanel.java @@ -1,21 +1,31 @@ package berack96.sim.util.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.sim.util.graph.Edge; import berack96.sim.util.graph.Graph; import berack96.sim.util.graph.MapGraph; import berack96.sim.util.graph.Vertex; import berack96.sim.util.graph.view.edge.EdgeComponent; import berack96.sim.util.graph.view.vertex.VertexComponent; -import java.awt.*; -import java.awt.event.KeyListener; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.util.Collection; -import java.util.HashSet; -import java.util.Observer; -import java.util.Set; - -@SuppressWarnings("unchecked") +@SuppressWarnings({"unchecked", "deprecation"}) public class GraphPanel extends Component { private static final long serialVersionUID = 1L; @@ -60,8 +70,14 @@ public class GraphPanel extends Component { if (component == null) { VertexComponent v = new VertexComponent<>(new Vertex<>(graph, vertex)); - if (!graph.contains(vertex)) { - v.vertex.addIfAbsent(); + 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); } @@ -81,6 +97,20 @@ public class GraphPanel extends Component { vertex.setLocation(rectangle.x, rectangle.y); } + public void addEdge(Edge edge) { + VertexComponent vSource = null; + VertexComponent vDest = null; + for (Component comp : vertices.getComponents()) { + VertexComponent temp = (VertexComponent) 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 source, VertexComponent dest, W weight) { try { Point center = new Point(Math.abs(source.getX() - dest.getY()), Math.abs(source.getY() - dest.getY())); @@ -88,7 +118,9 @@ public class GraphPanel extends Component { edgeComponent.setBounds(edgeRender.getBox(edgeComponent, center)); edges.add(edgeComponent); graph.addEdge(edgeComponent.edge); - } catch (Exception ignore) {} + } catch (Exception ignore) { + ignore.printStackTrace(); + } } public void removeEdge(VertexComponent source, VertexComponent dest) { @@ -128,6 +160,26 @@ public class GraphPanel extends Component { 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); + GraphGraphicalSave save = Graph.GSON.fromJson(saveContent, GraphGraphicalSave.class); + vertices.removeAll(); + edges.removeAll(); + + for(int i = 0; i graph.getEdgesOut(v).forEach(e -> addEdge(e))); + + repaint(); + } @Override public void setBounds(int x, int y, int width, int height) { @@ -170,5 +222,20 @@ public class GraphPanel extends Component { private void updateObservers() { observers.forEach(observer -> observer.update(null, this.graph)); } - + + class GraphGraphicalSave { + public GraphGraphicalSave() {} + protected GraphGraphicalSave(Container vertices) { + this.vertices = new LinkedList<>(); + this.points = new LinkedList<>(); + + for(Component vertex : vertices.getComponents()) { + this.points.add(new Point(vertex.getX(), vertex.getY())); + this.vertices.add(((VertexComponent) vertex).vertex.getValue()); + } + } + + public List vertices; + public List points; + } } diff --git a/src/berack96/sim/util/graph/view/vertex/VertexComponent.java b/src/berack96/sim/util/graph/view/vertex/VertexComponent.java index 8cd00c0..3502cf8 100644 --- a/src/berack96/sim/util/graph/view/vertex/VertexComponent.java +++ b/src/berack96/sim/util/graph/view/vertex/VertexComponent.java @@ -12,4 +12,19 @@ public class VertexComponent extends Component { public VertexComponent(Vertex vertex) { this.vertex = vertex; } + + + @Override + public String toString() { + return "[" + vertex + " {" + getX() + "," + getY() + "}]"; + } + + @Override + public boolean equals(Object obj) { + try { + return obj.getClass().equals(getClass()) && obj.toString().equals(toString()); + } catch (Exception e) { + return false; + } + } } diff --git a/test/berack96/test/sim/TestGraph.java b/test/berack96/test/sim/TestGraph.java index 48e31d6..03313ee 100644 --- a/test/berack96/test/sim/TestGraph.java +++ b/test/berack96/test/sim/TestGraph.java @@ -64,6 +64,7 @@ public class TestGraph { public void after() { try { String printed = bytes.toString(encoding); + bytes.reset(); if (!printed.isEmpty()) fail("Remove the printed string in the methods: " + printed); } catch (UnsupportedEncodingException e) { @@ -1186,6 +1187,10 @@ public class TestGraph { new Edge<>("5", "4", 5), new Edge<>("6", "2", 2)); + sub = graph.subGraph(); + shouldContain(sub.vertices(), "7", "8"); + shouldContain(sub.edges(), new Edge<>("8", "7", 9)); + sub = graph.subGraph(null); shouldContain(sub.vertices(), "7", "8"); shouldContain(sub.edges(), new Edge<>("8", "7", 9)); @@ -1325,37 +1330,54 @@ public class TestGraph { */ String fileName = "test/resources/test.json"; + Set vertices = new HashSet<>(); + Set> edges = new HashSet<>(); - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + vertices.add("1"); + vertices.add("2"); + vertices.add("3"); + vertices.add("4"); + vertices.add("5"); + vertices.add("6"); + vertices.add("7"); + vertices.add("8"); - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 1); - graph.addEdge("2", "5", 4); - graph.addEdge("4", "6", 6); - graph.addEdge("5", "3", 2); - graph.addEdge("5", "4", 5); - graph.addEdge("6", "2", 2); - graph.addEdge("8", "7", 9); + edges.add(new Edge<>("1", "2", 1)); + edges.add(new Edge<>("1", "3", 1)); + edges.add(new Edge<>("2", "5", 4)); + edges.add(new Edge<>("4", "6", 6)); + edges.add(new Edge<>("5", "3", 2)); + edges.add(new Edge<>("5", "4", 5)); + edges.add(new Edge<>("6", "2", 2)); + edges.add(new Edge<>("8", "7", 9)); + + graph.addAllVertices(vertices); + graph.addAllEdges(edges); + + /* because GSON convert to double */ + /* + Set> edgesD = new HashSet<>(); + edges.forEach((e) -> edgesD.add(new Edge(e.getSource(), e.getDestination(), e.getWeight().doubleValue()))); + */ + Set> edgesD = edges; try { Graph.save(graph, fileName); - Graph.load(graph, fileName); + Graph.load(graph, fileName, String.class, Integer.class); + shouldContain(graph.vertices(), vertices.toArray()); + shouldContain(graph.edges(), edgesD.toArray()); + graph.removeAllVertex(); - Graph.load(graph, fileName); + Graph.load(graph, fileName, String.class, Integer.class); + shouldContain(graph.vertices(), vertices.toArray()); + shouldContain(graph.edges(), edgesD.toArray()); } catch (Exception e) { fail(e.getMessage()); } graph = null; shouldThrow(new NullPointerException(), () -> { try { - Graph.load(graph, fileName); + Graph.load(graph, fileName, String.class, Integer.class); } catch (IOException e) { fail(); e.printStackTrace(); diff --git a/test/resources/test.json b/test/resources/test.json index eb6138f..a35f98b 100644 --- a/test/resources/test.json +++ b/test/resources/test.json @@ -1 +1 @@ -{"vertices":["1","2","3","4","5","6","7","8"],"edges":[{"source":"1","destination":"2","weight":1},{"source":"1","destination":"3","weight":1},{"source":"5","destination":"4","weight":5},{"source":"6","destination":"2","weight":2},{"source":"5","destination":"3","weight":2},{"source":"8","destination":"7","weight":9},{"source":"4","destination":"6","weight":6},{"source":"2","destination":"5","weight":4}]} \ No newline at end of file +{"vertices":["1","2","3","4","5","6","7","8"],"edges":[{"source":"1","destination":"2","weight":1},{"source":"1","destination":"3","weight":1},{"source":"5","destination":"4","weight":5},{"source":"6","destination":"2","weight":2},{"source":"5","destination":"3","weight":2},{"source":"8","destination":"7","weight":9},{"source":"4","destination":"6","weight":6},{"source":"2","destination":"5","weight":4}],"other":""} \ No newline at end of file