Updates sources to support 'can(Capability.SAVE_SOURCE)' as appropriate and added/implemented 'save()'.

This commit is contained in:
a2geek committed 2026-10-05 13:40:24 -05:00
1 parent 18c16a856c
commit f6d083d6bf
6 files changed
+117 -38

No files matched your search

@@ -26,6 +26,7 @@ import org.applecommander.util.Container;
import org.applecommander.util.DataBuffer;
import org.applecommander.util.InformationGroup;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -45,7 +46,9 @@ public class FileEntrySource implements Source {
@Override
public boolean can(Capability capability) {
// TODO?
if (fileEntry.getFormattedDisk().canWriteFileData()) {
return capability == Capability.SAVE_SOURCE;
}
return false;
}
@@ -69,6 +72,16 @@ public class FileEntrySource implements Source {
return fileEntry.getFilename();
}
@Override
public void save() throws IOException {
try {
fileEntry.setFileData(buffer.asBytes());
changed = false;
} catch (DiskException ex) {
throw new IOException(ex);
}
}
@Override
public DataBuffer readAllBytes() {
return buffer;
@@ -37,6 +37,7 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
public class ShrinkitFileStore implements FileStore {
@@ -45,6 +46,7 @@ public class ShrinkitFileStore implements FileStore {
private final List<FileEntry> files = new ArrayList<>();
public ShrinkitFileStore(Source source) {
Objects.requireNonNull(source);
this.source = source;
try {
this.archive = new NuFileArchive(new ByteArrayInputStream(source.readAllBytes().asBytes()));
@@ -26,6 +26,7 @@ import org.applecommander.util.Container;
import org.applecommander.util.DataBuffer;
import org.applecommander.util.InformationGroup;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -82,8 +83,7 @@ public class DiskCopyImage implements Source {
@Override
public boolean can(Capability capability) {
// TODO - not definted yet
return false;
return source.can(capability);
}
@Override
@@ -135,6 +135,11 @@ public class DiskCopyImage implements Source {
source.clearChanges();
}
@Override
public void save() throws IOException {
source.save();
}
@Override
public List<InformationGroup> information() {
BiFunction<Integer,Integer,String> cksumFn = (a, e) -> {
@@ -26,6 +26,7 @@ import org.applecommander.util.Container;
import org.applecommander.util.DataBuffer;
import org.applecommander.util.InformationGroup;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -87,8 +88,7 @@ public class UniversalDiskImage implements Source {
@Override
public boolean can(Capability capability) {
// TODO we haven't defined any yet
return false;
return source.can(capability);
}
@Override
@@ -136,6 +136,11 @@ public class UniversalDiskImage implements Source {
source.clearChanges();
}
@Override
public void save() throws IOException {
source.save();
}
@Override
public List<InformationGroup> information() {
Info info = getInfo();
@@ -28,6 +28,7 @@ import org.applecommander.util.InformationGroup;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
@@ -67,7 +68,9 @@ public class FileSource implements Source {
@Override
public boolean can(Capability capability) {
// TODO - we haven't actually identified save yet!
if (path.toFile().canWrite()) {
return capability == Capability.SAVE_SOURCE;
}
return false;
}
@@ -118,6 +121,12 @@ public class FileSource implements Source {
changed = false;
}
@Override
public void save() throws IOException {
Files.write(path, buffer.asBytes(), StandardOpenOption.TRUNCATE_EXISTING);
changed = false;
}
@Override
public List<InformationGroup> information() {
InformationGroup.Builder builder = InformationGroup.builder("File Source")
@@ -25,53 +25,100 @@ import org.applecommander.util.Container;
import org.applecommander.util.DataBuffer;
import org.applecommander.util.InformationProvider;
import java.io.IOException;
import java.util.Optional;
/**
* Source and Factory for an archive or disk in AppleCommander.
* <p/>
* Typical usage:
* {@snippet lang = java:
* import com.webcodepro.applecommander.storage.Sources;Optional<Source> sourceOpt = Sources.create(Path.of(filename));
* if (sourceOpt.isPresent()) {
* // do something with sourceOpt.get()
* System.out.println(sourceOpt.get().getSize());
* }
* // Only happens if the initiating object is not understood
* throw new RuntimeException("Unable to create image source");
*}
*/
/// Source and Factory for an archive or disk in AppleCommander.
///
/// Typical usage:
///
/// ```java
/// import com.webcodepro.applecommander.storage.Sources;
/// import java.util.Optional;
/// Optional<source> sourceOpt = Sources.create(Path.of(filename));
/// if (sourceOpt.isPresent()) {
/// // do something with the Source
/// Source source = sourceOpt.get();
/// System.out.println(source.getSize());
/// }
/// else {
/// // Only happens if the initiating object is not understood
/// throw new RuntimeException("Unable to create image source");
/// }
/// ```
///
/// Optionally, you can use the Optional's more _fluent_ API and write
/// code like this:
///
/// ```java
/// import com.webcodepro.applecommander.storage.Sources;
/// Sources.create(Path.of(filename)).ifPresent(source -> {
/// // do something with the Source
/// System.out.println(source.getSize());
/// });
/// ```
public interface Source extends CapabilityProvider, HintProvider, Container, InformationProvider {
/// Indicates the physical size of this image on disk.
int getSize();
/// The name of the source. It likely will be used in user interfaces. It could represent a file
/// from the computer or even a file from a file store.
String getName();
/// Read all bytes from this source.
DataBuffer readAllBytes();
/// Read a range of bytes from this source.
DataBuffer readBytes(int offset, int length);
/// Write bytes to this source. Updates the changed status.
void writeBytes(int offset, DataBuffer data);
/// Indicates if this source has changed.
boolean hasChanged();
/// Clears the changed indicator.
void clearChanges();
/**
* Indicates if the source image is approximately equal to this size
* (less any image over-read).
* Currently, hardcoded to allow up to 255 extra bytes at the end of a
* disk image. Must be at least the requested size!
*/
/// Save the image back to the origin. Note that this could conceivably save back to the
/// OS file system, or back to a file store. Use the `CapabilityProvider#can` to see if
/// a save is possible. The save operation should clear the change indicator.
///
/// For example:
///
/// ```java
/// if (source.can(Capability.SAVE_SOURCE)) {
/// source.save();
/// }
/// ```
///
/// Note that the capability can be used to enable appropriate options. If source _cannot_
/// be saved, a "Save As" type operation can be applied and the source switched for the
/// `FileStore` in question.
///
/// @throws IOException if an error occurs while saving the source.
/// @throws UnsupportedOperationException if the source does not support the save operation.
default void save() throws IOException {
throw new UnsupportedOperationException("save operation not supported");
}
/// Indicates if the source image is approximately equal to this size
/// (less any image over-read).
/// Currently, hardcoded to allow up to 255 extra bytes at the end of a
/// disk image. Must be at least the requested size!
default boolean isApproxEQ(int value) {
return getSize() >= value && getSize() <= value + 255;
}
/**
* Indicates if the source image is "approximately" between
* these two values (less any image over-read).
* Currently, hardcoded to allow up to 255 extra bytes at the end of a
* disk image. Must be at least the requested size!
*/
/// Indicates if the source image is "approximately" between
/// these two values (less any image over-read).
/// Currently, hardcoded to allow up to 255 extra bytes at the end of a
/// disk image. Must be at least the requested size!
default boolean isApproxBetween(int value1, int value2) {
return getSize() >= value1 && getSize() <= value2 + 255;
}
/**
* Test if this name has a given file extension.
*/
/// Test if this name has a given file extension.
default boolean extensionLike(String extension) {
String name = getName().toLowerCase();
String ext1 = String.format(".%s", extension.toLowerCase());
@@ -79,9 +126,7 @@ public interface Source extends CapabilityProvider, HintProvider, Container, Inf
return name.endsWith(ext1) || name.endsWith(ext2);
}
/**
* This is the Source creation factory interface.
*/
/// This is the Source creation factory interface.
interface Factory {
Optional<Source> fromObject(Object object);
Optional<Source> fromSource(Source source);