SaveController.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. package ui.controller;
  2. import com.google.gson.JsonObject;
  3. import com.google.gson.JsonPrimitive;
  4. import com.google.gson.reflect.TypeToken;
  5. import model.*;
  6. import org.apache.commons.compress.archivers.ArchiveException;
  7. import org.apache.commons.compress.archivers.ArchiveOutputStream;
  8. import org.apache.commons.compress.archivers.ArchiveStreamFactory;
  9. import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
  10. import org.apache.commons.compress.utils.IOUtils;
  11. import ui.model.IdCounter;
  12. import ui.model.Model;
  13. import utility.Vector2Float;
  14. import ui.model.IdCounter.CounterType;
  15. import ui.view.main.Category;
  16. import java.io.BufferedInputStream;
  17. import java.io.File;
  18. import java.io.FileInputStream;
  19. import java.io.FileOutputStream;
  20. import java.io.FileWriter;
  21. import java.io.IOException;
  22. import java.io.OutputStream;
  23. import java.util.ArrayDeque;
  24. import java.util.ArrayList;
  25. import java.util.Arrays;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. /**
  29. * Controller for Storage.
  30. *
  31. * @author Gruppe14
  32. */
  33. public class SaveController {
  34. private Model model;
  35. private int nCat, nObj, nEle, nEdge, nConn, nNodeEdge, nOldEdge, nUnitGraph, nStatsGraph;
  36. /**
  37. * Constructor.
  38. *
  39. * @param model the Model
  40. */
  41. SaveController(Model model) {
  42. this.model = model;
  43. }
  44. /**
  45. * Writes the current State of the Modelling into a JSON File which can be
  46. * loaded.
  47. *
  48. * @param path the Path
  49. * @throws IOException exception
  50. */
  51. void writeSave(String path) throws IOException, ArchiveException {
  52. File dst = new File(path);
  53. File holonFile = File.createTempFile("tmp", ".json");
  54. holonFile.deleteOnExit();
  55. dst.delete();
  56. OutputStream output = new FileOutputStream(dst);
  57. ArchiveOutputStream stream = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP,
  58. output);
  59. initNumeration();
  60. JsonObject file = new JsonObject();
  61. initialize(MODE.COMPLETE, file);
  62. storeCategory(file);
  63. storeCanvas(file);
  64. storeData(stream);
  65. FileWriter writer = new FileWriter(holonFile);
  66. writer.write(model.getGson().toJson(file));
  67. writer.flush();
  68. writer.close();
  69. addFileToSave(holonFile, stream, false);
  70. stream.finish();
  71. output.close();
  72. }
  73. /**
  74. * Writes the windows status (its position and dimensions)
  75. *
  76. * @param path the Path where to save it
  77. */
  78. void writeWindowStatus(String path, int x, int y, int width, int height)
  79. throws IOException, ArchiveException {
  80. JsonObject file = new JsonObject();
  81. initialize(MODE.SIZE, file);
  82. storeWindowPosAndSize(file, x, y, width, height);
  83. FileWriter writer = new FileWriter(path);
  84. writer.write(model.getGson().toJson(file));
  85. writer.flush();
  86. writer.close();
  87. }
  88. /**
  89. * Writes the Autosave File.
  90. *
  91. * @param path the Path
  92. * @throws IOException Exception
  93. */
  94. void writeAutosave(String path) throws IOException {
  95. initNumeration();
  96. JsonObject file = new JsonObject();
  97. initialize(MODE.PARTIAL, file);
  98. storeCanvas(file);
  99. FileWriter writer = new FileWriter(path);
  100. writer.write(model.getGson().toJson(file));
  101. writer.flush();
  102. writer.close();
  103. }
  104. /**
  105. * Writes the Category File in Case of Changes
  106. */
  107. void writeCategory(String path) throws IOException {
  108. initNumeration();
  109. JsonObject file = new JsonObject();
  110. initialize(MODE.CATEGORY, file);
  111. storeCategory(file);
  112. FileWriter writer = new FileWriter(path);
  113. writer.write(model.getGson().toJson(file));
  114. writer.flush();
  115. writer.close();
  116. }
  117. /**
  118. * Write needed default parameter into the JsonObject. Can be extended later
  119. * on
  120. */
  121. private void initialize(MODE mode, JsonObject file) {
  122. switch (mode) {
  123. case COMPLETE:
  124. file.add("MODE", new JsonPrimitive(mode.name()));
  125. file.add("IDCOUNTER", new JsonPrimitive(IdCounter.actualId(CounterType.Object)));
  126. file.add("IDCOUNTERELEMENT", new JsonPrimitive(IdCounter.actualId(CounterType.Element)));
  127. file.add("CANVAS_SIZE_X", new JsonPrimitive(model.getCanvasX()));
  128. file.add("CANVAS_SIZE_Y", new JsonPrimitive(model.getCanvasY()));
  129. break;
  130. case PARTIAL:
  131. file.add("MODE", new JsonPrimitive(mode.name()));
  132. file.add("IDCOUNTER", new JsonPrimitive(IdCounter.actualId(CounterType.Object)));
  133. file.add("IDCOUNTERELEMENT", new JsonPrimitive(IdCounter.actualId(CounterType.Element)));
  134. file.add("CANVAS_SIZE_X", new JsonPrimitive(model.getCanvasX()));
  135. file.add("CANVAS_SIZE_Y", new JsonPrimitive(model.getCanvasY()));
  136. break;
  137. case CATEGORY:
  138. file.add("MODE", new JsonPrimitive(mode.name()));
  139. break;
  140. case SIZE:
  141. file.add("MODE", new JsonPrimitive(mode.name()));
  142. default:
  143. break;
  144. }
  145. }
  146. /**
  147. * Store all Categories and Object into a Json File via Serialization
  148. */
  149. private void storeCategory(JsonObject file) {
  150. // forall categories store them into the jsontree
  151. for (Category cat : model.getCategories()) {
  152. String key = "CATEGORY" + getNumerator(NUMTYPE.CATEGORY);
  153. file.add(key, new JsonPrimitive(cat.getName()));
  154. // forall object in the category store them into the jsontree
  155. for (AbstractCanvasObject obj : cat.getObjects()) {
  156. file.add("CGOBJECT" + getNumerator(NUMTYPE.OBJECT),
  157. model.getGson().toJsonTree(obj, AbstractCanvasObject.class));
  158. // if its a holonobject add elements too
  159. if (obj instanceof HolonObject)
  160. elementsToJson(TYPE.CATEGORY, file, obj);
  161. }
  162. }
  163. }
  164. private void storeWindowPosAndSize(JsonObject file, int x, int y, int width, int height) {
  165. file.add("POS_X", new JsonPrimitive(x));
  166. file.add("POS_Y", new JsonPrimitive(y));
  167. file.add("WIDTH", new JsonPrimitive(width));
  168. file.add("HEIGHT", new JsonPrimitive(height));
  169. }
  170. /**
  171. * Travers through all Objects via BFS and Stores everything relevant
  172. */
  173. private void storeCanvas(JsonObject file) {
  174. ArrayDeque<AbstractCanvasObject> queue = new ArrayDeque<>();
  175. AbstractCanvasObject u = null;
  176. // put all objects into queue since there is not starting object
  177. for (AbstractCanvasObject cps : model.getObjectsOnCanvas()) {
  178. queue.add(cps);
  179. }
  180. // while quene not empty
  181. while (!queue.isEmpty()) {
  182. // u = current node
  183. u = queue.pop();
  184. // add currentnode into jsontree
  185. String key = "CVSOBJECT" + getNumerator(NUMTYPE.OBJECT);
  186. file.add(key, model.getGson().toJsonTree(u, AbstractCanvasObject.class));
  187. // and its connections too
  188. edgeToJson(EDGETYPE.CONNECTION, file, u.getId(), u.getConnections());
  189. // if holonobject elements too
  190. if (u instanceof HolonObject)
  191. elementsToJson(TYPE.CANVAS, file, u);
  192. // if switch graphpoints too
  193. if (u instanceof HolonSwitch)
  194. if (((HolonSwitch) u).getGraphPoints().size() != 0)
  195. unitgraphToJson(GRAPHTYPE.SWITCH, file, u.getId(), ((HolonSwitch) u).getGraphPoints());
  196. // if uppernode put all nodes inside the uppernode into queue
  197. if (u instanceof GroupNode) {
  198. for (AbstractCanvasObject adjacent : ((GroupNode) u).getNodes()) {
  199. queue.add(adjacent);
  200. }
  201. }
  202. }
  203. // lastly add canvasedges into json
  204. edgeToJson(EDGETYPE.CANVAS, file, 0, model.getEdgesOnCanvas());
  205. }
  206. /**
  207. * Save wanted Data
  208. */
  209. private void storeData(ArchiveOutputStream stream) throws IOException {
  210. File images = new File(System.getProperty("user.home") + "/.config/HolonGUI/Images");
  211. File background = new File(System.getProperty("user.home") + "/.config/HolonGUI/BackgroundImages");
  212. addFilesToSave(images, stream);
  213. addFilesToSave(background, stream);
  214. }
  215. /**
  216. * Stores Category or Canvas Elements into Json
  217. */
  218. void elementsToJson(TYPE type, JsonObject file, AbstractCanvasObject obj) {
  219. JsonObject temp = new JsonObject();
  220. String key = null;
  221. // forall elements store them into json and include the id of the object
  222. for (HolonElement ele : ((HolonObject) obj).getElements()) {
  223. temp.add("properties", model.getGson().toJsonTree(ele));
  224. temp.add("ID", new JsonPrimitive(obj.getId()));
  225. temp.add("FlexList", model.getGson().toJsonTree(ele.flexList, new TypeToken<List<Flexibility>>() {
  226. }.getType()));
  227. // switch for deciding the key
  228. switch (type) {
  229. case CANVAS:
  230. key = "CVSELEMENT" + getNumerator(NUMTYPE.ELEMENT);
  231. break;
  232. case CATEGORY:
  233. key = "CGELEMENT" + getNumerator(NUMTYPE.ELEMENT);
  234. break;
  235. default:
  236. break;
  237. }
  238. file.add(key, model.getGson().toJsonTree(temp));
  239. // if there are gps add them into
  240. if (!ele.getGraphPoints().isEmpty())
  241. unitgraphTESTToJson(file, ele.getId(), ele.getGraphPoints());
  242. temp = new JsonObject();
  243. }
  244. }
  245. /**
  246. * Put the UnitGraphs of Switches or Elements into Json
  247. */
  248. void unitgraphToJson(GRAPHTYPE type, JsonObject file, int id, LinkedList<Vector2Float> graph) {
  249. JsonObject temp = new JsonObject();
  250. String key = null;
  251. // forall points add them
  252. for (int i = 0; i < graph.size(); i++) {
  253. temp.add("" + i, new JsonPrimitive(graph.get(i).x + ":" + graph.get(i).y));
  254. }
  255. // decide key
  256. switch (type) {
  257. case SWITCH:
  258. key = "SWUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH);
  259. break;
  260. case ELEMENT:
  261. key = "ELEUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH);
  262. break;
  263. case TESTELEMENT:
  264. key = "ELETESTUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH);
  265. break;
  266. default:
  267. break;
  268. }
  269. // add id of element so it can be found again
  270. temp.add("ID", new JsonPrimitive(id));
  271. file.add(key, model.getGson().toJsonTree(temp));
  272. }
  273. /**
  274. * Put the UnitGraphs of Switches or Elements into Json
  275. */
  276. void unitgraphTESTToJson(JsonObject file, int id, LinkedList<Vector2Float> graph) {
  277. JsonObject temp = new JsonObject();
  278. String key = null;
  279. // forall points add them
  280. for (int i = 0; i < graph.size(); i++) {
  281. temp.add("" + i, new JsonPrimitive(graph.get(i).x + ":" + graph.get(i).y));
  282. }
  283. key = "ELETESTUNITGRAPH" + getNumerator(NUMTYPE.UNITGRAPH);
  284. // add id of element so it can be found again
  285. temp.add("ID", new JsonPrimitive(id));
  286. file.add(key, model.getGson().toJsonTree(temp));
  287. }
  288. /**
  289. * Canvas-Edge, Connections, Node-Edge and Old-Edges to json
  290. */
  291. private void edgeToJson(EDGETYPE type, JsonObject file, int id, ArrayList<Edge> arr) {
  292. String k = null;
  293. boolean b = false;
  294. JsonObject temp = new JsonObject();
  295. for (Edge edge : arr) {
  296. // add properties and only the ids from a and b
  297. temp.add("properties", model.getGson().toJsonTree(edge));
  298. temp.add("A", new JsonPrimitive(edge.getA().getId()));
  299. temp.add("B", new JsonPrimitive(edge.getB().getId()));
  300. // Key and occasionally the id of Uppernode
  301. switch (type) {
  302. case CANVAS:
  303. k = "CVSEDGE" + getNumerator(NUMTYPE.EDGE);
  304. break;
  305. case CONNECTION:
  306. k = "CONNEDGE" + getNumerator(NUMTYPE.CONNECTION);
  307. break;
  308. case NODE:
  309. temp.add("ID", new JsonPrimitive(id));
  310. k = "NODEEDGE" + getNumerator(NUMTYPE.NODEEDGE);
  311. break;
  312. case OLD:
  313. temp.add("ID", new JsonPrimitive(id));
  314. k = "OLDEDGE" + getNumerator(NUMTYPE.OLDEDGE);
  315. break;
  316. default:
  317. break;
  318. }
  319. // lookup if the CVS, NODE or OLDEDGE are also connections
  320. if (edge.getA().getConnections().contains(edge) && edge.getA().getConnections().contains(edge)
  321. && !type.equals(EDGETYPE.CONNECTION))
  322. b = true;
  323. temp.add("connection", new JsonPrimitive(b));
  324. file.add(k, model.getGson().toJsonTree(temp));
  325. temp = new JsonObject();
  326. }
  327. }
  328. /**
  329. * Differs Between Single file or whole Directory to Store
  330. */
  331. private void addFilesToSave(File src, ArchiveOutputStream stream) throws IOException {
  332. if (!src.exists())
  333. return;
  334. ArrayList<File> files = new ArrayList<>();
  335. files.addAll(Arrays.asList(src.listFiles()));
  336. for (File file : files) {
  337. if (file.isDirectory())
  338. addFilesToSave(file, stream);
  339. else
  340. addFileToSave(file, stream, true);
  341. }
  342. }
  343. /**
  344. * Add a File into the Archive
  345. */
  346. private void addFileToSave(File src, ArchiveOutputStream stream, boolean dir) throws IOException {
  347. String entryName = (dir ? src.getCanonicalPath() : src.getName());
  348. entryName = checkOS(entryName);
  349. ZipArchiveEntry entry = new ZipArchiveEntry(entryName);
  350. stream.putArchiveEntry(entry);
  351. BufferedInputStream input = new BufferedInputStream(new FileInputStream(src));
  352. IOUtils.copy(input, stream);
  353. input.close();
  354. stream.closeArchiveEntry();
  355. }
  356. private String checkOS(String entryName) {
  357. String os = System.getProperty("os.name").toLowerCase();
  358. String ret = entryName;
  359. String partition = null;
  360. if (os.contains("windows")) {
  361. partition = ret.substring(0, ret.indexOf(":") + 1);
  362. ret = ret.replace(System.getProperty("user.home") + "\\.config\\HolonGUI\\", "");
  363. ret = ret.replace(partition, "");
  364. }
  365. if (os.contains("mac")) {
  366. // dosmth
  367. ret = ret.replace(System.getProperty("user.home") + "/.config/HolonGUI/", "");
  368. }
  369. if (os.contains("linux")) {
  370. // dosmth
  371. ret = ret.replace(System.getProperty("user.home") + "/.config/HolonGUI/", "");
  372. }
  373. if (os.contains("solaris")) {
  374. // dosmth
  375. }
  376. return ret;
  377. }
  378. /**
  379. * Just initialize the Numerators for the Json Keys. Maybe bad Style..
  380. */
  381. void initNumeration() {
  382. this.nCat = this.nObj = this.nEle = this.nEdge = this.nConn = this.nNodeEdge = this.nOldEdge = this.nStatsGraph = 0;
  383. }
  384. /**
  385. * Get the wanted numerator and increment it
  386. */
  387. int getNumerator(NUMTYPE type) {
  388. switch (type) {
  389. case CATEGORY:
  390. return nCat++;
  391. case OBJECT:
  392. return nObj++;
  393. case ELEMENT:
  394. return nEle++;
  395. case EDGE:
  396. return nEdge++;
  397. case CONNECTION:
  398. return nConn++;
  399. case NODEEDGE:
  400. return nNodeEdge++;
  401. case OLDEDGE:
  402. return nOldEdge++;
  403. case UNITGRAPH:
  404. return nUnitGraph++;
  405. case STATSGRAPH:
  406. return nStatsGraph++;
  407. default:
  408. break;
  409. }
  410. return -1;
  411. }
  412. public enum MODE {
  413. COMPLETE, PARTIAL, CATEGORY, SIZE,
  414. }
  415. public enum TYPE {
  416. CATEGORY, CANVAS
  417. }
  418. public enum EDGETYPE {
  419. CANVAS, CONNECTION, NODE, OLD, LAYER
  420. }
  421. public enum NUMTYPE {
  422. CATEGORY, OBJECT, ELEMENT, EDGE, CONNECTION, NODEEDGE, OLDEDGE, UNITGRAPH, STATSGRAPH
  423. }
  424. public enum GRAPHTYPE {
  425. SWITCH, ELEMENT, TESTELEMENT
  426. }
  427. }