File and Resource Handling

File and Resource Handling

When an @Option, @Argument, or @Arguments field is typed as Resource, File, or Path, Aesh automatically provides file path completion, string-to-file conversion, and <file> usage hints in help text – with no extra configuration needed.

Quick Comparison

// Plain string — no completion, no path resolution
@Option(description = "Output path")
private String output;

// Resource — automatic file completion + path resolution + I/O API
@Option(description = "Output path")
private Resource output;

// File — automatic file completion + conversion
@Option(description = "Output path")
private File output;

// Path — automatic file completion + conversion
@Option(description = "Output path")
private Path output;

All three file-typed variants (Resource, File, Path) gain:

FeatureStringResource / File / Path
Tab completionNoneFile paths from filesystem
Type conversionNoneAutomatic string-to-type
Help text<output><file>
Shell redirect completionNoYes (after >, >>, <)

Resource Interface

Resource is Aesh’s filesystem abstraction. It provides a richer API than File or Path with built-in glob expansion, filtering, and I/O streams:

@CommandDefinition(name = "cat", description = "Display file contents")
public class CatCommand implements Command<CommandInvocation> {

    @Argument(description = "File to display", required = true)
    private Resource file;

    @Override
    public CommandResult execute(CommandInvocation invocation) throws CommandException {
        if (!file.exists()) {
            invocation.println("File not found: " + file.getName());
            return CommandResult.FAILURE;
        }
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(file.read()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                invocation.println(line);
            }
        } catch (IOException e) {
            throw new CommandException("Failed to read file", e);
        }
        return CommandResult.SUCCESS;
    }
}

Tab completion works immediately:

myshell$ cat sr<TAB>
src/
myshell$ cat src/main<TAB>
src/main/java/  src/main/resources/

API Reference

Path and name:

MethodReturnsDescription
getName()StringFilename only (no directory)
getAbsolutePath()StringFull absolute path
getParent()ResourceParent directory
newInstance(String)ResourceFactory – creates a new Resource from a path

Type checks:

MethodReturnsDescription
exists()booleanDoes the file/directory exist?
isLeaf()booleanIs it a file (not a directory)?
isDirectory()booleanIs it a directory?
isSymbolicLink()booleanIs it a symbolic link?
readSymbolicLink()ResourceResolves symlink target

Directory listing:

MethodReturnsDescription
list()List<Resource>Lists directory contents
list(ResourceFilter)List<Resource>Lists with filtering
listRoots()List<Resource>Filesystem roots
resolve(Resource cwd)List<Resource>Resolves path with glob expansion (~, *, ?)

I/O:

MethodReturnsDescription
read()InputStreamOpens file for reading
write(boolean append)OutputStreamOpens file for writing (append or overwrite)

File operations:

MethodReturnsDescription
mkdirs()booleanCreates directory and parents
delete()booleanDeletes file or empty directory
move(Resource)voidMoves/renames file
copy(Resource)ResourceCopies file or directory

Metadata:

MethodReturnsDescription
lastModified()longLast modified time (ms)
setLastModified(long)booleanSets last modified time
lastAccessed()longLast accessed time (ms)

FileResource

FileResource is the default Resource implementation backed by java.io.File. When a command field is typed as Resource, the input string is automatically converted to a FileResource resolved relative to the current working directory.

@Option(description = "Config file")
private Resource config;

// In execute():
if (config.isLeaf() && config.exists()) {
    // read the file
    InputStream in = config.read();
}

// Access the underlying File if needed:
File javaFile = ((FileResource) config).getFile();

You can also construct FileResource directly:

Resource home = new FileResource(System.getProperty("user.home"));
Resource config = new FileResource("/etc/myapp/config.yml");
Resource relative = new FileResource("data/output.csv");

Resource Filters

Resource filters control which files appear in directory listings and tab completion. Four built-in filters are available:

FilterAccepts
AllResourceFilterEverything (default)
DirectoryResourceFilterDirectories only
LeafResourceFilterFiles only (not directories)
NoDotNamesFilterFiles not starting with .

Filtering Completion Candidates

To restrict tab completion to directories only, provide a custom completer using FileOptionCompleter with a filter:

@CommandDefinition(name = "cd", description = "Change directory")
public class CdCommand implements Command<CommandInvocation> {

    @Argument(description = "Target directory",
              completer = DirectoryCompleter.class)
    private Resource target;

    @Override
    public CommandResult execute(CommandInvocation invocation) {
        if (target != null && target.isDirectory()) {
            invocation.getAeshContext().setCurrentWorkingDirectory(target);
        }
        return CommandResult.SUCCESS;
    }
}

public class DirectoryCompleter extends FileOptionCompleter {
    public DirectoryCompleter() {
        super(new DirectoryResourceFilter());
    }
}

Now tab completion only suggests directories:

myshell$ cd sr<TAB>
src/
myshell$ cd src/<TAB>
src/main/  src/test/

Filtering Directory Listings

Filters work with Resource.list() too:

// List only non-hidden files
List<Resource> visible = directory.list(new NoDotNamesFilter());

// List only subdirectories
List<Resource> dirs = directory.list(new DirectoryResourceFilter());

Custom Filters

Implement ResourceFilter for custom logic:

public class JavaFileFilter implements ResourceFilter {
    @Override
    public boolean accept(Resource resource) {
        return resource.isDirectory() || resource.getName().endsWith(".java");
    }
}

Glob Expansion

Resource.resolve() supports shell-style globs:

Resource cwd = invocation.getAeshContext().getCurrentWorkingDirectory();
Resource pattern = new FileResource("src/**/*.java");
List<Resource> matches = pattern.resolve(cwd);

The ~ character is expanded to the user’s home directory:

Resource home = new FileResource("~/documents");
List<Resource> resolved = home.resolve(cwd);

Resource vs File vs Path

ResourceFilePath
Tab completionAutomaticAutomaticAutomatic
API richnessFull (listing, glob, I/O, copy/move)Java standardJava NIO
Glob expansionBuilt-in via resolve()ManualVia PathMatcher
FilteringResourceFilter integrationManualManual
AbstractionPluggable (file, pipeline, remote)Local filesystem onlyLocal filesystem only
RecommendationUse for commands that work with filesUse when you need java.io.File APIsUse when you need java.nio.file APIs

Use Resource when your command needs filesystem operations and you want the richest API. Use File or Path if you need compatibility with existing Java APIs that expect those types.

See Also