diff --git a/README.md b/README.md index 21d6134..961d93f 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Sync files across directories. ### Issues +- Have Map entries be remove, once file ops is performed. - Create a parallel Thread for each SyncEnity. - Add support for modification dates. - And thereby eventually support 10 out of 10 file operation types. diff --git a/src/com/olexyn/ensync/Core.java b/src/com/olexyn/ensync/Core.java deleted file mode 100644 index 3ec0003..0000000 --- a/src/com/olexyn/ensync/Core.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.olexyn.ensync; - -import java.util.List; - -public class Core { - - - public Core(){ - - } - - -} diff --git a/src/com/olexyn/ensync/Flow.java b/src/com/olexyn/ensync/Flow.java new file mode 100644 index 0000000..5bd4714 --- /dev/null +++ b/src/com/olexyn/ensync/Flow.java @@ -0,0 +1,76 @@ +package com.olexyn.ensync; + +import com.olexyn.ensync.artifacts.SyncDirectory; +import com.olexyn.ensync.artifacts.SyncEntity; +import com.olexyn.ensync.artifacts.SyncFile; + +import java.io.File; +import java.util.List; +import java.util.Map; + +public class Flow { + + + public Flow(){ + + } + + + + + public void start() { + File asdf = new File("/home/user/"); + System.out.println(asdf.lastModified()); + + Tools tools = new Tools(); + Execute x = new Execute(); + + + SyncEntity syncEntity = new SyncEntity("test"); + syncEntity.addDirectory("/home/user/test/a"); + syncEntity.addDirectory("/home/user/test/b"); + //syncEntity.addDirectory("/home/user/test/c"); + + + for (Map.Entry entry : syncEntity.syncDirectories.entrySet()) { + SyncDirectory syncDirectory = entry.getValue(); + String path = syncDirectory.path; + String stateFilePath = syncDirectory.stateFilePath(path); + + if (new File(stateFilePath).exists()) { + syncDirectory.readStateFile(syncDirectory.path); + } else { + syncDirectory.writeStateFile(path); + } + } + + + while (true) { + + for (Map.Entry entry : syncEntity.syncDirectories.entrySet()) { + + SyncDirectory syncDirectory = entry.getValue(); + + String path = syncDirectory.path; + + syncDirectory.readState(path); + + List listCreated = syncDirectory.makeListCreated(path); + List listDeleted = syncDirectory.makeListDeleted(path); + List listModified = syncDirectory.makeListModified(path); + + syncDirectory.doCreate(listCreated); + syncDirectory.doDelete(listDeleted); + syncDirectory.doModify(listModified); + + syncDirectory.writeStateFile(path); + + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + } + }} + } +} diff --git a/src/com/olexyn/ensync/Main.java b/src/com/olexyn/ensync/Main.java index 423f17c..a6f00b1 100644 --- a/src/com/olexyn/ensync/Main.java +++ b/src/com/olexyn/ensync/Main.java @@ -1,71 +1,16 @@ package com.olexyn.ensync; -import com.olexyn.ensync.artifacts.SyncDirectory; -import com.olexyn.ensync.artifacts.SyncEntity; -import java.util.*; public class Main { public static void main(String[] args) { + new Flow().start(); - Tools tools = new Tools(); - Execute x = new Execute(); - SyncEntity syncEntity = new SyncEntity("test"); - syncEntity.addDirectory("/home/user/test/a"); - syncEntity.addDirectory("/home/user/test/b"); - //syncEntity.addDirectory("/home/user/test/c"); - - int br1 = 0; - - while (true) { - - for (Map.Entry entry : syncEntity.syncDirectories.entrySet()) { - - SyncDirectory syncDirectory = entry.getValue(); - - - syncDirectory.updateStateFileNew(); - syncDirectory.updatePoolNew(); - - syncDirectory.getListCreated(); - syncDirectory.getListDeleted(); - - - // - - syncDirectory.doSyncOps(); - - -// - // WARNING: - // Be very carefull when to update the StateFileOld - // i.e. you create a File and update StateFileOld without updating - // -> create newFile -> update StateFileNew-> getListCreated contains newFile -> addToMapCreated -> create copies as needed -> updateStateFileOld -> OK - // -> create newFile -> update StateFileOld -> getListDeletd contains newFile -> VERY BAD - // - syncDirectory.updateStateFileNew(); - syncDirectory.updatePoolNew(); - - syncDirectory.updateStateFileOld(); - syncDirectory.updatePoolOld(); - - - - - } - //Map mapCreated = syncEntity.getMapCreated(); - //Map mapDeleted = syncEntity.getMapDeleted(); - - int br = 0; - try {Thread.sleep(1000);} - catch (InterruptedException e){ - - } } } -} + diff --git a/src/com/olexyn/ensync/Tools.java b/src/com/olexyn/ensync/Tools.java index e7ea453..83f545f 100644 --- a/src/com/olexyn/ensync/Tools.java +++ b/src/com/olexyn/ensync/Tools.java @@ -1,13 +1,11 @@ package com.olexyn.ensync; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; +import com.olexyn.ensync.artifacts.SyncFile; + +import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class Tools { @@ -18,25 +16,15 @@ public class Tools { } + + + /** + * Convert BufferedReader to String. * + * @param br BufferedReader + * @return String */ - public void rsync(String param, - String source, - String destination) { - // - BufferedReader foo = x.execute(new String[]{"rsync", - param, - source, - destination}).output; - } - - public String getConf() { - BufferedReader output = x.execute(new String[]{"cat", - System.getProperty("user.dir") + "/src/com/olexyn/ensync/sync.conf"}).output; - return brToString(output); - } - public String brToString(BufferedReader br) { StringBuilder sb = new StringBuilder(); Object[] br_array = br.lines().toArray(); @@ -46,30 +34,23 @@ public class Tools { return sb.toString(); } + /** - * StateFile -> FilePool + * Convert BufferedReader to List of Strings. + * + * @param br BufferedReader + * @return List */ - public Map fileToPool(File file, - String type) { - List lines = fileToLines(file); - return linesToFilePool(lines, type); + public List brToListString(BufferedReader br) { + List list = new ArrayList<>(); + Object[] br_array = br.lines().toArray(); + for (int i = 0; i < br_array.length; i++) { + list.add(br_array[i].toString()); + } + return list; } - /** - * CREATE a StateFile from realPath.

- * WRITE the StateFle to stateFilePath. - * @param realPath the path of the directory the StateFile is created for. - * @param stateFilePath the desired path for the created Statefile. - */ - public File generateStateFile(String realPath, String stateFilePath) { - String[] cmd = new String[]{System.getProperty("user.dir") + "/src/com/olexyn/ensync/shell/toFile.sh", - "find", - realPath, - stateFilePath}; - x.execute(cmd); - return new File(realPath); - } public List fileToLines(File file) { @@ -84,32 +65,19 @@ public class Tools { } - public Map linesToFilePool(List lines, - String type) { - Map filepool = new HashMap<>(); - for (String line : lines) { - File file = new File(line); - if (type.equals("all") || type.equals("dir") && file.isDirectory() || type.equals("file") && file.isFile()) { - filepool.put(line, file); - } - } - return filepool; - } - - - public List mapMinus(Map fromA, - Map substractB) { + public List mapMinus(Map fromA, + Map substractB) { - List difference = new ArrayList<>(); + List difference = new ArrayList<>(); Iterator iterator = fromA.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); String key = (String) entry.getKey(); - File file = fromA.get(key); + SyncFile file = fromA.get(key); if (fromA.containsKey(key) && !substractB.containsKey(key)) { @@ -120,4 +88,51 @@ public class Tools { } return difference; } + + + public StringBuilder stringListToSb(List list) { + StringBuilder sb = new StringBuilder(); + + for (String line : list) { + sb.append(line + "\n"); + } + return sb; + } + + /** + * Write sb to file at path . + * + * @param path String + * @param sb StringBuilder + */ + public void writeSbToFile(String path, + StringBuilder sb) { + try { + BufferedWriter bw = new BufferedWriter(new FileWriter(new File(path))); + bw.write(sb.toString()); + bw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + /** + * Write List of String to file at path . + * + * @param path String + * @param list StringBuilder + */ + public void writeStringListToFile(String path, + List list) { + try { + BufferedWriter bw = new BufferedWriter(new FileWriter(new File(path))); + StringBuilder sb = stringListToSb(list); + bw.write(sb.toString()); + bw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } diff --git a/src/com/olexyn/ensync/artifacts/Data.java b/src/com/olexyn/ensync/artifacts/Data.java new file mode 100644 index 0000000..b1284c9 --- /dev/null +++ b/src/com/olexyn/ensync/artifacts/Data.java @@ -0,0 +1,36 @@ +package com.olexyn.ensync.artifacts; + +import java.util.Map; + +public class Data { + + + Map> database; + + + + + public Data(){ + + } + + + + + + + + + + + + + + + + + + + + +} diff --git a/src/com/olexyn/ensync/artifacts/StateFile.java b/src/com/olexyn/ensync/artifacts/StateFile.java deleted file mode 100644 index 3a0fef7..0000000 --- a/src/com/olexyn/ensync/artifacts/StateFile.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.olexyn.ensync.artifacts; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -public class StateFile { - - private final String[] types = new String[]{ "OLD" , "NEW"}; - - public String stateFilePath; - public File stateFileOld; - private Map poolOld = new HashMap<>(); - - - public StateFile(){ - - } - - - - - public void update(){ - - } - - - -} diff --git a/src/com/olexyn/ensync/artifacts/SyncDirectory.java b/src/com/olexyn/ensync/artifacts/SyncDirectory.java index fe779d3..43799d4 100644 --- a/src/com/olexyn/ensync/artifacts/SyncDirectory.java +++ b/src/com/olexyn/ensync/artifacts/SyncDirectory.java @@ -8,22 +8,12 @@ import java.util.*; public class SyncDirectory { - public String realPath; - public String stateFileBasePath; - public String stateFileOldPath; - public String stateFileNewPath; - public File stateFileOld; - public File stateFileNew; - private Map poolOld = new HashMap<>(); - private Map poolNew = new HashMap<>(); - private List listCreated = new ArrayList<>(); - private List listDeleted = new ArrayList<>(); + private SyncEntity syncEntity; + public String path= null; + - private String state = null; -// For an explanation of what the STATES mean, see the flow.png - private final List STATES = new ArrayList<>(Arrays.asList( "NEW-1", "LIST-1" , "LIST-2" , "SYNC-1" , "NEW-2", "OLD-1")); Tools tools = new Tools(); Execute x = new Execute(); @@ -31,169 +21,163 @@ public class SyncDirectory { /** * Create a SyncDirectory from realPath. * - * @param realPath + * @see SyncEntity */ - public SyncDirectory(String realPath, SyncEntity syncEntity) { - + public SyncDirectory(String path , SyncEntity syncEntity) { - this.realPath = realPath; - stateFileBasePath = "/tmp/find" + this.realPath.replace("/", "-"); - stateFileOldPath = stateFileBasePath + "-old"; - stateFileNewPath = stateFileBasePath + "-new"; - stateFileOld = getStateFileOld(); - stateFileNew = getStateFileNew(); - poolOld = getPoolOld(); - poolNew = getPoolNew(); + this.path = path; this.syncEntity = syncEntity; } + /** - * IF NOT EXISTS the StateFileOld for this SyncDirectory,

- * - THEN create the File on Disk. - * - * @return the StateFileOld. + * NOTE that the SFile().lastModifiedOld is not set here, so it is 0 by default. */ - public File getStateFileOld() { - stateFileOld = new File(stateFileOldPath); - if (!stateFileOld.exists()) { - stateFileOld = tools.generateStateFile(realPath, stateFileOldPath); + public Map readState(String path) { + Map filemap = new HashMap<>(); + + Execute.TwoBr find = x.execute(new String[]{"find", + path}); + + List pathList = tools.brToListString(find.output); + + for (String filePath : pathList) { + filemap.put(filePath, new SyncFile(filePath)); } - return stateFileOld; + + + return filemap; + + } + /** - * READ directory contents.

- * WRITE a new StateFileNew to Disk. This is IMPORTANT in order to make sure that StateFileOld is NEVER newer than StateFileNew.

- * WRITE a new StateFileOld to Disk. + * READ the contents of StateFile to Map. */ - public void updateStateFileOld() { - // - if (state.equals(STATES.get(4))){ - state = STATES.get(5); - } else { - return ; - } - tools.generateStateFile(realPath, stateFileOldPath); - } + public Map readStateFile(String path) { + Map filemap = new HashMap<>(); + List lines = tools.fileToLines(new File(stateFilePath(path))); - public File getStateFileNew() { - stateFileNew = new File(stateFileNewPath); - if (!stateFileNew.exists()) { - stateFileNew = tools.generateStateFile(realPath, stateFileNewPath); + for (String line : lines) { + // this is a predefined format: "modification-time path" + String modTimeString = line.split("")[0]; + long modTime = Long.parseLong(modTimeString); + + String sFilePath = line.replace(modTimeString + "", ""); + SyncFile sfile = new SyncFile(sFilePath); + + sfile.setLastModifiedOld(modTime); + + filemap.put(line, sfile); } - return stateFileNew; + + return filemap; + } + /** - * READ directory contents.

- * WRITE a new StateFileNew Disk. + * Compare the OLD and NEW pools. + * + * @return */ - public void updateStateFileNew() { - // - if (state == null || state.equals(STATES.get(5))){ - state = STATES.get(0); - }else if (state.equals(STATES.get(3))){ - state = STATES.get(4); - } else { - return; - } + public List makeListCreated(String path) { + return tools.mapMinus(readState(path), readStateFile(path)); + } - tools.generateStateFile(realPath, stateFileNewPath); + public String stateFilePath(String path) { + return "/tmp/ensync/state" + path.replace("/", "-"); + } - } + /** + * Compare the OLD and NEW pools. + * + * @return + */ + public List makeListDeleted(String path) { - public Map getPoolOld() { - if (poolOld.isEmpty()) { - updatePoolOld(); - } - return poolOld; + return tools.mapMinus(readStateFile(path), readState(path)); } + /** - * UPDATE PoolOld FROM contents of StateFileOld. + * Compare the OLD and NEW pools. + * + * @return */ - public void updatePoolOld() { + public List makeListModified(String path) { - poolOld = tools.fileToPool(getStateFileOld(), "all"); + List listModified = new ArrayList<>(); + Map oldMap = readStateFile(path); - } + for (Map.Entry newFileEntry : readState(path).entrySet()) { + // If KEY exists in OLD , thus FILE was NOT created. + String newFileKey = newFileEntry.getKey(); + SyncFile newFile = newFileEntry.getValue(); + if (oldMap.containsKey(newFileKey)) { + + long lastModifiedNew = newFile.lastModified(); - public Map getPoolNew() { - if (poolNew.isEmpty()) { - updatePoolNew(); + long lastModifiedOld = oldMap.get(newFileKey).lastModifiedOld(); + + if (lastModifiedNew > lastModifiedOld) { + listModified.add(newFile); + } + } } - return poolNew; + return listModified; } + /** - * UPDATE PoolNew FROM contents of StateFileNew. + * QUERY state of the filesystem at realPath. + * WRITE the state of the filesystem to file. */ - public void updatePoolNew() { - poolNew = tools.fileToPool(getStateFileNew(), "all"); + public void writeStateFile(String path) { + List outputList = new ArrayList<>(); - } - public List getListCreated() { - if (state.equals(STATES.get(0))){ - state = STATES.get(1); - } else { - return null; - } - updateListCreated(); - return listCreated; - } + Execute.TwoBr find = x.execute(new String[]{"find", + path}); - public void updateListCreated(){ + List pathList = tools.brToListString(find.output); - listCreated = tools.mapMinus(getPoolNew(), getPoolOld()); - } - public List getListDeleted() { - if (state.equals(STATES.get(1))){ - state = STATES.get(2); - } else { - return null; + for (String filePath : pathList) { + long lastModified = new File(filePath).lastModified(); + outputList.add("" + lastModified + " " + filePath); } - listDeleted = tools.mapMinus(getPoolOld(), getPoolNew()); - return listDeleted; + tools.writeStringListToFile(stateFilePath(path), outputList); } - public void doSyncOps(){ - - if (state.equals(STATES.get(2))){ - state = STATES.get(3); - } else { - return ; - } - - - + public void doCreate(List listCreated){ for (File createdFile : listCreated) { for (Map.Entry otherEntry : syncEntity.syncDirectories.entrySet()) { SyncDirectory otherSyncDirectory = otherEntry.getValue(); - if (!this.equals(otherSyncDirectory)){ + if (!this.equals(otherSyncDirectory)) { // Example: // syncDirectory /foo // otherSyncDirectory /bar // createdFile /foo/hello/created-file.gif // relativePath /hello/created-file.gif - String relativePath = createdFile.getPath().replace(this.realPath, ""); - String targetPath = otherSyncDirectory.realPath + relativePath; + String relativePath = createdFile.getPath().replace(this.path, ""); + String targetPath = otherSyncDirectory.path + relativePath; String targetParentPath = new File(targetPath).getParent(); - if (!new File(targetParentPath).exists()){ + if (!new File(targetParentPath).exists()) { String[] cmd = new String[]{"mkdir", "-p", targetParentPath}; @@ -202,28 +186,44 @@ public class SyncDirectory { String[] cmd = new String[]{"cp", createdFile.getPath(), - otherSyncDirectory.realPath + relativePath}; + otherSyncDirectory.path + relativePath}; x.execute(cmd); } } } + } + + + + public void doDelete(List listDeleted){ - // for (File deletedFile : listDeleted) { for (Map.Entry otherEntry : syncEntity.syncDirectories.entrySet()) { SyncDirectory otherSyncDirectory = otherEntry.getValue(); - if (!this.equals(otherSyncDirectory)){ - String relativePath = deletedFile.getPath().replace(this.realPath, ""); - String[] cmd = new String[]{"rm", "-r", - otherSyncDirectory.realPath + relativePath}; + if (!this.equals(otherSyncDirectory)) { + String relativePath = deletedFile.getPath().replace(this.path, ""); + String[] cmd = new String[]{"rm", + "-r", + otherSyncDirectory.path + relativePath}; x.execute(cmd); } } } + + } + public void doModify(List listModified) { + + + + + + } + + } diff --git a/src/com/olexyn/ensync/artifacts/SyncFile.java b/src/com/olexyn/ensync/artifacts/SyncFile.java new file mode 100644 index 0000000..e729530 --- /dev/null +++ b/src/com/olexyn/ensync/artifacts/SyncFile.java @@ -0,0 +1,26 @@ +package com.olexyn.ensync.artifacts; + +import java.io.File; + +public class SyncFile extends File { + + + // Very IMPORTANT field. Allows to store lastModified as it is stored in the StateFile. + private long lastModifiedOld = 0; + + + public SyncFile(String pathname) { + super(pathname); + } + + + + public long lastModifiedOld(){ + return lastModifiedOld; + } + + public void setLastModifiedOld(long value){ + lastModifiedOld = value; + } + +} diff --git a/src/com/olexyn/ensync/flow.png b/src/com/olexyn/ensync/flow.png index 4106764..1799e84 100644 Binary files a/src/com/olexyn/ensync/flow.png and b/src/com/olexyn/ensync/flow.png differ diff --git a/src/com/olexyn/ensync/flow.uxf b/src/com/olexyn/ensync/flow.uxf index 48099b8..dd739c9 100644 --- a/src/com/olexyn/ensync/flow.uxf +++ b/src/com/olexyn/ensync/flow.uxf @@ -4,8 +4,8 @@ UMLClass - 1350 - 1220 + 1230 + 620 130 60 @@ -18,8 +18,8 @@ group=1 UMLClass - 1480 - 1220 + 1360 + 620 130 30 @@ -32,8 +32,8 @@ group=1 UMLClass - 1480 - 1250 + 1360 + 650 130 30 @@ -45,8 +45,8 @@ group=1 UMLClass - 1350 - 1040 + 1230 + 520 130 30 @@ -59,8 +59,8 @@ group=2 UMLClass - 1480 - 1040 + 1360 + 520 130 60 @@ -73,8 +73,8 @@ group=2 UMLClass - 1350 - 1070 + 1230 + 550 130 30 @@ -86,8 +86,8 @@ group=2 UMLState - 1090 - 870 + 1040 + 430 120 40 @@ -99,8 +99,8 @@ halign=left UMLState - 1090 - 1060 + 1040 + 530 120 40 @@ -112,8 +112,8 @@ halign=left UMLState - 1090 - 1240 + 1040 + 630 120 40 @@ -125,30 +125,30 @@ halign=left Relation - 1130 - 990 + 1090 + 500 30 - 90 + 50 lt=<- - 10.0;70.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 1120 - 1170 - 50 - 90 + 1090 + 600 + 30 + 50 lt=<- - 30.0;70.0;10.0;10.0 + 10.0;30.0;10.0;10.0 UMLState - 1090 - 1380 + 1040 + 830 120 40 @@ -161,43 +161,43 @@ style=wordwrap Relation - 1140 - 1320 - 60 - 80 + 1090 + 800 + 30 + 50 lt=<- - 40.0;60.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 1120 - 1090 - 50 - 90 + 1090 + 560 + 30 + 50 lt=<- - 10.0;70.0;30.0;10.0 + 10.0;30.0;10.0;10.0 UMLClass - 840 - 840 - 820 - 1090 + 940 + 390 + 590 + 810 For Each SyncDirectory -halign=left +halign=right layer=-1 UMLClass - 1340 - 1030 + 1220 + 510 280 80 @@ -210,30 +210,30 @@ group=2 Relation - 1200 - 1240 - 160 - 40 + 1150 + 640 + 90 + 30 lt=. - 140.0;10.0;10.0;20.0 + 70.0;10.0;10.0;10.0 Relation - 1150 - 800 + 1090 + 370 30 - 90 + 80 lt=<- - 10.0;70.0;10.0;10.0 + 10.0;60.0;10.0;10.0 UMLSpecialState - 1150 - 790 + 1090 + 360 20 20 @@ -243,8 +243,8 @@ group=2 UMLSpecialState - 1150 - 1970 + 1090 + 1220 20 20 @@ -254,30 +254,30 @@ group=2 Relation - 1130 - 1500 - 50 - 90 + 1090 + 900 + 30 + 50 lt=<- - 30.0;70.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 1150 - 1900 + 1090 + 1170 30 - 90 + 70 lt=<- - 10.0;70.0;10.0;10.0 + 10.0;50.0;10.0;10.0 UMLState - 1070 - 1720 + 1040 + 1030 120 50 @@ -290,9 +290,9 @@ style=wordwrap UMLState - 1100 - 1160 - 150 + 1030 + 590 + 140 20 State 1 : LIST-1 @@ -302,8 +302,8 @@ bg=orange UMLState - 1070 - 1570 + 1040 + 930 120 40 @@ -316,32 +316,32 @@ style=wordwrap UMLState - 1110 - 1490 - 160 + 1030 + 890 + 140 20 - State 3 : SYNC-1 + State 4 : SYNC-1 bg=orange UMLState - 1110 - 1640 - 150 + 1030 + 990 + 140 20 - State 4 : NEW-2 + State 5 : NEW-2 bg=orange UMLState - 1110 - 980 + 1030 + 490 140 20 @@ -352,31 +352,31 @@ bg=orange UMLState - 1120 - 1790 + 1030 + 1100 140 20 - State 5 : OLD-1 + State 6 : OLD-1 bg=orange Relation - 1130 - 1650 - 40 - 90 + 1090 + 1000 + 30 + 50 lt=<- - 20.0;70.0;10.0;10.0 + 10.0;30.0;10.0;10.0 UMLSpecialState - 1140 - 1870 + 1080 + 1140 40 40 @@ -386,41 +386,41 @@ bg=orange Relation - 1150 - 1800 + 1090 + 1110 30 - 90 + 50 lt=<- - 10.0;70.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 920 - 880 - 240 - 1030 + 970 + 440 + 130 + 740 lt=<- - 170.0;10.0;20.0;260.0;10.0;1010.0;220.0;1010.0 + 70.0;10.0;10.0;10.0;10.0;720.0;110.0;720.0 Relation - 1130 - 900 - 40 - 100 + 1090 + 460 + 30 + 50 lt=<- - 10.0;80.0;20.0;10.0 + 10.0;30.0;10.0;10.0 UMLClass - 1340 - 1210 + 1220 + 610 280 80 @@ -433,20 +433,20 @@ group=1 Relation - 1200 - 1060 - 160 - 40 + 1150 + 540 + 90 + 30 lt=. - 10.0;20.0;140.0;10.0 + 10.0;10.0;70.0;10.0 UMLState - 1120 - 1310 - 160 + 1030 + 690 + 140 20 State 2 : LIST-2 @@ -456,45 +456,92 @@ bg=orange Relation - 1140 - 1270 + 1090 + 660 30 - 60 + 50 lt=<- - 10.0;40.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 1140 - 1760 - 40 + 1090 + 1070 + 30 50 lt=<- - 20.0;30.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 1110 - 1600 - 50 - 60 + 1090 + 960 + 30 + 50 lt=<- - 30.0;40.0;10.0;10.0 + 10.0;30.0;10.0;10.0 Relation - 1130 - 1410 - 60 - 100 + 1090 + 860 + 30 + 50 + + lt=<- + 10.0;30.0;10.0;10.0 + + + UMLState + + 1030 + 790 + 140 + 20 + + State 3 : LIST-3 +bg=orange + + + + UMLState + + 1040 + 730 + 120 + 40 + + get +ListModified +halign=left + + + + Relation + + 1090 + 760 + 30 + 50 + + lt=<- + 10.0;30.0;10.0;10.0 + + + Relation + + 1090 + 700 + 30 + 50 lt=<- - 10.0;80.0;40.0;10.0 + 10.0;30.0;10.0;10.0