Skip to content

Commit

Permalink
Updating documentation and tests a bit.
Browse files Browse the repository at this point in the history
  • Loading branch information
a2geek committed Jun 3, 2018
1 parent 4627e08 commit aae02d7
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 8 deletions.
32 changes: 30 additions & 2 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ To include in a Maven project:
<dependency>
<groupId>net.sf.applecommander</groupId>
<artifactId>applesingle-api</artifactId>
<version>1.0.0</version>
<version>1.2.0</version>
</dependency>
```

Expand All @@ -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"
// ...
}
```
Expand Down Expand Up @@ -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<Entry> entries = stdinFlag ? AppleSingle.asEntries(System.in) : AppleSingle.asEntries(inputFile);
// ...
AppleSingle.write(outputStream, newEntries);
```
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ public void save(Path path) throws IOException {
}
}

/**
* 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<Entry> entries) throws IOException {
final byte[] filler = new byte[16];
ByteBuffer buf = ByteBuffer.allocate(26).order(ByteOrder.BIG_ENDIAN);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import java.nio.ByteOrder;
import java.util.Objects;

/**
* Represents an AppleSingle entry.
*/
public class Entry {
public static final int BYTES = 12;
private int entryId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <br/>
* <code>
* $ 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!.|
* </code>
*/
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;
}
}
}
10 changes: 6 additions & 4 deletions tools/asu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Void call() throws IOException {
List<IntRange> 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)
Expand Down

0 comments on commit aae02d7

Please sign in to comment.