mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2026-10-24 23:02:27 +00:00
Adding DisplayColumn and updating tests and the rest of FileStore to use or support it.
This commit is contained in:
1 parent
7f29095acd
commit
cffedf2a67
6 files changed
+168
-50
No files matched your search
+63
-27
@@ -22,13 +22,14 @@ package com.webcodepro.applecommander.storage;
|
||||
import com.webcodepro.applecommander.storage.os.prodos.ProdosDirectoryEntry;
|
||||
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
|
||||
import org.applecommander.capability.Capability;
|
||||
import org.applecommander.filestore.DirectoryEntry;
|
||||
import org.applecommander.filestore.Entry;
|
||||
import org.applecommander.filestore.Directory;
|
||||
import org.applecommander.filestore.DisplayColumn;
|
||||
import org.applecommander.filestore.FileEntry;
|
||||
import org.applecommander.filestore.FileStore;
|
||||
import org.applecommander.util.Container;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The DiskFileStoreAdapter is a shim that allows FormattedDisk to be mapped into the
|
||||
@@ -52,8 +53,8 @@ public class DiskFileStoreAdapter implements FileStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryEntry getRootDirectory() {
|
||||
return new DiskDirectoryEntryAdapter(this, null, disk);
|
||||
public DiskDirectoryAdapter getRootDirectory() {
|
||||
return new DiskDirectoryAdapter(this, null, disk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,46 +74,73 @@ public class DiskFileStoreAdapter implements FileStore {
|
||||
};
|
||||
}
|
||||
|
||||
public abstract static class DiskEntryAdapter implements Entry {
|
||||
protected final DiskFileStoreAdapter adapter;
|
||||
protected final DirectoryEntry parent;
|
||||
|
||||
DiskEntryAdapter(DiskFileStoreAdapter adapter, DirectoryEntry parent) {
|
||||
this.adapter = adapter;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public DirectoryEntry getParent() {
|
||||
return parent;
|
||||
}
|
||||
public FileStore getFileStore() {
|
||||
return adapter;
|
||||
/**
|
||||
* Translate the "legacy" FormattedDisk FileColumnHeader to the new DisplayColumn structure.
|
||||
* Note that we need to track what has been seen as well as where it comes from and the index
|
||||
* values in order to replicate most of the capability. This does not solve for type since the
|
||||
* old API pre-formats everything as a String.
|
||||
*/
|
||||
@Override
|
||||
public List<DisplayColumn> getDisplayColumns() {
|
||||
List<DisplayColumn> columns = new ArrayList<>();
|
||||
Set<String> alreadySeenKeys = new HashSet<>();
|
||||
for (int displayMode : List.of(FormattedDisk.FILE_DISPLAY_NATIVE,
|
||||
FormattedDisk.FILE_DISPLAY_DETAIL)) {
|
||||
List<FormattedDisk.FileColumnHeader> headers = disk.getFileColumnHeaders(displayMode);
|
||||
DisplayColumn.Mode mode = DisplayColumn.Mode.DETAIL;
|
||||
if (displayMode == FormattedDisk.FILE_DISPLAY_NATIVE) {
|
||||
mode = DisplayColumn.Mode.NATIVE;
|
||||
};
|
||||
for (int i = 0; i<headers.size(); i++) {
|
||||
var header = headers.get(i);
|
||||
DisplayColumn.Alignment alignment = switch(header.getAlignment()) {
|
||||
case FormattedDisk.FileColumnHeader.ALIGN_CENTER -> DisplayColumn.Alignment.CENTER;
|
||||
case FormattedDisk.FileColumnHeader.ALIGN_RIGHT -> DisplayColumn.Alignment.RIGHT;
|
||||
default -> DisplayColumn.Alignment.LEFT;
|
||||
};
|
||||
if (!alreadySeenKeys.contains(header.getKey())) {
|
||||
alreadySeenKeys.add(header.getKey());
|
||||
final int headerIndex = i;
|
||||
Function<FileEntry,String> mappingFn = entry -> {
|
||||
// TODO investigate to see if generics work across these interfaces
|
||||
if (entry instanceof DiskFileEntryAdapter fileEntryAdapter) {
|
||||
return fileEntryAdapter.fileEntry.getFileColumnData(displayMode).get(headerIndex);
|
||||
}
|
||||
throw new RuntimeException("Unexpected file entry type: " + entry.getClass().getName());
|
||||
};
|
||||
DisplayColumn displayColumn = new DisplayColumn(header.getTitle(),
|
||||
alignment, mappingFn::apply, "%s", mode);
|
||||
columns.add(displayColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The DiskFileEntryAdapter is a shim that allows a FileEntry to be mapped into the
|
||||
* new/evolving FileEntry interface(s).
|
||||
*/
|
||||
public static class DiskFileEntryAdapter implements FileEntry {
|
||||
private final DiskFileStoreAdapter adapter;
|
||||
private final DirectoryEntry parent;
|
||||
private final DiskDirectoryAdapter parent;
|
||||
private final com.webcodepro.applecommander.storage.FileEntry fileEntry;
|
||||
private final DirectoryEntry subdirectory;
|
||||
private final DiskDirectoryAdapter subdirectory;
|
||||
|
||||
public DiskFileEntryAdapter(DiskFileStoreAdapter adapter, DirectoryEntry parent,
|
||||
public DiskFileEntryAdapter(DiskFileStoreAdapter adapter, DiskDirectoryAdapter parent,
|
||||
com.webcodepro.applecommander.storage.FileEntry fileEntry) {
|
||||
this.adapter = adapter;
|
||||
this.parent = parent;
|
||||
this.fileEntry = fileEntry;
|
||||
if (fileEntry instanceof ProdosDirectoryEntry prodosDirectoryEntry) {
|
||||
this.subdirectory = new DiskDirectoryEntryAdapter(adapter, parent, prodosDirectoryEntry);
|
||||
this.subdirectory = new DiskDirectoryAdapter(adapter, parent, prodosDirectoryEntry);
|
||||
}
|
||||
else {
|
||||
this.subdirectory = null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public DirectoryEntry getParent() {
|
||||
public DiskDirectoryAdapter getParent() {
|
||||
return parent;
|
||||
}
|
||||
@Override
|
||||
@@ -168,18 +196,22 @@ public class DiskFileStoreAdapter implements FileStore {
|
||||
* The DiskDirectoryEntryAdapter is a shim that allows a FormattedDisk or DirectoryEntry to be mapped into the
|
||||
* new/evolving DirectoryEntry interface(s).
|
||||
*/
|
||||
public static class DiskDirectoryEntryAdapter implements DirectoryEntry {
|
||||
public static class DiskDirectoryAdapter implements Directory {
|
||||
private final DiskFileStoreAdapter adapter;
|
||||
private final DirectoryEntry parent;
|
||||
private final DiskDirectoryAdapter parent;
|
||||
private final com.webcodepro.applecommander.storage.DirectoryEntry directoryEntry;
|
||||
|
||||
public DiskDirectoryEntryAdapter(DiskFileStoreAdapter adapter, DirectoryEntry parent,
|
||||
com.webcodepro.applecommander.storage.DirectoryEntry directoryEntry) {
|
||||
public DiskDirectoryAdapter(DiskFileStoreAdapter adapter, DiskDirectoryAdapter parent,
|
||||
com.webcodepro.applecommander.storage.DirectoryEntry directoryEntry) {
|
||||
this.adapter = adapter;
|
||||
this.parent = parent;
|
||||
this.directoryEntry = directoryEntry;
|
||||
}
|
||||
@Override
|
||||
public Optional<Directory> getParent() {
|
||||
return Optional.ofNullable(parent);
|
||||
}
|
||||
@Override
|
||||
public List<FileEntry> getFiles() {
|
||||
try {
|
||||
List<FileEntry> entries = new ArrayList<>();
|
||||
@@ -191,6 +223,10 @@ public class DiskFileStoreAdapter implements FileStore {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public DiskFileStoreAdapter getFileStore() {
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-7
@@ -28,7 +28,15 @@ import java.util.stream.Collectors;
|
||||
* FileStores that don't naturally support a directory do contain a root directory,
|
||||
* so there will be a directory associated to each FileStore.
|
||||
*/
|
||||
public interface DirectoryEntry {
|
||||
public interface Directory {
|
||||
/**
|
||||
* Get the parent directory. If this is the root directory, it returns empty.
|
||||
*/
|
||||
Optional<Directory> getParent();
|
||||
/**
|
||||
* Returns the FileStore that this Directory belongs to.
|
||||
*/
|
||||
FileStore getFileStore();
|
||||
/**
|
||||
* Return all entries that are stored in this directory.
|
||||
*/
|
||||
@@ -36,10 +44,11 @@ public interface DirectoryEntry {
|
||||
/**
|
||||
* Filter out the <code>DirectoryEntry</code> entries in this directory.
|
||||
*/
|
||||
default List<DirectoryEntry> getDirectories() {
|
||||
default List<Directory> getDirectories() {
|
||||
return getFiles().stream()
|
||||
.filter(fileEntry -> fileEntry instanceof DirectoryEntry)
|
||||
.map(fileEntry -> (DirectoryEntry)fileEntry)
|
||||
.map(fileEntry -> fileEntry.get(Directory.class))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
/**
|
||||
@@ -50,9 +59,9 @@ public interface DirectoryEntry {
|
||||
if (name.equalsIgnoreCase(fileEntry.getName())) {
|
||||
return Optional.of(fileEntry);
|
||||
}
|
||||
Optional<DirectoryEntry> directoryEntry = fileEntry.get(DirectoryEntry.class);
|
||||
if (directoryEntry.isPresent()) {
|
||||
Optional<FileEntry> finding = directoryEntry.get().findFile(name);
|
||||
Optional<Directory> directory = fileEntry.get(Directory.class);
|
||||
if (directory.isPresent()) {
|
||||
Optional<FileEntry> finding = directory.get().findFile(name);
|
||||
if (finding.isPresent()) {
|
||||
return finding;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2026 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 org.applecommander.filestore;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A DisplayColumn supports formatted output from various FileStores in a
|
||||
* dynamic fashion. The intent is to provide a flexible display interface
|
||||
* that does not rely on hard-coded implementations.
|
||||
*/
|
||||
public record DisplayColumn(String headerText, Alignment alignment, Function<FileEntry,Object> valueFn, String fmt, Mode... modes) {
|
||||
/**
|
||||
* Indicates if this DisplayColumn supports the mode. In this manner, the columns
|
||||
* can be filtered out. Or it can be ignored to display everything.
|
||||
*/
|
||||
public boolean supports(Mode mode) {
|
||||
return Set.of(modes).contains(mode);
|
||||
}
|
||||
/**
|
||||
* Helper method to convert the column into a formatted string for display.
|
||||
*/
|
||||
public String formatAsText(FileEntry fileEntry) {
|
||||
return String.format(fmt, valueFn.apply(fileEntry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the display mode to help filter columns to users' preference.
|
||||
*/
|
||||
public enum Mode {
|
||||
NATIVE,
|
||||
DETAIL
|
||||
}
|
||||
/**
|
||||
* Indicates how this column should be formatted.
|
||||
*/
|
||||
public enum Alignment {
|
||||
LEFT,
|
||||
CENTER,
|
||||
RIGHT
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public interface FileEntry extends Container {
|
||||
/**
|
||||
* The parent <code>DirectoryEntry</code>, if applicable. Can return <code>null</code>.
|
||||
*/
|
||||
DirectoryEntry getParent();
|
||||
Directory getParent();
|
||||
/**
|
||||
* Indicates if this <code>Entry</code> is deleted.
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,8 @@ package org.applecommander.filestore;
|
||||
import org.applecommander.capability.CapabilityProvider;
|
||||
import org.applecommander.util.Container;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A FileStore is a generic interface for all archive and filesystem objects.
|
||||
*/
|
||||
@@ -33,8 +35,10 @@ public interface FileStore extends CapabilityProvider, Container {
|
||||
*/
|
||||
String getLabel();
|
||||
/** All FileStores support a "root" directory that references all files. */
|
||||
DirectoryEntry getRootDirectory();
|
||||
Directory getRootDirectory();
|
||||
/** This is the path separator character. */
|
||||
// TODO there has to be a better / dynamic way to do this (applies to ProDOS, ShrinkIt only?)
|
||||
String getPathSeparator();
|
||||
/** Yields a list of columns to be used in this display mode. */
|
||||
List<DisplayColumn> getDisplayColumns();
|
||||
}
|
||||
+23
-14
@@ -21,7 +21,6 @@ package com.webcodepro.applecommander.storage;
|
||||
|
||||
import org.applecommander.capability.Capability;
|
||||
import org.applecommander.filestore.*;
|
||||
import org.applecommander.filestore.DirectoryEntry;
|
||||
import org.applecommander.filestore.FileEntry;
|
||||
import org.applecommander.source.Source;
|
||||
import org.applecommander.source.Sources;
|
||||
@@ -248,20 +247,30 @@ public class DiskFileStoreTest {
|
||||
return ctx.fileStores;
|
||||
}
|
||||
|
||||
protected void showFiles(DirectoryEntry parent, String indent) {
|
||||
protected void showFiles(Directory parent, String indent) {
|
||||
FileStore fileStore = parent.getFileStore();
|
||||
List<DisplayColumn> displayColumns = fileStore.getDisplayColumns();
|
||||
if (parent.getParent().isEmpty()) {
|
||||
// Print columns only at the top
|
||||
System.out.print(indent);
|
||||
displayColumns.forEach(column -> {
|
||||
System.out.print(column.headerText());
|
||||
System.out.print(" ");
|
||||
});
|
||||
System.out.println();
|
||||
}
|
||||
for (FileEntry file : parent.getFiles()) {
|
||||
if (!file.isDeleted()) {
|
||||
//List<String> data = entry.getFileColumnData(FormattedDisk.FILE_DISPLAY_NATIVE);
|
||||
//System.out.print(indent);
|
||||
//for (int d=0; d<data.size(); d++) {
|
||||
// System.out.print(data.get(d));
|
||||
// System.out.print(" ");
|
||||
//}
|
||||
//System.out.println();
|
||||
System.out.printf("%s%s %s %d\n", indent, file.getName(), file.getFiletype(), file.getSize());
|
||||
System.out.print(indent);
|
||||
displayColumns.forEach(column -> {
|
||||
System.out.print(column.formatAsText(file));
|
||||
System.out.print(" ");
|
||||
});
|
||||
System.out.println();
|
||||
|
||||
Optional<Directory> subdirectory = file.get(Directory.class);
|
||||
subdirectory.ifPresent(directoryEntry -> showFiles(directoryEntry, indent + " "));
|
||||
}
|
||||
Optional<DirectoryEntry> subdirectory = file.get(DirectoryEntry.class);
|
||||
subdirectory.ifPresent(directoryEntry -> showFiles(directoryEntry, indent + " "));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,9 +322,9 @@ public class DiskFileStoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertCanReadFiles(DirectoryEntry parent) throws DiskException {
|
||||
protected void assertCanReadFiles(Directory parent) throws DiskException {
|
||||
for (FileEntry fileEntry : parent.getFiles()) {
|
||||
Optional<DirectoryEntry> subdirectory = fileEntry.get(DirectoryEntry.class);
|
||||
Optional<Directory> subdirectory = fileEntry.get(Directory.class);
|
||||
if (fileEntry.isDeleted()) {
|
||||
System.out.printf("Skipping deleted file: %s\n", fileEntry.getName());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user