diff --git a/build.gradle b/build.gradle index 970cb32..0a13e15 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,9 @@ tasks.withType(Jar) { from { configurations.runtime.collect { it.name.startsWith('ShrinkItArchive') || it.name.startsWith('applesingle-api') || - it.name.startsWith('bastools-api') ? zipTree(it) : 'fake' + it.name.startsWith('bastools-api') || it.name.startsWith('commons-csv') || + it.name.startsWith('gson') + ? zipTree(it) : 'fake' } } } @@ -77,6 +79,8 @@ dependencies { compile "net.sf.applecommander:ShrinkItArchive:$shkVersion" compile "net.sf.applecommander:applesingle-api:$asVersion" compile "net.sf.applecommander:bastools-api:$btVersion" + compile "org.apache.commons:commons-csv:$commonsCsvVersion" + compile "com.google.code.gson:gson:$gsonVersion" compileOnly "org.apache.ant:ant:$antVersion" testCompile "junit:junit:$junitVersion" diff --git a/gradle.properties b/gradle.properties index 006a040..5c572e8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,8 @@ piswtVersion=3.8.2 junitVersion=4.12 antVersion=1.8.2 commonsLang3Version=3.7 +commonsCsvVersion=1.8 +gsonVersion=2.8.6 # Maven Central Repository G and A of GAV coordinate. :-) group=net.sf.applecommander diff --git a/src/main/java/com/webcodepro/applecommander/storage/FormattedDisk.java b/src/main/java/com/webcodepro/applecommander/storage/FormattedDisk.java index 188bb0a..b33c615 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/FormattedDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/FormattedDisk.java @@ -97,10 +97,12 @@ public abstract class FormattedDisk extends Disk implements DirectoryEntry { private String title; private int maximumWidth; private int alignment; - public FileColumnHeader(String title, int maximumWidth, int alignment) { + private String key; + public FileColumnHeader(String title, int maximumWidth, int alignment, String key) { this.title = title; this.maximumWidth = maximumWidth; this.alignment = alignment; + this.key = key; } public String getTitle() { return title; @@ -111,6 +113,9 @@ public abstract class FormattedDisk extends Disk implements DirectoryEntry { public int getAlignment() { return alignment; } + public String getKey() { + return key; + } public boolean isLeftAlign() { return alignment == ALIGN_LEFT; } @@ -207,13 +212,13 @@ public abstract class FormattedDisk extends Disk implements DirectoryEntry { public List getFileColumnHeaders(int displayMode) { List list = new ArrayList<>(); list.add(new FileColumnHeader(textBundle - .get("Name"), 30, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$ + .get("Name"), 30, FileColumnHeader.ALIGN_LEFT, "name")); list.add(new FileColumnHeader(textBundle - .get("Type"), 8, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + .get("Type"), 8, FileColumnHeader.ALIGN_CENTER, "type")); list.add(new FileColumnHeader(textBundle - .get("SizeInBytes"), 6, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ + .get("SizeInBytes"), 6, FileColumnHeader.ALIGN_RIGHT, "sizeInBytes")); list.add(new FileColumnHeader(textBundle - .get("LockedQ"), 6, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + .get("LockedQ"), 6, FileColumnHeader.ALIGN_CENTER, "locked")); return list; } diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java index 906e91c..4a0d29c 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/cpm/CpmFormatDisk.java @@ -497,24 +497,24 @@ public class CpmFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(textBundle.get("Name"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("Type"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + list.add(new FileColumnHeader(textBundle.get("Name"), 8, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("Type"), 3, + FileColumnHeader.ALIGN_LEFT, "type")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(textBundle.get("Name"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("Type"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("CpmFormatDisk.UserNumber"), 4, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("LockedQ"), 6, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); + list.add(new FileColumnHeader(textBundle.get("Name"), 8, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("Type"), 3, + FileColumnHeader.ALIGN_LEFT, "type")); + list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, + FileColumnHeader.ALIGN_RIGHT, "sizeInBytes")); + list.add(new FileColumnHeader(textBundle.get("CpmFormatDisk.UserNumber"), 4, + FileColumnHeader.ALIGN_RIGHT, "user")); + list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, + FileColumnHeader.ALIGN_CENTER, "deleted")); + list.add(new FileColumnHeader(textBundle.get("LockedQ"), 6, + FileColumnHeader.ALIGN_CENTER, "locked")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java index a2a9997..320a952 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/dos33/DosFormatDisk.java @@ -354,23 +354,28 @@ public class DosFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, + FileColumnHeader.ALIGN_RIGHT, "sectors")); + list.add(new FileColumnHeader(textBundle.get("Name"), 30, + FileColumnHeader.ALIGN_LEFT, "name")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("Name"), 30, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, + FileColumnHeader.ALIGN_RIGHT, "sectors")); + list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, + FileColumnHeader.ALIGN_CENTER, "deleted")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, + FileColumnHeader.ALIGN_CENTER, "trackAndSectorList")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java index 80adfb6..27f9385 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/gutenberg/GutenbergFormatDisk.java @@ -324,23 +324,28 @@ public class GutenbergFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, + FileColumnHeader.ALIGN_RIGHT, "sectors")); + list.add(new FileColumnHeader(textBundle.get("Name"), 30, + FileColumnHeader.ALIGN_LEFT, "name")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("Name"), 30, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, + FileColumnHeader.ALIGN_RIGHT, "sectors")); + list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, + FileColumnHeader.ALIGN_CENTER, "deleted")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, + FileColumnHeader.ALIGN_CENTER, "trackAndSectorList")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java index 32ebc4e..a974b64 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/nakedos/NakedosFormatDisk.java @@ -324,23 +324,28 @@ public class NakedosFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, + FileColumnHeader.ALIGN_RIGHT, "sectors")); + list.add(new FileColumnHeader(textBundle.get("Name"), 30, + FileColumnHeader.ALIGN_LEFT, "name")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 30, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("Name"), 30, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.SizeInSectors"), 3, + FileColumnHeader.ALIGN_RIGHT, "sectors")); + list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, + FileColumnHeader.ALIGN_CENTER, "deleted")); + list.add(new FileColumnHeader(textBundle.get("DosFormatDisk.TrackAndSectorList"), 7, + FileColumnHeader.ALIGN_CENTER, "trackAndSectorList")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java index 87528a6..0f46421 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/pascal/PascalFormatDisk.java @@ -455,35 +455,35 @@ public class PascalFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(textBundle.get("Modified"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("Name"), 15, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + list.add(new FileColumnHeader(textBundle.get("Modified"), 8, + FileColumnHeader.ALIGN_CENTER, "modified")); + list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, + FileColumnHeader.ALIGN_RIGHT, "blocks")); + list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, + FileColumnHeader.ALIGN_CENTER, "filetype")); + list.add(new FileColumnHeader(textBundle.get("Name"), 15, + FileColumnHeader.ALIGN_LEFT, "name")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(textBundle.get("Modified"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); + list.add(new FileColumnHeader(textBundle.get("Modified"), 8, + FileColumnHeader.ALIGN_CENTER, "modified")); + list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, + FileColumnHeader.ALIGN_RIGHT, "blocks")); list.add(new FileColumnHeader( - textBundle.get("PascalFormatDisk.BytesInLastBlock"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("Name"), 15, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + textBundle.get("PascalFormatDisk.BytesInLastBlock"), 3, + FileColumnHeader.ALIGN_RIGHT, "bytesInLastBlock")); + list.add(new FileColumnHeader(textBundle.get("SizeInBytes"), 6, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, + FileColumnHeader.ALIGN_CENTER, "filetype")); + list.add(new FileColumnHeader(textBundle.get("Name"), 15, + FileColumnHeader.ALIGN_LEFT, "name")); list.add(new FileColumnHeader( - textBundle.get("PascalFormatDisk.FirstBlock"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); + textBundle.get("PascalFormatDisk.FirstBlock"), 3, + FileColumnHeader.ALIGN_RIGHT, "firstBlock")); list.add(new FileColumnHeader( - textBundle.get("PascalFormatDisk.LastBlock"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); + textBundle.get("PascalFormatDisk.LastBlock"), 3, + FileColumnHeader.ALIGN_RIGHT, "lastBlock")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java index 237002e..16523fc 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/prodos/ProdosFormatDisk.java @@ -442,46 +442,59 @@ public class ProdosFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 15, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); - list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("Modified"), 10, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("Name"), 15, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, + FileColumnHeader.ALIGN_RIGHT, "blocks")); + list.add(new FileColumnHeader(textBundle.get("Modified"), 10, + FileColumnHeader.ALIGN_CENTER, "modified")); list.add(new FileColumnHeader( - textBundle.get("ProdosFormatDisk.Created"), 10, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); + textBundle.get("ProdosFormatDisk.Created"), 10, + FileColumnHeader.ALIGN_CENTER, "created")); list.add(new FileColumnHeader( - textBundle.get("ProdosFormatDisk.Length"), 10, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); + textBundle.get("ProdosFormatDisk.Length"), 10, + FileColumnHeader.ALIGN_RIGHT, "size")); list.add(new FileColumnHeader( - textBundle.get("ProdosFormatDisk.AuxType"), 8, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); + textBundle.get("ProdosFormatDisk.AuxType"), 8, + FileColumnHeader.ALIGN_LEFT, "auxType")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 15, //$NON-NLS-1$ - FileColumnHeader.ALIGN_LEFT)); - list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, //$NON-NLS-1$ - FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Permissions"), 8, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.DirectoryQ"), 9, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, //$NON-NLS-1$ - FileColumnHeader.ALIGN_RIGHT)); - list.add(new FileColumnHeader(textBundle.get("Modified"), 10, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Created"), 10, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Length"), 10, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.AuxType"), 8, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.DirectoryHeader"), 5, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.KeyBlock"), 5, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.KeyType"), 8, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Changed"), 5, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.MinimumProdosVersion"), 2, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.ProdosVersion"), 2, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + list.add(new FileColumnHeader(" ", 1, FileColumnHeader.ALIGN_CENTER, "locked")); + list.add(new FileColumnHeader(textBundle.get("Name"), 15, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, + FileColumnHeader.ALIGN_CENTER, "deleted")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Permissions"), 8, + FileColumnHeader.ALIGN_LEFT, "permissions")); + list.add(new FileColumnHeader(textBundle.get("Filetype"), 8, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.DirectoryQ"), 9, + FileColumnHeader.ALIGN_CENTER, "directory")); + list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, + FileColumnHeader.ALIGN_RIGHT, "blocks")); + list.add(new FileColumnHeader(textBundle.get("Modified"), 10, + FileColumnHeader.ALIGN_CENTER, "modified")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Created"), 10, + FileColumnHeader.ALIGN_CENTER, "created")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Length"), 10, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.AuxType"), 8, + FileColumnHeader.ALIGN_LEFT, "auxType")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.DirectoryHeader"), 5, + FileColumnHeader.ALIGN_RIGHT, "directory")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.KeyBlock"), 5, + FileColumnHeader.ALIGN_RIGHT, "keyBlock")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.KeyType"), 8, + FileColumnHeader.ALIGN_LEFT, "keyType")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.Changed"), 5, + FileColumnHeader.ALIGN_CENTER, "changed")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.MinimumProdosVersion"), 2, + FileColumnHeader.ALIGN_CENTER, "minimumProdosVersion")); + list.add(new FileColumnHeader(textBundle.get("ProdosFormatDisk.ProdosVersion"), 2, + FileColumnHeader.ALIGN_CENTER, "prodosVersion")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java b/src/main/java/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java index 45e5d42..fc34595 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/os/rdos/RdosFormatDisk.java @@ -310,20 +310,32 @@ public class RdosFormatDisk extends FormattedDisk { List list = new ArrayList<>(); switch (displayMode) { case FILE_DISPLAY_NATIVE: - list.add(new FileColumnHeader(textBundle.get("Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 24, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Size"), 6, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.StartingBlock"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ + list.add(new FileColumnHeader(textBundle.get("Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, + FileColumnHeader.ALIGN_RIGHT, "blocks")); + list.add(new FileColumnHeader(textBundle.get("Name"), 24, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Size"), 6, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.StartingBlock"), 3, + FileColumnHeader.ALIGN_RIGHT, "firstBlock")); break; case FILE_DISPLAY_DETAIL: - list.add(new FileColumnHeader(textBundle.get("Type"), 1, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("Name"), 24, FileColumnHeader.ALIGN_LEFT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Size"), 6, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.StartingBlock"), 3, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Address"), 5, FileColumnHeader.ALIGN_RIGHT)); //$NON-NLS-1$ - list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, FileColumnHeader.ALIGN_CENTER)); //$NON-NLS-1$ + list.add(new FileColumnHeader(textBundle.get("Type"), 1, + FileColumnHeader.ALIGN_CENTER, "type")); + list.add(new FileColumnHeader(textBundle.get("Blocks"), 3, + FileColumnHeader.ALIGN_RIGHT, "blocks")); + list.add(new FileColumnHeader(textBundle.get("Name"), 24, + FileColumnHeader.ALIGN_LEFT, "name")); + list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Size"), 6, + FileColumnHeader.ALIGN_RIGHT, "size")); + list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.StartingBlock"), 3, + FileColumnHeader.ALIGN_RIGHT, "firstBlock")); + list.add(new FileColumnHeader(textBundle.get("RdosFormatDisk.Address"), 5, + FileColumnHeader.ALIGN_RIGHT, "address")); + list.add(new FileColumnHeader(textBundle.get("DeletedQ"), 7, + FileColumnHeader.ALIGN_CENTER, "deleted")); break; default: // FILE_DISPLAY_STANDARD list.addAll(super.getFileColumnHeaders(displayMode)); diff --git a/src/main/java/com/webcodepro/applecommander/ui/AntTask.java b/src/main/java/com/webcodepro/applecommander/ui/AntTask.java index eb4d9e6..7ec6a05 100644 --- a/src/main/java/com/webcodepro/applecommander/ui/AntTask.java +++ b/src/main/java/com/webcodepro/applecommander/ui/AntTask.java @@ -168,11 +168,11 @@ public class AntTask extends Task { String[] onlyOneImage = { "nonsense", _imageName }; if (_command.equals("ls")) - com.webcodepro.applecommander.ui.ac.showDirectory(onlyOneImage, FormattedDisk.FILE_DISPLAY_STANDARD); + com.webcodepro.applecommander.ui.ac.showDirectory(DirectoryLister.text(FormattedDisk.FILE_DISPLAY_STANDARD), onlyOneImage); else if (_command.equals("l")) - com.webcodepro.applecommander.ui.ac.showDirectory(onlyOneImage, FormattedDisk.FILE_DISPLAY_NATIVE); + com.webcodepro.applecommander.ui.ac.showDirectory(DirectoryLister.text(FormattedDisk.FILE_DISPLAY_NATIVE), onlyOneImage); else // Assume "ll" - com.webcodepro.applecommander.ui.ac.showDirectory(onlyOneImage, FormattedDisk.FILE_DISPLAY_DETAIL); + com.webcodepro.applecommander.ui.ac.showDirectory(DirectoryLister.text(FormattedDisk.FILE_DISPLAY_DETAIL), onlyOneImage); } catch (IOException io) { diff --git a/src/main/java/com/webcodepro/applecommander/ui/DirectoryLister.java b/src/main/java/com/webcodepro/applecommander/ui/DirectoryLister.java new file mode 100644 index 0000000..cbf07c0 --- /dev/null +++ b/src/main/java/com/webcodepro/applecommander/ui/DirectoryLister.java @@ -0,0 +1,180 @@ +package com.webcodepro.applecommander.ui; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.DiskUnrecognizedException; +import com.webcodepro.applecommander.storage.FormattedDisk; +import com.webcodepro.applecommander.storage.FormattedDisk.FileColumnHeader; +import com.webcodepro.applecommander.util.TextBundle; +import com.webcodepro.applecommander.util.filestreamer.FileStreamer; +import com.webcodepro.applecommander.util.filestreamer.FileTuple; + +public class DirectoryLister { + private static TextBundle textBundle = UiBundle.getInstance(); + + public static DirectoryLister text(int display) { + return new DirectoryLister(new TextListingStrategy(display)); + } + public static DirectoryLister csv(int display) { + try { + return new DirectoryLister(new CsvListingStrategy(display)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public static DirectoryLister json(int display) { + return new DirectoryLister(new JsonListingStrategy(display)); + } + + private ListingStrategy strategy; + + private DirectoryLister(ListingStrategy strategy) { + this.strategy = strategy; + } + + public void list(String filename) throws DiskUnrecognizedException, IOException { + Disk disk = new Disk(filename); + strategy.first(disk); + + FileStreamer.forDisk(disk) + .recursive(true) + .includeDeleted(false) + .beforeDisk(strategy::beforeDisk) + .afterDisk(strategy::afterDisk) + .stream() + .forEach(strategy::forEach); + + strategy.last(disk); + } + + private static abstract class ListingStrategy { + protected int display; + protected ListingStrategy(int display) { + this.display = display; + } + + protected void first(Disk d) {}; + protected void beforeDisk(FormattedDisk d) {} + protected void afterDisk(FormattedDisk d) {} + protected void forEach(FileTuple f) {} + protected void last(Disk d) {}; + } + + private static class TextListingStrategy extends ListingStrategy { + protected TextListingStrategy(int display) { + super(display); + } + protected void beforeDisk(FormattedDisk disk) { + System.out.printf("%s %s\n", disk.getFilename(), disk.getDiskName()); + } + protected void afterDisk(FormattedDisk disk) { + System.out.printf("%s\n\n", + textBundle.format("CommandLineStatus", + disk.getFormat(), disk.getFreeSpace(), disk.getUsedSpace())); + } + protected void forEach(FileTuple tuple) { + System.out.printf("%s%s\n", + repeat(" ", tuple.paths.size()), + String.join(" ", tuple.fileEntry.getFileColumnData(display))); + } + + private String repeat(String s, int n) { + StringBuilder sb = new StringBuilder(); + for (int i=0; i headers = tuple.formattedDisk.getFileColumnHeaders(display); + List columns = tuple.fileEntry.getFileColumnData(display); + for (int i=0; i 3 ? new PrintStream(new FileOutputStream(args[3])) : System.out)); @@ -510,61 +522,23 @@ public class ac { } return null; } - + /** * Display a directory listing of each disk in args. */ - static void showDirectory(String[] args, int display) throws IOException { - for (int d = 1; d < args.length; d++) { + static void showDirectory(DirectoryLister dl, String[] args) throws IOException { + for (String filename : Arrays.copyOfRange(args, 1, args.length)) { try { - Disk disk = new Disk(args[d]); - FormattedDisk[] formattedDisks = disk.getFormattedDisks(); - for (int i = 0; i < formattedDisks.length; i++) { - FormattedDisk formattedDisk = formattedDisks[i]; - System.out.print(args[d] + " "); - System.out.println(formattedDisk.getDiskName()); - List files = formattedDisk.getFiles(); - if (files != null) { - showFiles(files, "", display); //$NON-NLS-1$ - } - System.out.println(textBundle.format("CommandLineStatus", //$NON-NLS-1$ - formattedDisk.getFormat(), - formattedDisk.getFreeSpace(), - formattedDisk.getUsedSpace())); - System.out.println(); - } + dl.list(filename); } catch (DiskException e) { throw new IOException(e); } catch (RuntimeException e) { - System.out.println(args[d] + ": " + e.getMessage()); //$NON-NLS-1$ + System.out.println(filename + ": " + e.getMessage()); //$NON-NLS-1$ System.out.println(); } } } - /** - * Recursive routine to display directory entries. In the instance of a - * system with directories (e.g. ProDOS), this really returns the first file - * with the given filename. - */ - static void showFiles(List files, String indent, int display) throws DiskException { - for (FileEntry entry : files) { - if (!entry.isDeleted()) { - List data = entry.getFileColumnData(display); - System.out.print(indent); - for (int d = 0; d < data.size(); d++) { - System.out.print(data.get(d)); - System.out.print(" "); //$NON-NLS-1$ - } - System.out.println(); - } - if (entry.isDirectory()) { - showFiles(((DirectoryEntry) entry).getFiles(), - indent + " ", display); //$NON-NLS-1$ - } - } - } - /** * Display information about each disk in args. */ diff --git a/src/main/java/com/webcodepro/applecommander/util/DiskRecurser.java b/src/main/java/com/webcodepro/applecommander/util/DiskRecurser.java new file mode 100644 index 0000000..66f8a95 --- /dev/null +++ b/src/main/java/com/webcodepro/applecommander/util/DiskRecurser.java @@ -0,0 +1,96 @@ +package com.webcodepro.applecommander.util; + +import java.util.function.Consumer; + +import com.webcodepro.applecommander.storage.DirectoryEntry; +import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.DiskException; +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FormattedDisk; + +public class DiskRecurser { + private boolean recursive; + private Consumer diskHeader = DiskRecurser::doNothing; + private Consumer diskFooter = DiskRecurser::doNothing; + private Consumer formattedDiskHeader = DiskRecurser::doNothing; + private Consumer formattedDiskFooter = DiskRecurser::doNothing; + private Consumer fileEntryConsumer = DiskRecurser::doNothing; + private Consumer directoryEntryConsumer = DiskRecurser::doNothing; + + private DiskRecurser() {} + + public void recurse(Disk disk) throws DiskException { + diskHeader.accept(disk); + for (FormattedDisk formattedDisk : disk.getFormattedDisks()) { + recurse(formattedDisk); + } + diskFooter.accept(disk); + } + + public void recurse(FormattedDisk formattedDisk) throws DiskException { + formattedDiskHeader.accept(formattedDisk); + for (FileEntry fileEntry : formattedDisk.getFiles()) { + fileEntryConsumer.accept(fileEntry); + if (fileEntry.isDirectory() && recursive) { + directoryEntryConsumer.accept((DirectoryEntry)fileEntry); + } + } + formattedDiskFooter.accept(formattedDisk); + } + + public void recurse(DirectoryEntry directoryEntry) throws DiskException { + for (FileEntry fileEntry : directoryEntry.getFiles()) { + if (fileEntry.isDirectory() && recursive) { + directoryEntryConsumer.accept((DirectoryEntry)fileEntry); + recurse((DirectoryEntry)fileEntry); + } else { + fileEntryConsumer.accept(fileEntry); + } + } + } + + public static void doNothing(Disk disk) {} + public static void doNothing(FormattedDisk formattedDisk) {} + public static void doNothing(FileEntry fileEntry) {} + public static void doNothing(DirectoryEntry directoryEntry) {} + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private DiskRecurser recurser = new DiskRecurser(); + + public Builder recursive() { + recurser.recursive = true; + return this; + } + public Builder diskHeader(Consumer consumer) { + recurser.diskHeader = consumer; + return this; + } + public Builder diskFooter(Consumer consumer) { + recurser.diskFooter = consumer; + return this; + } + public Builder formattedDiskHeader(Consumer consumer) { + recurser.formattedDiskHeader = consumer; + return this; + } + public Builder formattedDiskFooter(Consumer consumer) { + recurser.formattedDiskFooter = consumer; + return this; + } + public Builder fileEntry(Consumer consumer) { + recurser.fileEntryConsumer = consumer; + return this; + } + public Builder directoryEntry(Consumer consumer) { + recurser.directoryEntryConsumer = consumer; + return this; + } + public DiskRecurser build() { + return recurser; + } + } +} diff --git a/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java b/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java new file mode 100644 index 0000000..f57eaef --- /dev/null +++ b/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileStreamer.java @@ -0,0 +1,194 @@ +package com.webcodepro.applecommander.util.filestreamer; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.DiskException; +import com.webcodepro.applecommander.storage.DiskUnrecognizedException; +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FormattedDisk; + +/** + * FileStreamer is utility class that will (optionally) recurse through all directories and + * feed a Java Stream of useful directory walking detail (disk, directory, file, and the + * textual path to get there). + *

+ * Sample usage: + *

+ * FileStreamer.forDisk(image)
+ *             .ignoreErrors(true)
+ *             .stream()
+ *             .filter(this::fileFilter)
+ *             .forEach(fileHandler);
+ * 
+ * + * @author rob + */ +public class FileStreamer { + private static final Consumer NOOP_CONSUMER = d -> {}; + + public static FileStreamer forDisk(File file) throws IOException, DiskUnrecognizedException { + return forDisk(file.getPath()); + } + public static FileStreamer forDisk(String fileName) throws IOException, DiskUnrecognizedException { + return new FileStreamer(new Disk(fileName)); + } + public static FileStreamer forDisk(Disk disk) throws DiskUnrecognizedException { + return new FileStreamer(disk); + } + + private FormattedDisk[] formattedDisks = null; + + // Processor flags (used in gathering) + private boolean ignoreErrorsFlag = false; + private boolean recursiveFlag = true; + + // Processor events + private Consumer beforeDisk = NOOP_CONSUMER; + private Consumer afterDisk = NOOP_CONSUMER; + + // Filters + private Predicate filters = this::deletedFileFilter; + private boolean includeDeletedFlag = false; + private List pathMatchers = new ArrayList<>(); + + private FileStreamer(Disk disk) throws DiskUnrecognizedException { + this.formattedDisks = disk.getFormattedDisks(); + } + + public FileStreamer ignoreErrors(boolean flag) { + this.ignoreErrorsFlag = flag; + return this; + } + public FileStreamer recursive(boolean flag) { + this.recursiveFlag = flag; + return this; + } + public FileStreamer matchGlobs(List globs) { + if (globs != null && !globs.isEmpty()) { + FileSystem fs = FileSystems.getDefault(); + for (String glob : globs) { + pathMatchers.add(fs.getPathMatcher("glob:" + glob)); + } + this.filters = filters.and(this::globFilter); + } + return this; + } + public FileStreamer matchGlobs(String... globs) { + return matchGlobs(Arrays.asList(globs)); + } + public FileStreamer includeTypeOfFile(TypeOfFile type) { + this.filters = filters.and(type.predicate); + return this; + } + public FileStreamer includeDeleted(boolean flag) { + this.includeDeletedFlag = flag; + return this; + } + public FileStreamer beforeDisk(Consumer consumer) { + this.beforeDisk = consumer; + return this; + } + public FileStreamer afterDisk(Consumer consumer) { + this.afterDisk = consumer; + return this; + } + + public Stream stream() { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(), 0), false) + .filter(filters); + } + public Iterator iterator() { + return new FileTupleIterator(); + } + + protected boolean deletedFileFilter(FileTuple tuple) { + return includeDeletedFlag || !tuple.fileEntry.isDeleted(); + } + protected boolean globFilter(FileTuple tuple) { + FileSystem fs = FileSystems.getDefault(); + Path path = Paths.get(String.join(fs.getSeparator(), tuple.paths), tuple.fileEntry.getFilename()); + for (PathMatcher pathMatcher : pathMatchers) { + if (pathMatcher.matches(path)) return true; + } + return false; + } + + private class FileTupleIterator implements Iterator { + private LinkedList files = new LinkedList<>(); + private FormattedDisk currentDisk; + + private FileTupleIterator() { + for (FormattedDisk formattedDisk : formattedDisks) { + files.addAll(toTupleList(FileTuple.of(formattedDisk))); + } + } + + @Override + public boolean hasNext() { + boolean hasNext = !files.isEmpty(); + if (hasNext) { + FileTuple tuple = files.peek(); + // Was there a disk switch? + if (tuple.formattedDisk != currentDisk) { + if (currentDisk != null) { + afterDisk.accept(currentDisk); + } + currentDisk = tuple.formattedDisk; + beforeDisk.accept(currentDisk); + } + } else { + if (currentDisk != null) { + afterDisk.accept(currentDisk); + } + currentDisk = null; + } + return hasNext; + } + + @Override + public FileTuple next() { + if (hasNext()) { + FileTuple tuple = files.removeFirst(); + if (recursiveFlag && tuple.fileEntry.isDirectory()) { + FileTuple newTuple = tuple.pushd(tuple.fileEntry); + files.addAll(0, toTupleList(newTuple)); + } + return tuple; + } else { + throw new NoSuchElementException(); + } + } + + private List toTupleList(FileTuple tuple) { + List list = new ArrayList<>(); + try { + for (FileEntry fileEntry : tuple.directoryEntry.getFiles()) { + list.add(tuple.of(fileEntry)); + } + } catch (DiskException e) { + if (!ignoreErrorsFlag) { + throw new RuntimeException(e); + } + } + return list; + } + } +} diff --git a/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileTuple.java b/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileTuple.java new file mode 100644 index 0000000..ec054fa --- /dev/null +++ b/src/main/java/com/webcodepro/applecommander/util/filestreamer/FileTuple.java @@ -0,0 +1,42 @@ +package com.webcodepro.applecommander.util.filestreamer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +import com.webcodepro.applecommander.storage.DirectoryEntry; +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FormattedDisk; + +public class FileTuple { + private static final Logger LOG = Logger.getLogger(FileTuple.class.getName()); + public final FormattedDisk formattedDisk; + public final List paths; + public final DirectoryEntry directoryEntry; + public final FileEntry fileEntry; + + private FileTuple(FormattedDisk formattedDisk, + List paths, + DirectoryEntry directoryEntry, + FileEntry fileEntry) { + this.formattedDisk = formattedDisk; + this.paths = Collections.unmodifiableList(paths); + this.directoryEntry = directoryEntry; + this.fileEntry = fileEntry; + } + + public FileTuple pushd(FileEntry directoryEntry) { + LOG.fine("Adding directory " + directoryEntry.getFilename()); + List newPaths = new ArrayList<>(paths); + newPaths.add(directoryEntry.getFilename()); + return new FileTuple(formattedDisk, newPaths, (DirectoryEntry)directoryEntry, null); + } + public FileTuple of(FileEntry fileEntry) { + return new FileTuple(formattedDisk, paths, directoryEntry, fileEntry); + } + + public static FileTuple of(FormattedDisk disk) { + return new FileTuple(disk, new ArrayList(), (DirectoryEntry)disk, null); + } +} diff --git a/src/main/java/com/webcodepro/applecommander/util/filestreamer/TypeOfFile.java b/src/main/java/com/webcodepro/applecommander/util/filestreamer/TypeOfFile.java new file mode 100644 index 0000000..c092121 --- /dev/null +++ b/src/main/java/com/webcodepro/applecommander/util/filestreamer/TypeOfFile.java @@ -0,0 +1,15 @@ +package com.webcodepro.applecommander.util.filestreamer; + +import java.util.function.Predicate; + +public enum TypeOfFile { + FILE(tuple -> !tuple.fileEntry.isDirectory()), + DIRECTORY(tuple -> tuple.fileEntry.isDirectory()), + BOTH(tuple -> true); + + public final Predicate predicate; + + private TypeOfFile(Predicate predicate) { + this.predicate = predicate; + } +} \ No newline at end of file diff --git a/src/test/java/com/webcodepro/applecommander/util/filestreamer/FileStreamerTest.java b/src/test/java/com/webcodepro/applecommander/util/filestreamer/FileStreamerTest.java new file mode 100644 index 0000000..f67f34a --- /dev/null +++ b/src/test/java/com/webcodepro/applecommander/util/filestreamer/FileStreamerTest.java @@ -0,0 +1,85 @@ +package com.webcodepro.applecommander.util.filestreamer; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Test; + +import com.webcodepro.applecommander.storage.DiskUnrecognizedException; + +public class FileStreamerTest { + private static final List EXPECTED_MERLIN = Arrays.asList( + "PRODOS", "MERLIN.SYSTEM", "PARMS", "ED", "ED.16", + "SOURCEROR", "SOURCEROR/OBJ", "SOURCEROR/LABELS", "SOURCEROR/LABELS.S", + "LIBRARY", "LIBRARY/SENDMSG.S", "LIBRARY/PRDEC.S", "LIBRARY/FPMACROS.S", + "LIBRARY/MACROS.S", "LIBRARY/ROCKWELL.S", + "SOURCE", "SOURCE/PARMS.S", "SOURCE/EDMAC.S", "SOURCE/KEYMAC.S", + "SOURCE/PRINTFILER.S", "SOURCE/MAKE.DUMP.S", "SOURCE/CLOCK.S", + "SOURCE/PI.START.S", "SOURCE/PI.MAIN.S", "SOURCE/PI.LOOK.S", + "SOURCE/PI.DIV.S", "SOURCE/PI.ADD.S", "SOURCE/PI.MACS.S", + "SOURCE/PI.NAMES.S", + "UTILITIES", "UTILITIES/REMOVE.ED", "UTILITIES/EDMAC", "UTILITIES/CLOCK.12.ED", + "UTILITIES/XREF", "UTILITIES/XREFA", "UTILITIES/FORMATTER", + "UTILITIES/PRINTFILER", "UTILITIES/MON.65C02", "UTILITIES/MAKE.DUMP", + "UTILITIES/CONV.REL.LNK", "UTILITIES/CONV.LNK.REL", + "UTILITIES/CLR.HI.BIT", "UTILITIES/KEYMAC", + "PI", "PI/NAMES", "PI/START", "PI/MAIN", "PI/LOOK", "PI/DIV", "PI/ADD", "PI/OBJ" + ); + private static final List EXPECTED_UNIDOS = Arrays.asList( + "HELLO", "FORMATTER", "FORMATTER.OBJ", "MFID", "FUD", // Disk #1 + "HELLO", "MFID", "FUD" // Disk #2 + ); + + @Test + public void testRecursiveListMerlin() throws DiskUnrecognizedException, IOException { + List actual = + FileStreamer.forDisk("./src/test/resources/disks/MERLIN8PRO1.DSK") + .recursive(true) + .stream() + .map(this::makeFullPath) + .collect(Collectors.toList()); + + assertEquals(EXPECTED_MERLIN, actual); + } + + @Test + public void testNonRecursiveListMerlin() throws DiskUnrecognizedException, IOException { + List actual = + FileStreamer.forDisk("./src/test/resources/disks/MERLIN8PRO1.DSK") + .recursive(false) + .stream() + .map(this::makeFullPath) + .collect(Collectors.toList()); + + List expected = EXPECTED_MERLIN.stream() + .filter(s -> !s.contains("/")) + .collect(Collectors.toList()); + + assertEquals(expected, actual); + } + + @Test + public void testListUnidos() throws DiskUnrecognizedException, IOException { + List actual = + FileStreamer.forDisk("./src/test/resources/disks/UniDOS_3.3.dsk") + .recursive(true) + .stream() + .map(this::makeFullPath) + .collect(Collectors.toList()); + + assertEquals(EXPECTED_UNIDOS, actual); + } + + + private String makeFullPath(FileTuple tuple) { + if (tuple.paths == null || tuple.paths.isEmpty()) { + return tuple.fileEntry.getFilename(); + } else { + return String.join("/", String.join("/", tuple.paths), tuple.fileEntry.getFilename()); + } + } +} diff --git a/src/test/java/com/webcodepro/applecommander/util/filestreamer/FileTupleTest.java b/src/test/java/com/webcodepro/applecommander/util/filestreamer/FileTupleTest.java new file mode 100644 index 0000000..df0d6ad --- /dev/null +++ b/src/test/java/com/webcodepro/applecommander/util/filestreamer/FileTupleTest.java @@ -0,0 +1,31 @@ +package com.webcodepro.applecommander.util.filestreamer; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Test; + +import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.DiskException; +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FormattedDisk; + +public class FileTupleTest { + @Test + public void test() throws IOException, DiskException { + Disk disk = new Disk("./src/test/resources/disks/MERLIN8PRO1.DSK"); + FormattedDisk formattedDisk = disk.getFormattedDisks()[0]; + FileTuple tuple = FileTuple.of(formattedDisk); + FileEntry sourcerorDir = tuple.formattedDisk.getFile("SOURCEROR"); + tuple = tuple.pushd(sourcerorDir); + FileEntry labelsSource = tuple.directoryEntry.getFiles().get(2); + tuple = tuple.of(labelsSource); + + assertEquals(Arrays.asList("SOURCEROR"), tuple.paths); + assertEquals(formattedDisk, tuple.formattedDisk); + assertEquals(sourcerorDir, tuple.directoryEntry); + assertEquals(labelsSource, tuple.fileEntry); + } +} diff --git a/src/test/resources/disks/MERLIN8PRO1.DSK b/src/test/resources/disks/MERLIN8PRO1.DSK new file mode 100644 index 0000000..4131b05 Binary files /dev/null and b/src/test/resources/disks/MERLIN8PRO1.DSK differ diff --git a/src/test/resources/disks/UniDOS_3.3.dsk b/src/test/resources/disks/UniDOS_3.3.dsk new file mode 100644 index 0000000..02e4acf Binary files /dev/null and b/src/test/resources/disks/UniDOS_3.3.dsk differ