Save/Load

* improved Tests
* fixed subGraph of markers
* added support for save/load graphically
* not all pass... must continue coding
This commit is contained in:
2019-06-14 18:02:56 +02:00
parent b4c5575bec
commit e9f7375ca4
8 changed files with 215 additions and 67 deletions

View File

@@ -27,7 +27,7 @@ public class Edge<V, W extends Number> {
*
* @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;

View File

@@ -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<V, W extends Number> extends Iterable<V> {
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.<br>
* 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<V, W extends Number> extends Iterable<V> {
/**
* 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 null then the returning graph will have all the vertices that are not marked by any 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
@@ -547,19 +549,21 @@ public interface Graph<V, W extends Number> extends Iterable<V> {
Map<V, List<Edge<V, W>>> 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 <V, W extends Number> void save(Graph<V, W> graph, String other, String file) throws IOException {
GraphSaveStructure<V, W> save = new GraphSaveStructure<>(graph, other);
FileWriter writer = new FileWriter(file);
writer.write(gson.toJson(save));
GSON.toJson(save, writer);
writer.close();
}
@SuppressWarnings("unchecked")
static <V, W extends Number> void load(Graph<V, W> graph, String file) throws IOException {
static <V, W extends Number> String load(Graph<V, W> graph, String file, Class<V> classV, Class<W> 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<V, W extends Number> extends Iterable<V> {
while((c = reader.read()) != -1)
fileContent.append((char)c);
reader.close();
GraphSaveStructure<V, W> save = gson.fromJson(fileContent.toString(), GraphSaveStructure.class);
GraphSaveStructure<V, W> 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<save.edges.src.size(); i++) {
V s = GSON.fromJson(save.edges.src.get(i), classV);
V d = GSON.fromJson(save.edges.dest.get(i), classV);
W w = GSON.fromJson(save.edges.weight.get(i), classW);
graph.addEdge(s, d, w);
}
return save.other;
}
class GraphSaveStructure<V, W extends Number> {
public GraphSaveStructure() {}
protected GraphSaveStructure(Graph<V, W> graph) {
vertices = graph.vertices();
edges = graph.edges();
protected GraphSaveStructure(Graph<V, W> 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<V> vertices;
public Collection<Edge<V, W>> edges;
public List<String> vertices;
public EdgeSaveStructure<V, W> edges;
public String other;
}
class EdgeSaveStructure<V, W extends Number> {
public EdgeSaveStructure() {}
protected EdgeSaveStructure(Collection<Edge<V, W>> edges) {
src = new LinkedList<>();
dest = new LinkedList<>();
weight = new LinkedList<>();
for(Edge<V, W> ed : edges) {
src.add(GSON.toJson(ed.getSource()));
dest.add(GSON.toJson(ed.getDestination()));
weight.add(GSON.toJson(ed.getWeight()));
}
}
public List<String> src;
public List<String> dest;
public List<String> weight;
}
// TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser

View File

@@ -416,17 +416,18 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
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 (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<V> toAdd = vertices();
toAdd.removeAll(allVertices);
allVertices.clear();

View File

@@ -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<V, W extends Number> 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<V, W> graph = graphPanel.getGraph();
Graph.load((Graph<Object, Number>) 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<V, W extends Number> 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<V, W extends Number> extends JPanel {
this.add(panelDescription);
this.add(panelInfo);
this.add(panelVertex);
this.add(panelErrors);
this.add(panelSave);
/*this.add(panelSave);*/
modVertex.doClick();

View File

@@ -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<V, W extends Number> extends Component {
private static final long serialVersionUID = 1L;
@@ -60,8 +70,14 @@ public class GraphPanel<V, W extends Number> extends Component {
if (component == null) {
VertexComponent<V> 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<V, W extends Number> extends Component {
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()));
@@ -88,7 +118,9 @@ public class GraphPanel<V, W extends Number> 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<V> source, VertexComponent<V> dest) {
@@ -128,6 +160,26 @@ public class GraphPanel<V, W extends Number> 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<save.vertices.size(); i++) {
V v = save.vertices.get(i);
Point p = save.points.get(i);
addVertex(p, v);
}
save.vertices.forEach(v -> 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<V, W extends Number> 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<V>) vertex).vertex.getValue());
}
}
public List<V> vertices;
public List<Point> points;
}
}

View File

@@ -12,4 +12,19 @@ public class VertexComponent<V> extends Component {
public VertexComponent(Vertex<V> 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;
}
}
}

View File

@@ -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<String> vertices = new HashSet<>();
Set<Edge<String, Integer>> 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<Edge<String, Double>> edgesD = new HashSet<>();
edges.forEach((e) -> edgesD.add(new Edge<String, Double>(e.getSource(), e.getDestination(), e.getWeight().doubleValue())));
*/
Set<Edge<String, Integer>> 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();

View File

@@ -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}]}
{"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":""}