Updating documentation and tests a bit.
This commit is contained in:
parent
4627e08f9f
commit
aae02d7323
|
@ -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>
|
||||
```
|
||||
|
||||
|
@ -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<Entry> entries = stdinFlag ? AppleSingle.asEntries(System.in) : AppleSingle.asEntries(inputFile);
|
||||
// ...
|
||||
AppleSingle.write(outputStream, newEntries);
|
||||
```
|
||||
|
|
|
@ -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<Entry> entries) throws IOException {
|
||||
final byte[] filler = new byte[16];
|
||||
ByteBuffer buf = ByteBuffer.allocate(26).order(ByteOrder.BIG_ENDIAN);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -54,7 +54,7 @@ public class AnalyzeCommand implements Callable<Void> {
|
|||
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)
|
||||
|
|
Loading…
Reference in New Issue