First draft of filter file command to apply a filter to an existing
file.
This commit is contained in:
parent
8ca3e27fc4
commit
85a3e2a12a
|
@ -42,7 +42,7 @@ import com.webcodepro.applecommander.storage.filters.TextFileFilter;
|
|||
import io.github.applecommander.filters.AppleSingleFileFilter;
|
||||
import io.github.applecommander.filters.RawFileFilter;
|
||||
|
||||
public enum ExportMethod {
|
||||
public enum FilterMethod {
|
||||
APPLESINGLE(AppleSingleFileFilter::new, "as", "applesingle"),
|
||||
APPLESOFT(ApplesoftFileFilter::new, "bas", "applesoft"),
|
||||
APPLEWORKS_DATABASE(AppleWorksDataBaseFileFilter::new, "adb"),
|
||||
|
@ -63,7 +63,7 @@ public enum ExportMethod {
|
|||
private Supplier<FileFilter> constructor;
|
||||
private List<String> codes;
|
||||
|
||||
private ExportMethod(Supplier<FileFilter> constructor, String... codes) {
|
||||
private FilterMethod(Supplier<FileFilter> constructor, String... codes) {
|
||||
this.constructor = constructor;
|
||||
this.codes = Arrays.asList(codes);
|
||||
}
|
|
@ -32,6 +32,7 @@ import io.github.applecommander.acx.command.DeleteCommand;
|
|||
import io.github.applecommander.acx.command.DiskMapCommand;
|
||||
import io.github.applecommander.acx.command.DumpCommand;
|
||||
import io.github.applecommander.acx.command.ExportCommand;
|
||||
import io.github.applecommander.acx.command.FilterCommand;
|
||||
import io.github.applecommander.acx.command.FindDuplicateFilesCommand;
|
||||
import io.github.applecommander.acx.command.ImportCommand;
|
||||
import io.github.applecommander.acx.command.InfoCommand;
|
||||
|
@ -66,6 +67,7 @@ import picocli.CommandLine.Option;
|
|||
DiskMapCommand.class,
|
||||
DumpCommand.class,
|
||||
ExportCommand.class,
|
||||
FilterCommand.class,
|
||||
FindDuplicateFilesCommand.class,
|
||||
HelpCommand.class,
|
||||
ImportCommand.class,
|
||||
|
|
|
@ -39,10 +39,10 @@ import com.webcodepro.applecommander.util.filestreamer.FileStreamer;
|
|||
import com.webcodepro.applecommander.util.filestreamer.FileTuple;
|
||||
import com.webcodepro.applecommander.util.filestreamer.TypeOfFile;
|
||||
|
||||
import io.github.applecommander.acx.ExportMethod;
|
||||
import io.github.applecommander.acx.FilterMethod;
|
||||
import io.github.applecommander.acx.base.ReadOnlyDiskImageCommandOptions;
|
||||
import io.github.applecommander.acx.converter.ExportMethodConverter;
|
||||
import io.github.applecommander.acx.converter.ExportMethodConverter.ExportMethodCandidates;
|
||||
import io.github.applecommander.acx.converter.FilterMethodConverter;
|
||||
import io.github.applecommander.acx.converter.FilterMethodConverter.FilterMethodCandidates;
|
||||
import picocli.CommandLine.ArgGroup;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
|
@ -144,21 +144,21 @@ public class ExportCommand extends ReadOnlyDiskImageCommandOptions {
|
|||
private static class FileExtractMethods {
|
||||
private Function<FileEntry,FileFilter> extractFunction = this::asSuggestedFile;
|
||||
|
||||
@Option(names = { "--method" }, converter = ExportMethodConverter.class,
|
||||
completionCandidates = ExportMethodCandidates.class,
|
||||
@Option(names = { "--method" }, converter = FilterMethodConverter.class,
|
||||
completionCandidates = FilterMethodCandidates.class,
|
||||
description = "Select a specific export method type (${COMPLETION-CANDIDATES}).")
|
||||
public void selectExportMethod(final ExportMethod exportMethod) {
|
||||
this.extractFunction = fileFilter -> exportMethod.create();
|
||||
public void selectFilterMethod(final FilterMethod filterMethod) {
|
||||
this.extractFunction = fileFilter -> filterMethod.create();
|
||||
}
|
||||
|
||||
// Short-cuts to some of the more common, non-suggested, filters
|
||||
@Option(names = { "--raw", "--binary" }, description = "Extract file in native format.")
|
||||
public void setBinaryExtraction(boolean flag) {
|
||||
selectExportMethod(ExportMethod.BINARY);
|
||||
selectFilterMethod(FilterMethod.BINARY);
|
||||
}
|
||||
@Option(names = { "--hex", "--dump" }, description = "Extract file in hex dump format.")
|
||||
public void setHexDumpExtraction(boolean flag) {
|
||||
selectExportMethod(ExportMethod.HEX_DUMP);
|
||||
selectFilterMethod(FilterMethod.HEX_DUMP);
|
||||
}
|
||||
@Option(names = { "--suggested" }, description = "Extract file as suggested by AppleCommander (default)")
|
||||
public void setSuggestedExtraction(boolean flag) {
|
||||
|
@ -166,11 +166,11 @@ public class ExportCommand extends ReadOnlyDiskImageCommandOptions {
|
|||
}
|
||||
@Option(names = { "--as", "--applesingle" }, description = "Extract file to AppleSingle file.")
|
||||
public void setAppleSingleExtraction(boolean flag) {
|
||||
selectExportMethod(ExportMethod.APPLESINGLE);
|
||||
selectFilterMethod(FilterMethod.APPLESINGLE);
|
||||
}
|
||||
@Option(names = { "--disassembly" }, description = "Dissassembly file.")
|
||||
public void setDisassemblyExtraction(boolean flag) {
|
||||
selectExportMethod(ExportMethod.DISASSEMBLY);
|
||||
selectFilterMethod(FilterMethod.DISASSEMBLY);
|
||||
}
|
||||
|
||||
public FileFilter asSuggestedFile(FileEntry entry) {
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2019-2022 by Robert Greene and others
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package io.github.applecommander.acx.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.webcodepro.applecommander.storage.DiskFullException;
|
||||
import com.webcodepro.applecommander.storage.FileEntry;
|
||||
import com.webcodepro.applecommander.storage.FileFilter;
|
||||
import com.webcodepro.applecommander.storage.FormattedDisk;
|
||||
import com.webcodepro.applecommander.util.readerwriter.FileEntryReader;
|
||||
import com.webcodepro.applecommander.util.readerwriter.OverrideFileEntryReader;
|
||||
|
||||
import io.github.applecommander.acx.FilterMethod;
|
||||
import io.github.applecommander.acx.base.ReusableCommandOptions;
|
||||
import io.github.applecommander.acx.converter.FilterMethodConverter;
|
||||
import io.github.applecommander.acx.converter.FilterMethodConverter.FilterMethodCandidates;
|
||||
import picocli.CommandLine.ArgGroup;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
import picocli.CommandLine.Spec;
|
||||
|
||||
@Command(name = "filter", description = "Filter on-disk file (that are not in a disk image).")
|
||||
public class FilterCommand extends ReusableCommandOptions {
|
||||
@Spec
|
||||
private CommandSpec spec;
|
||||
|
||||
@ArgGroup(exclusive = true, heading = "%nInput source:%n")
|
||||
private InputData inputData = new InputData();
|
||||
|
||||
@ArgGroup(exclusive = true, heading = "%nFile filter methods:%n")
|
||||
private FileFilterMethods extraction = new FileFilterMethods();
|
||||
|
||||
@ArgGroup(exclusive = true, heading = "%nOutput destination:%n")
|
||||
private OutputData outputData = new OutputData();
|
||||
|
||||
@Override
|
||||
public int handleCommand() throws Exception {
|
||||
FileEntry fileEntry = inputData.asFileEntry();
|
||||
byte[] data = extraction.filter(fileEntry);
|
||||
outputData.write(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static class InputData {
|
||||
private Supplier<FileEntryReader> fileEntryReaderSupplier = this::fromStdin;
|
||||
|
||||
public FileEntry asFileEntry() {
|
||||
return new FileEntryMimic(fileEntryReaderSupplier.get());
|
||||
}
|
||||
|
||||
@Option(names = { "--stdin" }, description = "Read from standard input (default).")
|
||||
public void stdinFlag(boolean flag) {
|
||||
fileEntryReaderSupplier = this::fromStdin;
|
||||
}
|
||||
private FileEntryReader fromStdin() {
|
||||
try {
|
||||
byte[] data = System.in.readAllBytes();
|
||||
return OverrideFileEntryReader.builder()
|
||||
.fileData(data)
|
||||
.build();
|
||||
} catch (IOException cause) {
|
||||
throw new UncheckedIOException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Option(names = { "--in", "--input" }, description = "File to read. (Required if specifying two file names.)")
|
||||
public void inputFileFlag(final String filename) {
|
||||
fromFileParameter(filename);
|
||||
}
|
||||
|
||||
@Parameters(description = "File to read.")
|
||||
public void fromFileParameter(final String filename) {
|
||||
fileEntryReaderSupplier = () -> {
|
||||
try {
|
||||
Path path = Path.of(filename);
|
||||
byte[] data = Files.readAllBytes(path);
|
||||
return OverrideFileEntryReader.builder()
|
||||
.fileData(data)
|
||||
.filename(filename)
|
||||
.build();
|
||||
} catch (IOException cause) {
|
||||
throw new UncheckedIOException(cause);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileFilterMethods {
|
||||
private FilterMethod filterMethod = FilterMethod.TEXT;
|
||||
public byte[] filter(FileEntry fileEntry) {
|
||||
return filterMethod.create().filter(fileEntry);
|
||||
}
|
||||
|
||||
@Option(names = { "--method" }, converter = FilterMethodConverter.class,
|
||||
completionCandidates = FilterMethodCandidates.class,
|
||||
description = "Select a specific export method type (${COMPLETION-CANDIDATES}).")
|
||||
public void selectFilterMethod(final FilterMethod filterMethod) {
|
||||
this.filterMethod = filterMethod;
|
||||
}
|
||||
|
||||
// Short-cuts to some of the more common, non-suggested, filters
|
||||
@Option(names = { "--text" }, description = "Treat file as Apple II text file (default).")
|
||||
public void setTextFilter(boolean flag) {
|
||||
selectFilterMethod(FilterMethod.TEXT);
|
||||
}
|
||||
@Option(names = { "--disassembly" }, description = "Disassemble input file.")
|
||||
public void setDisassemblyFilter(boolean flag) {
|
||||
selectFilterMethod(FilterMethod.DISASSEMBLY);
|
||||
}
|
||||
@Option(names = { "--applesoft" }, description = "De-tokenize Applesoft input file.")
|
||||
public void setApplesoftFilter(boolean flag) {
|
||||
selectFilterMethod(FilterMethod.DISASSEMBLY);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OutputData {
|
||||
private Consumer<byte[]> outputDataConsumer = this::writeToStdout;
|
||||
public void write(byte[] data) {
|
||||
outputDataConsumer.accept(data);
|
||||
}
|
||||
|
||||
@Option(names = { "--stdout" }, description = "Write to standard output (default).")
|
||||
public void stdoutFlag(boolean flag) {
|
||||
outputDataConsumer = this::writeToStdout;
|
||||
}
|
||||
private void writeToStdout(byte[] data) {
|
||||
try {
|
||||
System.out.write(data);
|
||||
} catch (IOException cause) {
|
||||
throw new UncheckedIOException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Option(names = { "--out", "--output" }, description = "File to write. (Required if using stdin but writing to file.)")
|
||||
public void outputFile(final String filename) {
|
||||
toFile(filename);
|
||||
}
|
||||
|
||||
@Parameters(description = "File to write.")
|
||||
public void toFile(final String filename) {
|
||||
outputDataConsumer = (data) -> {
|
||||
try {
|
||||
Files.write(Path.of(filename), data);
|
||||
} catch (IOException cause) {
|
||||
throw new UncheckedIOException(cause);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileEntryMimic implements FileEntry {
|
||||
private FileEntryReader fileEntryReader;
|
||||
|
||||
public FileEntryMimic(FileEntryReader fileEntryReader) {
|
||||
Objects.requireNonNull(fileEntryReader);
|
||||
this.fileEntryReader = fileEntryReader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return fileEntryReader.getFilename().orElse("UNKNOWN");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilename(String filename) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFiletype() {
|
||||
return fileEntryReader.getProdosFiletype().orElse("BIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiletype(String filetype) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
return fileEntryReader.isLocked().orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocked(boolean lock) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return getFileData().length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFileColumnData(int displayMode) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getFileData() {
|
||||
return fileEntryReader.getFileData().orElse(new byte[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFileData(byte[] data) throws DiskFullException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileFilter getSuggestedFilter() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormattedDisk getFormattedDisk() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumFilenameLength() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsAddress() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddress(int address) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAddress() {
|
||||
return fileEntryReader.getBinaryAddress().orElse(0x800);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCompile() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,33 +24,33 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.github.applecommander.acx.ExportMethod;
|
||||
import io.github.applecommander.acx.FilterMethod;
|
||||
import picocli.CommandLine.ITypeConverter;
|
||||
import picocli.CommandLine.TypeConversionException;
|
||||
|
||||
public class ExportMethodConverter implements ITypeConverter<ExportMethod> {
|
||||
public static final Map<String,ExportMethod> EXPORTS = new HashMap<>();
|
||||
public class FilterMethodConverter implements ITypeConverter<FilterMethod> {
|
||||
public static final Map<String,FilterMethod> FILTERS = new HashMap<>();
|
||||
static {
|
||||
for (ExportMethod x : ExportMethod.values()) {
|
||||
for (FilterMethod x : FilterMethod.values()) {
|
||||
for (String code : x.getCodes()) {
|
||||
EXPORTS.put(code, x);
|
||||
FILTERS.put(code, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportMethod convert(String value) throws Exception {
|
||||
if (EXPORTS.containsKey(value)) {
|
||||
return EXPORTS.get(value);
|
||||
public FilterMethod convert(String value) throws Exception {
|
||||
if (FILTERS.containsKey(value)) {
|
||||
return FILTERS.get(value);
|
||||
}
|
||||
throw new TypeConversionException(String.format("Export method not found: %s", value));
|
||||
}
|
||||
|
||||
public static class ExportMethodCandidates extends ArrayList<String> {
|
||||
public static class FilterMethodCandidates extends ArrayList<String> {
|
||||
private static final long serialVersionUID = -744232190636905235L;
|
||||
|
||||
ExportMethodCandidates() {
|
||||
super(EXPORTS.keySet());
|
||||
FilterMethodCandidates() {
|
||||
super(FILTERS.keySet());
|
||||
Collections.sort(this);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue