diff --git a/api/README.md b/api/README.md
index 5941e05..7971e3a 100644
--- a/api/README.md
+++ b/api/README.md
@@ -10,7 +10,7 @@ To include in a Maven project:
net.sf.applecommander
applesingle-api
- 1.0.0
+ 1.2.0
```
@@ -19,7 +19,7 @@ To include in a Gradle project:
```groovy
dependencies {
// ...
- compile "net.sf.applecommander:applesingle-api:1.0.0"
+ compile "net.sf.applecommander:applesingle-api:1.2.0"
// ...
}
```
@@ -58,3 +58,31 @@ as.save(file);
```
The `save(...)` method can save to a `File`, `Path`, or an `OutputStream`.
+
+## Entries
+
+If the higher-level API is insufficient, the lower-level API does allow either tracking of the processing
+(see code for the `analyze` subcommand) or alternate processing of `Entry` objects (see the `filter`
+subcommand).
+
+To tap into the `AppleSingleReader` events, add as many reporters as required. For example, the `analyze`
+command uses these to display the details of the AppleSingle file as it is read:
+
+```java
+AppleSingleReader reader = AppleSingleReader.builder(fileData)
+ .readAtReporter((start,chunk,desc) -> used.add(IntRange.of(start, start + chunk.length)))
+ .readAtReporter((start,chunk,desc) -> dumper.dump(start, chunk, desc))
+ .versionReporter(this::reportVersion)
+ .numberOfEntriesReporter(this::reportNumberOfEntries)
+ .entryReporter(this::reportEntry)
+ .build();
+```
+
+To work with the raw `Entry` objects, use the various `AppleSingle#asEntries` methods. For instance, the
+`filter` subcommand bypasses the `AppleSingle` object altogether to implement the filter:
+
+```java
+List entries = stdinFlag ? AppleSingle.asEntries(System.in) : AppleSingle.asEntries(inputFile);
+// ...
+AppleSingle.write(outputStream, newEntries);
+```
diff --git a/api/src/main/java/io/github/applecommander/applesingle/AppleSingle.java b/api/src/main/java/io/github/applecommander/applesingle/AppleSingle.java
index 09628ac..bab2fbb 100644
--- a/api/src/main/java/io/github/applecommander/applesingle/AppleSingle.java
+++ b/api/src/main/java/io/github/applecommander/applesingle/AppleSingle.java
@@ -111,6 +111,10 @@ public class AppleSingle {
}
}
+ /**
+ * Common write capability for an AppleSingle. Also can be used by external entities to
+ * write a properly formatted AppleSingle file without the ProDOS assumptions of AppleSingle.
+ */
public static void write(OutputStream outputStream, List entries) throws IOException {
final byte[] filler = new byte[16];
ByteBuffer buf = ByteBuffer.allocate(26).order(ByteOrder.BIG_ENDIAN);
diff --git a/api/src/main/java/io/github/applecommander/applesingle/Entry.java b/api/src/main/java/io/github/applecommander/applesingle/Entry.java
index dd9ccfe..0b7a4ff 100644
--- a/api/src/main/java/io/github/applecommander/applesingle/Entry.java
+++ b/api/src/main/java/io/github/applecommander/applesingle/Entry.java
@@ -6,6 +6,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Objects;
+/**
+ * Represents an AppleSingle entry.
+ */
public class Entry {
public static final int BYTES = 12;
private int entryId;
diff --git a/api/src/main/java/io/github/applecommander/applesingle/FileDatesInfo.java b/api/src/main/java/io/github/applecommander/applesingle/FileDatesInfo.java
index 2f89473..b977556 100644
--- a/api/src/main/java/io/github/applecommander/applesingle/FileDatesInfo.java
+++ b/api/src/main/java/io/github/applecommander/applesingle/FileDatesInfo.java
@@ -6,7 +6,7 @@ import java.time.Instant;
import java.util.function.IntSupplier;
public class FileDatesInfo {
- /** The number of seconds at the begining of the AppleSingle date epoch since the Unix epoch began. */
+ /** The number of seconds at the beginning of the AppleSingle date epoch since the Unix epoch began. */
public static final Instant EPOCH_INSTANT = Instant.parse("2000-01-01T00:00:00.00Z");
/** Per the AppleSingle technical notes. */
public static final int UNKNOWN_DATE = 0x80000000;
diff --git a/api/src/test/java/io/github/applecommander/applesingle/AppleSingleReaderTest.java b/api/src/test/java/io/github/applecommander/applesingle/AppleSingleReaderTest.java
new file mode 100644
index 0000000..4efe52b
--- /dev/null
+++ b/api/src/test/java/io/github/applecommander/applesingle/AppleSingleReaderTest.java
@@ -0,0 +1,72 @@
+package io.github.applecommander.applesingle;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+public class AppleSingleReaderTest {
+ @Test(expected = NullPointerException.class)
+ public void testDoesNotAcceptNull() {
+ AppleSingleReader.builder(null);
+ }
+
+ @Test
+ public void testReporters() throws IOException {
+ Ticker versionCalled = new Ticker();
+ Ticker numberOfEntriesCalled = new Ticker();
+ Ticker entryReporterCalled = new Ticker();
+ Ticker readAtCalled = new Ticker();
+ // Intentionally calling ticker 2x to ensure events do get chained
+ AppleSingleReader r = AppleSingleReader.builder(SAMPLE_FILE)
+ .versionReporter(v -> versionCalled.tick())
+ .versionReporter(v -> assertEquals(AppleSingle.VERSION_NUMBER2, v.intValue()))
+ .versionReporter(v -> versionCalled.tick())
+ .numberOfEntriesReporter(n -> numberOfEntriesCalled.tick())
+ .numberOfEntriesReporter(n -> assertEquals(1, n.intValue()))
+ .numberOfEntriesReporter(n -> numberOfEntriesCalled.tick())
+ .readAtReporter((o,b,d) -> readAtCalled.tick())
+ .readAtReporter((o,b,d) -> readAtCalled.tick())
+ .entryReporter(e -> entryReporterCalled.tick())
+ .entryReporter(e -> assertEquals("Hello, World!\n", new String(e.getData())))
+ .entryReporter(e -> assertEquals(e.getEntryId(), EntryType.DATA_FORK.entryId))
+ .entryReporter(e -> entryReporterCalled.tick())
+ .build();
+ // Executes on the reader
+ AppleSingle.asEntries(r);
+ // Validate
+ assertEquals(2, versionCalled.count());
+ assertEquals(2, numberOfEntriesCalled.count());
+ assertEquals(2, entryReporterCalled.count());
+ assertTrue(readAtCalled.count() >= 2);
+ }
+
+ /**
+ * AppleSingle file with a simple Data Fork and nothing else.
+ *
+ *
+ * $ echo "Hello, World!" | asu create --stdin-fork=data --filetype=txt --stdout | asu filter --include=1 --stdin --stdout | hexdump -C
+ * 00000000 00 05 16 00 00 02 00 00 00 00 00 00 00 00 00 00 |................|
+ * 00000010 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 |................|
+ * 00000020 00 26 00 00 00 0e 48 65 6c 6c 6f 2c 20 57 6f 72 |.&....Hello, Wor|
+ * 00000030 6c 64 21 0a |ld!.|
+ *
+ */
+ public static final byte[] SAMPLE_FILE = {
+ 0x00, 0x05, 0x16, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x26, 0x00, 0x00, 0x00, 0x0e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72,
+ 0x6c, 0x64, 0x21, 0x0a
+ };
+
+ public static class Ticker {
+ private int count;
+
+ public void tick() {
+ this.count++;
+ }
+ public int count() {
+ return this.count;
+ }
+ }
+}
diff --git a/tools/asu/README.md b/tools/asu/README.md
index 241d8c9..8069b84 100644
--- a/tools/asu/README.md
+++ b/tools/asu/README.md
@@ -4,13 +4,13 @@ For the included command-line utility, we are using `asu` for the name.
`as` is the GNU Assembler while `applesingle` is already on Macintoshes.
Hopefully that will prevent some confusion!
-Note that all runs are with the `asu` alias defined as `alias asu='java -jar build/libs/applesingle-1.0.0.jar'`
+Note that all runs are with the `asu` alias defined as `alias asu='java -jar build/libs/applesingle-1.2.0.jar'`
(adjust as necessary).
## Basic usage
```shell
-$ asu
+$ asu --help
Usage: asu [-hV] [--debug] [COMMAND]
AppleSingle utility
@@ -21,10 +21,12 @@ Options:
-V, --version Print version information and exit.
Commands:
- help Displays help information about the specified command
- info Display information about an AppleSingle file
+ analyze Perform an analysis on an AppleSingle file
create Create an AppleSingle file
extract Extract contents of an AppleSingle file
+ filter Filter an AppleSingle file
+ help Displays help information about the specified command
+ info Display information about an AppleSingle file
```
## Subcommand help
diff --git a/tools/asu/src/main/java/io/github/applecommander/applesingle/tools/asu/AnalyzeCommand.java b/tools/asu/src/main/java/io/github/applecommander/applesingle/tools/asu/AnalyzeCommand.java
index 5e6bebc..3770344 100644
--- a/tools/asu/src/main/java/io/github/applecommander/applesingle/tools/asu/AnalyzeCommand.java
+++ b/tools/asu/src/main/java/io/github/applecommander/applesingle/tools/asu/AnalyzeCommand.java
@@ -54,7 +54,7 @@ public class AnalyzeCommand implements Callable {
List used = new ArrayList<>();
HexDumper dumper = HexDumper.standard();
AppleSingleReader reader = AppleSingleReader.builder(fileData)
- .readAtReporter((start,b,d) -> used.add(IntRange.of(start, start + b.length)))
+ .readAtReporter((start,chunk,desc) -> used.add(IntRange.of(start, start + chunk.length)))
.readAtReporter((start,chunk,desc) -> dumper.dump(start, chunk, desc))
.versionReporter(this::reportVersion)
.numberOfEntriesReporter(this::reportNumberOfEntries)