diff --git a/distribution/ReleaseNotes.txt b/distribution/ReleaseNotes.txt index 0983b9322..0d3df7ebf 100644 --- a/distribution/ReleaseNotes.txt +++ b/distribution/ReleaseNotes.txt @@ -7,6 +7,7 @@ planned as v0.10 - With the right mouse button you can now select and move/delete wires. - Added a real bidirectional switch and a relay. - Added N and P channel FETs and some CMOS examples +- Improved and documented the file import strategy. v0.9, released on 03. Feb 2017 - improved documentation diff --git a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java index 65bfb6a84..ce3919d00 100644 --- a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java +++ b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java @@ -160,7 +160,7 @@ public class ElementLibrary implements Iterable try { description = elementNotFoundNotification.elementNotFound(file); } catch (IOException e) { - throw new ElementNotFoundException(Lang.get("msg_errorImportingModel", elementName)); + throw new ElementNotFoundException(Lang.get("msg_errorImportingModel", elementName), e); } if (description != null) diff --git a/src/main/java/de/neemann/digital/draw/library/ElementNotFoundException.java b/src/main/java/de/neemann/digital/draw/library/ElementNotFoundException.java index 8a31012de..13d404213 100644 --- a/src/main/java/de/neemann/digital/draw/library/ElementNotFoundException.java +++ b/src/main/java/de/neemann/digital/draw/library/ElementNotFoundException.java @@ -7,11 +7,21 @@ package de.neemann.digital.draw.library; */ public class ElementNotFoundException extends Exception { /** - * Creates a new Iistance + * Creates a new Instance * * @param message the error message */ public ElementNotFoundException(String message) { super(message); } + + /** + * Creates a new Instance + * + * @param message the error message + * @param cause the errors cause + */ + public ElementNotFoundException(String message, Exception cause) { + super(message, cause); + } } diff --git a/src/main/java/de/neemann/digital/draw/shapes/MissingShape.java b/src/main/java/de/neemann/digital/draw/shapes/MissingShape.java index 093dee09a..b57bbfbdf 100644 --- a/src/main/java/de/neemann/digital/draw/shapes/MissingShape.java +++ b/src/main/java/de/neemann/digital/draw/shapes/MissingShape.java @@ -28,7 +28,14 @@ public class MissingShape implements Shape { */ public MissingShape(String elementName, Exception cause) { this.message = Lang.get("msg_missingShape_N", elementName); - this.cause = cause.getMessage(); + this.cause = getMessage(cause); + } + + private String getMessage(Throwable e) { + String message = e.getMessage(); + if (e.getCause() != null) + message += "; " + getMessage(e.getCause()); + return message; } @Override diff --git a/src/main/java/de/neemann/digital/gui/LibrarySelector.java b/src/main/java/de/neemann/digital/gui/LibrarySelector.java index ce00c2080..34de4882a 100644 --- a/src/main/java/de/neemann/digital/gui/LibrarySelector.java +++ b/src/main/java/de/neemann/digital/gui/LibrarySelector.java @@ -22,6 +22,7 @@ import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; /** * The LibrarySelector is responsible for building the menu used to select items for adding them to the circuit. @@ -38,6 +39,7 @@ public class LibrarySelector implements ElementNotFoundNotification { private InsertHistory insertHistory; private CircuitComponent circuitComponent; private ArrayList importedElements; + private HashMap treeFileMap; /** * Creates a new library selector. @@ -138,6 +140,7 @@ public class LibrarySelector implements ElementNotFoundNotification { * @param filePath the file path */ public void setFilePath(File filePath) { + treeFileMap = null; this.filePath = filePath; } @@ -149,7 +152,47 @@ public class LibrarySelector implements ElementNotFoundNotification { if (primary.exists()) file = primary; - return importElement(file).description; + // check if there is a file with the given name below the current directory + // if so, load this file! + File f = getFileFromTree(file.getName()); + if (f != null) + return importElement(f).description; + + // than check if the exact given file exists + if (file.exists()) + return importElement(file).description; + + // could not find the given file + throw new IOException(Lang.get("err_couldNotFindIncludedFile_N0", file)); + } + + private File getFileFromTree(String name) throws IOException { + if (treeFileMap == null) { + treeFileMap = new HashMap<>(); + populateTreeFileMap(treeFileMap, filePath); + } else { + if (!treeFileMap.containsKey(name)) { // if file not in map rescan folder + treeFileMap.clear(); + populateTreeFileMap(treeFileMap, filePath); + } + } + return treeFileMap.get(name); + } + + private void populateTreeFileMap(HashMap map, File path) throws IOException { + File[] list = path.listFiles(); + if (list != null) { + for (File f : list) { + final String name = f.getName(); + if (f.isFile() && name.endsWith(".dig")) { + if (map.containsKey(name)) + throw new IOException(Lang.get("err_file_N0_ExistsTwiceBelow_N1", name, filePath)); + map.put(name, f); + } + if (f.isDirectory()) + populateTreeFileMap(map, f); + } + } } private static String getActionName(ElementTypeDescription typeDescription) { diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index b230684ad..f840447ce 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -420,6 +420,8 @@ Zur Analyse können Sie die Schaltung im Gatterschrittmodus ausführen. Ein Splitter der hochohmige Eingänge erlaubt, kann nur einen Eingang haben! Konnte den Order '{0}' nicht erzeugen! Ein Schalter kann nicht nur mit Eingängen verbunden werden. + Die Datei {0} existiert mehrfach unter {1} + Die Datei {0} konnte nicht gefunden werden. Adress-Bits Anzahl der Adress-Bits die verwendet werden. diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 81bbe6679..462ee0e5b 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -406,6 +406,8 @@ To analyse you can run the circuit in single gate step mode. A splitter which allows high z inputs can only have one input! Could not create folder '{0}'! It is not allowed to connect only inputs to a switch. + The file {0} exists multiple times below {1}. + Could not find the file {0}. Address Bits Number of address bits used. diff --git a/src/test/resources/docu/static_de.xml b/src/test/resources/docu/static_de.xml index 5009410fd..359a62b11 100644 --- a/src/test/resources/docu/static_de.xml +++ b/src/test/resources/docu/static_de.xml @@ -99,6 +99,37 @@ erzeugt wird. + + + Um eine Schaltung in mehrere Teile zu zerlegen, die auch unabhängig z.B. in einem VCS abgelegt und verfolgt + werden können, wird jede eingebundenen Schaltung in einer eigenen Datei gespeichert. Das hat zu Folge, dass in einer + Schaltung nur die Dateinamen der Teilschaltungen gespeichert sind, und diese Dateien zur Laufzeit im Dateisystem + gefunden werden müssen. Um die verschiedenen Arbeitsweisen der Nutzer bestmöglich zu unterstützen und dennoch + auf eine komplexe Verwaltung von Importpfaden usw. zu verzichten, ist eine etwas ungewöhnliche Import-Strategie + implementiert. + + + In einer Schaltung sind die absoluten Pfade der eingebetteten Schaltungen gespeichert. Wenn diese Datei eingelesen + werden soll, wird jedoch zunächst geprüft, ob sich eine Datei des gleichen Namens im Ordner der einbindenden Datei befindet. + Ist das der Fall, wird diese Datei verwendet. + + + Ist das nicht der Fall, werden alle Unterordner nach einer Datei des entsprechenden Namens durchsucht. Wird + eine passende Datei gefunden, wird diese importiert. Dabei kommt es nur auf den Dateinamen der einzulesenden + Datei an, nicht auf dessen Pfad. Entsprechend wird eine Fehlermeldung erzeugt, wenn sich in verschiedenen + Unterordneren mehrere Dateien gleichen Namens befinden, da dann Mehrdeutigkeiten entstehen. + + + Erst wenn bisher keine Datei gefunden wurde, wird der komplette absolute Pfad verwendet und es wird versucht, + diese Datei zu importieren. Gelingt auch das nicht, wird eine Fehlermeldung erzeugt. + + + Eine geeignete Projektstruktur sieht daher wie folgt aus: In einem eigenen Ordner befindet sich die Wurzelschaltung. + Alle importierten Schaltungen sollte sin im selben Ordner oder in Unterordnern befinden. Alle Schaltungen sollten + unterschiedliche Namen haben, es sollte also nicht vorkommen, dass sich in verschiedenen Ordnern Schaltungen + gleichen Namens befinden. + + Wie kann ich eine Leitung verschieben? diff --git a/src/test/resources/docu/static_en.xml b/src/test/resources/docu/static_en.xml index 8b5a975b3..56219c9b9 100644 --- a/src/test/resources/docu/static_en.xml +++ b/src/test/resources/docu/static_en.xml @@ -42,7 +42,6 @@ circuit behave in exact the same way as if all components had been inserted at the same circuit level. - All components must be connected via wires. It is not possible to connect two components to each other by @@ -97,7 +96,35 @@ circuit is not analyzed correctly, which means that an incorrect state transition table is generated. - + + + In order to decompose a circuit into several parts which are independent, e.g. can be stored and tracked in + a VCS, each embedded circuit is stored in its own file. The result is that in a circuit only the file names + of the subcircuits are stored, and these files must be found in the file system at runtime. + In order to support the various work methods of the users as best as possible and still to avoid a complex + administration of import paths, etc., a somewhat unusual import strategy is implemented. + + + The absolute paths of the embedded circuits are stored in a circuits file. If a file is to be imported, + however, it is first checked whether a file with the same name is located in the folder of the including + file. If this is the case, this file is used. + + + If this is not the case, all subfolders are searched for a file of the corresponding name. + If a suitable file is found, it is imported. This process only depends on the file name of the file to be + read, not on its path. Correspondingly, an error message is generated if there are several files of the + same name in different subfolders, since ambiguities then arise. + + + Only if no file has been found yet, the complete absolute path is used and an attempt is made to import + this file. If this is not successful, an error message is generated. + + + A suitable project structure therefore looks as follows: The root circuit is located in a separate folder. + All imported circuits should be in the same folder or subfolders. All circuits should have different names, + so it should not happen that there are circuits of the same name in different folders. + + How to move a wire?