package ui.controller; import classes.*; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.ArchiveStreamFactory; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.utils.IOUtils; import ui.model.Model; import java.awt.geom.Point2D; import java.io.*; import java.util.*; import java.util.List; /** * Controller for Storage. * * @author Gruppe14 */ public class SaveController { private Model model; private int nCat, nObj, nEle, nEdge, nConn, nNodeEdge, nOldEdge, nUnitGraph, nStatsGraph; /** * Constructor. * * @param model the Model */ SaveController(Model model) { this.model = model; } /** * Writes the current State of the Modelling into a JSON File which can be * loaded. * * @param path the Path * @throws IOException exception */ void writeSave(String path) throws IOException, ArchiveException { File dst = new File(path); File holonFile = File.createTempFile("tmp", ".json"); holonFile.deleteOnExit(); dst.delete(); OutputStream output = new FileOutputStream(dst); ArchiveOutputStream stream = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, output); initNumeration(); JsonObject file = new JsonObject(); initialize(MODE.COMPLETE, file); storeCategory(file); storeCanvas(file); storeData(stream); FileWriter writer = new FileWriter(holonFile); writer.write(model.getGson().toJson(file)); writer.flush(); writer.close(); addFileToSave(holonFile, stream, false); stream.finish(); output.close(); } /** * Writes the windows status (its position and dimensions) * * @param path the Path where to save it */ void writeWindowStatus(String path, int x, int y, int width, int height) throws IOException, ArchiveException { JsonObject file = new JsonObject(); initialize(MODE.SIZE, file); storeWindowPosAndSize(file, x, y, width, height); FileWriter writer = new FileWriter(path); writer.write(model.getGson().toJson(file)); writer.flush(); writer.close(); } /** * Writes the Autosave File. * * @param path the Path * @throws IOException Exception */ void writeAutosave(String path) throws IOException { initNumeration(); JsonObject file = new JsonObject(); initialize(MODE.PARTIAL, file); storeCanvas(file); FileWriter writer = new FileWriter(path); writer.write(model.getGson().toJson(file)); writer.flush(); writer.close(); } /** * Writes the Category File in Case of Changes */ void writeCategory(String path) throws IOException { initNumeration(); JsonObject file = new JsonObject(); initialize(MODE.CATEGORY, file); storeCategory(file); FileWriter writer = new FileWriter(path); writer.write(model.getGson().toJson(file)); writer.flush(); writer.close(); } /** * Write needed default parameter into the JsonObject. Can be extended later * on */ private void initialize(MODE mode, JsonObject file) { switch (mode) { case COMPLETE: file.add("MODE", new JsonPrimitive(mode.name())); file.add("IDCOUNTER", new JsonPrimitive(IdCounter.getCounter())); file.add("IDCOUNTERELEMENT", new JsonPrimitive(IdCounterElem.getCounter())); file.add("CANVAS_SIZE_X", new JsonPrimitive(model.getCanvasX())); file.add("CANVAS_SIZE_Y", new JsonPrimitive(model.getCanvasY())); break; case PARTIAL: file.add("MODE", new JsonPrimitive(mode.name())); file.add("IDCOUNTER", new JsonPrimitive(IdCounter.getCounter())); file.add("IDCOUNTERELEMENT", new JsonPrimitive(IdCounterElem.getCounter())); file.add("CANVAS_SIZE_X", new JsonPrimitive(model.getCanvasX())); file.add("CANVAS_SIZE_Y", new JsonPrimitive(model.getCanvasY())); break; case CATEGORY: file.add("MODE", new JsonPrimitive(mode.name())); break; case SIZE: file.add("MODE", new JsonPrimitive(mode.name())); default: break; } } /** * Store all Categories and Object into a Json File via Serialization */ private void storeCategory(JsonObject file) { // forall categories store them into the jsontree for (Category cat : model.getCategories()) { String key = "CATEGORY" + getNumerator(NUMTYPE.CATEGORY); file.add(key, new JsonPrimitive(cat.getName())); // forall object in the category store them into the jsontree for (AbstractCanvasObject obj : cat.getObjects()) { file.add("CGOBJECT" + getNumerator(NUMTYPE.OBJECT), model.getGson().toJsonTree(obj, AbstractCanvasObject.class)); // if its a holonobject add elements too if (obj instanceof HolonObject) elementsToJson(TYPE.CATEGORY, file, obj); } } } private void storeWindowPosAndSize(JsonObject file, int x, int y, int width, int height) { file.add("POS_X", new JsonPrimitive(x)); file.add("POS_Y", new JsonPrimitive(y)); file.add("WIDTH", new JsonPrimitive(width)); file.add("HEIGHT", new JsonPrimitive(height)); } /** * Travers through all Objects via BFS and Stores everything relevant */ private void storeCanvas(JsonObject file) { ArrayDeque queue = new ArrayDeque<>(); AbstractCanvasObject u = null; // put all objects into queue since there is not starting object for (AbstractCanvasObject cps : model.getObjectsOnCanvas()) { queue.add(cps); } // while quene not empty while (!queue.isEmpty()) { // u = current node u = queue.pop(); // add currentnode into jsontree String key = "CVSOBJECT" + getNumerator(NUMTYPE.OBJECT); file.add(key, model.getGson().toJsonTree(u, AbstractCanvasObject.class)); // and its connections too edgeToJson(EDGETYPE.CONNECTION, file, u.getId(), u.getConnections()); // if holonobject elements too if (u instanceof HolonObject) elementsToJson(TYPE.CANVAS, file, u); // if switch graphpoints too if (u instanceof HolonSwitch) if (((HolonSwitch) u).getGraphPoints().size() != 0) unitgraphToJson(GRAPHTYPE.SWITCH, file, u.getId(), ((HolonSwitch) u).getGraphPoints()); // if uppernode put all nodes inside the uppernode into queue if (u instanceof GroupNode) { for (AbstractCanvasObject adjacent : ((GroupNode) u).getNodes()) { queue.add(adjacent); } } } // lastly add canvasedges into json edgeToJson(EDGETYPE.CANVAS, file, 0, model.getEdgesOnCanvas()); } /** * Save wanted Data */ private void storeData(ArchiveOutputStream stream) throws IOException { File images = new File(System.getProperty("user.home") + "/.config/HolonGUI/Images"); File background = new File(System.getProperty("user.home") + "/.config/HolonGUI/BackgroundImages"); addFilesToSave(images, stream); addFilesToSave(background, stream); } /** * Stores Category or Canvas Elements into Json */ void elementsToJson(TYPE type, JsonObject file, AbstractCanvasObject obj) { JsonObject temp = new JsonObject(); String key = null; // forall elements store them into json and include the id of the object for (HolonElement ele : ((HolonObject) obj).getElements()) { temp.add("properties", model.getGson().toJsonTree(ele)); temp.add("ID", new JsonPrimitive(obj.getId())); temp.add("FlexList", model.getGson().toJsonTree(ele.flexList, new TypeToken>() { }.getType())); // switch for deciding the key switch (type) { case CANVAS: key = "CVSELEMENT" + getNumerator(NUMTYPE.ELEMENT); break; case CATEGORY: key = "CGELEMENT" + getNumerator(NUMTYPE.ELEMENT); break; default: break; } file.add(key, model.getGson().toJsonTree(temp)); // if there are gps add them into if (!ele.getGraphPoints().isEmpty()) unitgraphTESTToJson(file, ele.getId(), ele.getGraphPoints()); temp = new JsonObject(); } } /** * Put the UnitGraphs of Switches or Elements into Json */ void unitgraphToJson(GRAPHTYPE type, JsonObject file, int id, LinkedList graph) { JsonObject temp = new JsonObject(); String key = null; // forall points add them for (int i = 0; i < graph.size(); i++) { temp.add("" + i, new JsonPrimitive(graph.get(i).x + ":" + graph.get(i).y)); } // decide key switch (type) { case SWITCH: key = "SWUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH); break; case ELEMENT: key = "ELEUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH); break; case TESTELEMENT: key = "ELETESTUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH); break; default: break; } // add id of element so it can be found again temp.add("ID", new JsonPrimitive(id)); file.add(key, model.getGson().toJsonTree(temp)); } /** * Put the UnitGraphs of Switches or Elements into Json */ void unitgraphTESTToJson(JsonObject file, int id, LinkedList graph) { JsonObject temp = new JsonObject(); String key = null; // forall points add them for (int i = 0; i < graph.size(); i++) { temp.add("" + i, new JsonPrimitive(graph.get(i).x + ":" + graph.get(i).y)); } key = "ELETESTUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH); // add id of element so it can be found again temp.add("ID", new JsonPrimitive(id)); file.add(key, model.getGson().toJsonTree(temp)); } /** * Canvas-Edge, Connections, Node-Edge and Old-Edges to json */ private void edgeToJson(EDGETYPE type, JsonObject file, int id, ArrayList arr) { String k = null; boolean b = false; JsonObject temp = new JsonObject(); for (Edge edge : arr) { // add properties and only the ids from a and b temp.add("properties", model.getGson().toJsonTree(edge)); temp.add("A", new JsonPrimitive(edge.getA().getId())); temp.add("B", new JsonPrimitive(edge.getB().getId())); // Key and occasionally the id of Uppernode switch (type) { case CANVAS: k = "CVSEDGE" + getNumerator(NUMTYPE.EDGE); break; case CONNECTION: k = "CONNEDGE" + getNumerator(NUMTYPE.CONNECTION); break; case NODE: temp.add("ID", new JsonPrimitive(id)); k = "NODEEDGE" + getNumerator(NUMTYPE.NODEEDGE); break; case OLD: temp.add("ID", new JsonPrimitive(id)); k = "OLDEDGE" + getNumerator(NUMTYPE.OLDEDGE); break; default: break; } // lookup if the CVS, NODE or OLDEDGE are also connections if (edge.getA().getConnections().contains(edge) && edge.getA().getConnections().contains(edge) && !type.equals(EDGETYPE.CONNECTION)) b = true; temp.add("connection", new JsonPrimitive(b)); file.add(k, model.getGson().toJsonTree(temp)); temp = new JsonObject(); } } /** * Differs Between Single file or whole Directory to Store */ private void addFilesToSave(File src, ArchiveOutputStream stream) throws IOException { if (!src.exists()) return; ArrayList files = new ArrayList<>(); files.addAll(Arrays.asList(src.listFiles())); for (File file : files) { if (file.isDirectory()) addFilesToSave(file, stream); else addFileToSave(file, stream, true); } } /** * Add a File into the Archive */ private void addFileToSave(File src, ArchiveOutputStream stream, boolean dir) throws IOException { String entryName = (dir ? src.getCanonicalPath() : src.getName()); entryName = checkOS(entryName); ZipArchiveEntry entry = new ZipArchiveEntry(entryName); stream.putArchiveEntry(entry); BufferedInputStream input = new BufferedInputStream(new FileInputStream(src)); IOUtils.copy(input, stream); input.close(); stream.closeArchiveEntry(); } private String checkOS(String entryName) { String os = System.getProperty("os.name").toLowerCase(); String ret = entryName; String partition = null; if (os.contains("windows")) { partition = ret.substring(0, ret.indexOf(":") + 1); ret = ret.replace(System.getProperty("user.home") + "\\.config\\HolonGUI\\", ""); ret = ret.replace(partition, ""); } if (os.contains("mac")) { // dosmth ret = ret.replace(System.getProperty("user.home") + "/.config/HolonGUI/", ""); } if (os.contains("linux")) { // dosmth ret = ret.replace(System.getProperty("user.home") + "/.config/HolonGUI/", ""); } if (os.contains("solaris")) { // dosmth } return ret; } /** * Just initialize the Numerators for the Json Keys. Maybe bad Style.. */ void initNumeration() { this.nCat = this.nObj = this.nEle = this.nEdge = this.nConn = this.nNodeEdge = this.nOldEdge = this.nStatsGraph = 0; } /** * Get the wanted numerator and increment it */ int getNumerator(NUMTYPE type) { switch (type) { case CATEGORY: return nCat++; case OBJECT: return nObj++; case ELEMENT: return nEle++; case EDGE: return nEdge++; case CONNECTION: return nConn++; case NODEEDGE: return nNodeEdge++; case OLDEDGE: return nOldEdge++; case UNITGRAPH: return nUnitGraph++; case STATSGRAPH: return nStatsGraph++; default: break; } return -1; } public enum MODE { COMPLETE, PARTIAL, CATEGORY, SIZE, } public enum TYPE { CATEGORY, CANVAS } public enum EDGETYPE { CANVAS, CONNECTION, NODE, OLD, LAYER } public enum NUMTYPE { CATEGORY, OBJECT, ELEMENT, EDGE, CONNECTION, NODEEDGE, OLDEDGE, UNITGRAPH, STATSGRAPH } public enum GRAPHTYPE { SWITCH, ELEMENT, TESTELEMENT } }