Updated API and UML
API changes
As of AppleCommander 12, the API is starting to be redesigned. This page will capture what the "new world" looks like if you are a developer using the AppleCommander APIs.
Changes (so far):
ByteArrayImageLayout
was replaced withSource
.Source
discovery mechanism is now inSource.Factory
.Sources#create(Object)
is now used to createSource
objects. Sometimes with a specialSource
.Disk
was removed. After discovery was removed, it was just a placeholder and all functionality was merged intoFormattedDisk
.- Constants from
Disk
are now inDiskConstants
. FormattedDisk
discovery mechanism is now inDiskFactory
.Disks#inspect(Source)
is now used to initiate theFormattedDisk
discovery mechanism.
Accessing a disk image
With the removal of Disk
as the discovery and creation mechanism, there are a couple of replacement hooks to be aware of:
A FormattedDisk
originates from a Source
(new) and is created via the discovery mechanism supported by Disks
.
That is:
- Create a
Source
viaSources
. - Use
Disks
to "discover" a list ofFormattedDisk
objects.
Sources
A Source
, which replaces the layout, does some of the same stuff but supports discovery and complex types (such as 2IMG and DiskCopy).
Note that a source can wrap around another source, allowing a constant GZip support or data source (such as a FileEntry
on an image
or a Shrinkit archive).
Source source = Sources.create(filename).orElseThrow();
To create a source, pass in a String
, File
, Path
, or another Source
to the Sources
factory. The create(...)
method
returns an Optional<Source>
just in case -- mostly, you can ignore the optional part and just retrieve the object.
This is the interface for a Source
factory. Note that in the first pass, a Source
is identified via fromObject(Object)
and in the second pass is identified via fromSource(Source)
. The FileSource
is identified in the first round (handling GZip as well);
while the DiskCopyImage
and UniversalDiskImage
is identified via the second mechanism. Note that the FileEntry
source
comes in programmatically (as in via a double-click in the GUI) and DataBufferSource
is used for blank (new) images.
interface Source.Factory {
Optional<Source> fromObject(Object object);
Optional<Source> fromSource(Source source);
}
Disks
To identify disks, a DiskFactory
has been created that allows every type of disk to have its own dedicated discovery mechanism.
Prior to this, all that logic was mashed into the (old) Disk
class and partially handled in the constructor and then handled
in the getFormattedDisks
method itself. The intent of a DiskFactory
is to get that logic out of the Disk
and into a
dedicated filesystem (or other) discovery mechanism.
Harnessing the DiskFactory
is rather straight-forward. Use Disks#inspect(Source)
to initiate discovery. For example:
Source source = Sources.create(imageName).orElseThrow();
DiskFactory.Context ctx = Disks.inspect(source);
This will hand back a Context
. This contains all the information used to create FormattedDisk
s, including the Source
,
and any ImageOrder
s that were considered.
class Context {
public final Source source;
public final List<ImageOrder> orders = new ArrayList<>();
public final List<FormattedDisk> disks = new ArrayList<>();
}
Most of the time, disks
is what you'll need. If nothing is recognized, disks
will be empty, however orders
will
have the ImageOrder
s considered. Of course, Source
is what is handed into the Disks#inspect(Source)
method.
Other (semi-experimental)
Some of these are in play today (
DataBuffer
,HintProvider
, andInformation
) and others are partly implemented (CapabilityProvider
andContainer
).
DataBuffer
A lot of functionality was encompassed in AppleUtil
. As an experiment, DataBuffer
has been created to wrap around that
functionality (reading unsigned bytes, unsigned words, 32-bit integers, handling little-ending vs big-endian numbers, etc).
That DataBuffer
can "get" from a specified offset, or can "read" in a forward manner. Usage depends on what is needed.
CapabilityProvider
The concept of a capability is "what can this -thing- do?" This is still early and won't really show up until devices are put together. But you might as if this "thing" can "write" or "save to disk". Instead of having a bunch of dedicated "is" methods, this should help clean up the API.
public interface CapabilityProvider {
boolean can(Capability capability);
}
Expected usage:
FormattedDisk disk = ...;
if (disk.can(Capability.SAVE_TO_FILE)) {
window.enableSaveButton();
}
HintProvider
"Hints" are used to indicate somewthing we know. In particular, a Hint
is used while creating the disk image. One example
is if we know the sector ordering for a disk. We can provide that hint for certain sources (DiskCopy, 2IMG, and the current
FileEntry
sources do this).
public interface HintProvider {
boolean is(Hint hint);
default boolean isAny(Hint... hints);
}
Example usage:
if (source.is(Hint.PRODOS_BLOCK_ORDER)) {
// ...
}
Container
A "Container" allows pulling out internal components without providing a bunch of random "get" methods. For instance, the current
FormattedDisk
(and Disk
before that) as well as ImageOrder
all support both block and track/sector reading and writing.
So, a ProDOS disk has reading by block or sector. DOS hsa reading by block or sector, etc. RDOS has, not only block or sector,
but also "RDOS" block (256) bytes; and CP/M is the same except the CP/M block is 1024 bytes. It would be nice to hide all that
cruft and "ask" the (in this case) file system if it supported a particular type of device. That's where containers come into
play -- we expose one "get" method that then looks for that type of item. It returns a Java Optional
, so it may not and we
can handle that accordingly.
public interface Container {
<T> Optional<T> get(Class<T> iface);
}
Expected usage:
Optional<BlockDevice> blockOpt = disk.get(BlockDevice.class);
blockOpt.isPresent(device -> {
DataBuffer data = device.readBlock(0);
// ...
});
Information
A lot of things have extra information about them. DiskCopy has image type, disk name, and disk format (for instance) while
2IMG supports ordering, creator, and comments. These are available through a generic getInformation()
method call.
public record Information(String label, String value) {
}
Note that the Information
record includes a builder to aid in construction.
@Override
public List<Information> information() {
List<Information> list = source.information();
// ...
list.add(Information.builder("Image Type").value("Universal Disk Image (2IMG/2MG)"));
list.add(Information.builder("ProDOS Blocks").value(info.prodosBlocks()));
list.add(Information.builder("Data Offset & Length").value("$%08x & %08x",
info.dataOffset(), info.dataLength()));
// ...
}