Skip to content

Commit

Permalink
finalize Directory component, add component scaffold
Browse files Browse the repository at this point in the history
  • Loading branch information
jclausen committed Apr 10, 2024
1 parent 56b5cf8 commit e07a535
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@

package ortus.boxlang.runtime.bifs.global.io;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import ortus.boxlang.runtime.bifs.BIF;
Expand All @@ -25,7 +23,6 @@
import ortus.boxlang.runtime.scopes.ArgumentsScope;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.Argument;
import ortus.boxlang.runtime.types.exceptions.BoxIOException;
import ortus.boxlang.runtime.types.exceptions.BoxRuntimeException;
import ortus.boxlang.runtime.util.FileSystemUtil;

Expand All @@ -41,7 +38,8 @@ public DirectoryCreate() {
declaredArguments = new Argument[] {
new Argument( true, "string", Key.path ),
new Argument( false, "boolean", Key.createPath, true ),
new Argument( false, "boolean", Key.ignoreExists, false )
new Argument( false, "boolean", Key.ignoreExists, false ),
new Argument( false, "string", Key.mode )
};
}

Expand All @@ -56,26 +54,19 @@ public DirectoryCreate() {
* @argument.createPath [true] Whether to create all paths necessary to create the directory path
*
* @argument.ignoreExists [false] Whether to ignore if a directory already exists
*
* @argument.mode When provided will attempt to set the posix permissions on the directory
*/
public Object _invoke( IBoxContext context, ArgumentsScope arguments ) {
String targetDirectory = arguments.getAsString( Key.path );
Path targetPath = Path.of( targetDirectory );
Boolean createPath = arguments.getAsBoolean( Key.createPath );
Boolean ignoreExists = arguments.getAsBoolean( Key.ignoreExists );
String mode = arguments.getAsString( Key.mode );
if ( !ignoreExists && FileSystemUtil.exists( targetDirectory ) ) {
throw new BoxRuntimeException( "The directory [" + targetPath.toAbsolutePath().toString()
throw new BoxRuntimeException( "The directory [" + Path.of( targetDirectory ).toAbsolutePath().toString()
+ "] already exists. Set the boolean argument ignoreExists to true to prevent this error" );
}
try {
if ( createPath ) {
Files.createDirectories( targetPath );
} else {
Files.createDirectory( targetPath );
}
} catch ( IOException e ) {
throw new BoxIOException( e );
}

FileSystemUtil.createDirectory( targetDirectory, createPath, mode );
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,13 @@

package ortus.boxlang.runtime.bifs.global.io;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import ortus.boxlang.runtime.bifs.BIF;
import ortus.boxlang.runtime.bifs.BoxBIF;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.scopes.ArgumentsScope;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.Argument;
import ortus.boxlang.runtime.types.exceptions.BoxIOException;
import ortus.boxlang.runtime.types.exceptions.BoxRuntimeException;
import ortus.boxlang.runtime.util.FileSystemUtil;

@BoxBIF
@BoxBIF( alias = "DirectoryRename" )
Expand Down Expand Up @@ -58,24 +53,11 @@ public DirectoryMove() {
* @argument.createPath [true] Whether to create all necessary paths to the new path
*/
public Object _invoke( IBoxContext context, ArgumentsScope arguments ) {
Path sourcePath = Path.of( arguments.getAsString( Key.oldPath ) );
Path destinationPath = Path.of( arguments.getAsString( Key.newPath ) );
Boolean createPath = arguments.getAsBoolean( Key.createPath );
String source = arguments.getAsString( Key.oldPath );
String destination = arguments.getAsString( Key.newPath );
Boolean createPath = arguments.getAsBoolean( Key.createPath );

if ( !createPath && !Files.exists( destinationPath.getParent() ) ) {
throw new BoxRuntimeException( "The directory [" + destinationPath.toAbsolutePath().toString()
+ "] cannot be created because the parent directory [" + destinationPath.getParent().toAbsolutePath().toString()
+ "] does not exist. To prevent this error set the createPath argument to true." );
} else if ( Files.exists( destinationPath ) ) {
throw new BoxRuntimeException( "The target directory [" + destinationPath.toAbsolutePath().toString() + "] already exists" );
} else {
try {
Files.createDirectories( destinationPath.getParent() );
Files.move( sourcePath, destinationPath );
} catch ( IOException e ) {
throw new BoxIOException( e );
}
}
FileSystemUtil.move( source, destination, createPath );

return null;
}
Expand Down
181 changes: 112 additions & 69 deletions src/main/java/ortus/boxlang/runtime/components/io/Directory.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,46 @@
*/
package ortus.boxlang.runtime.components.io;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import ortus.boxlang.runtime.BoxRuntime;
import ortus.boxlang.runtime.bifs.BIFDescriptor;
import ortus.boxlang.runtime.components.Attribute;
import ortus.boxlang.runtime.components.BoxComponent;
import ortus.boxlang.runtime.components.Component;
import ortus.boxlang.runtime.context.IBoxContext;
import ortus.boxlang.runtime.dynamic.ExpressionInterpreter;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.IStruct;
import ortus.boxlang.runtime.types.exceptions.BoxIOException;
import ortus.boxlang.runtime.types.Struct;
import ortus.boxlang.runtime.types.exceptions.BoxRuntimeException;
import ortus.boxlang.runtime.util.FileSystemUtil;
import ortus.boxlang.runtime.validation.Validator;

@BoxComponent
public class Directory extends Component {

/**
* The runtime instance
*/
protected BoxRuntime runtime = BoxRuntime.getInstance();
private final HashMap<Key, BIFDescriptor> actionsMap = new HashMap<Key, BIFDescriptor>() {

{
put( Key.list,
runtime.getFunctionService().getGlobalFunction( Key.directoryList ) );
put( Key.copy,
runtime.getFunctionService().getGlobalFunction( Key.directoryCopy ) );
put( Key.create,
runtime.getFunctionService().getGlobalFunction( Key.directoryCreate ) );
put( Key.delete,
runtime.getFunctionService().getGlobalFunction( Key.directoryDelete ) );
put( Key.rename,
runtime.getFunctionService().getGlobalFunction( Key.directoryMove ) );
}
};

/**
* Constructor
*/
Expand All @@ -47,21 +66,22 @@ public Directory() {
new Attribute( Key.action, "string", Set.of(
Validator.REQUIRED,
Validator.NON_EMPTY,
Validator.valueOneOf( "list", "create", "delete", "rename" )
Validator.valueOneOf( "list", "create", "delete", "rename", "copy" )
) ),
new Attribute( Key.directory, "string", Set.of( Validator.REQUIRED, Validator.NON_EMPTY ) ),
new Attribute( Key._NAME, "string" ),
new Attribute( Key.filter, "string" ),
new Attribute( Key.filter, "string", "*" ),
new Attribute( Key.mode, "string" ),
new Attribute( Key.sort, "string" ),
new Attribute( Key.newdirectory, "string" ),
new Attribute( Key.newDirectory, "string" ),
new Attribute( Key.destination, "string" ),
new Attribute( Key.recurse, "boolean", false ),
new Attribute( Key.type, "string", "all", Set.of(
Validator.REQUIRED,
Validator.NON_EMPTY,
Validator.valueOneOf( "dir", "file", "all" )
) ),
new Attribute( Key.listInfo, "string", "all", Set.of(
new Attribute( Key.listInfo, "string", "query", Set.of(
Validator.REQUIRED,
Validator.NON_EMPTY,
Validator.valueOneOf( "name", "all" )
Expand All @@ -79,30 +99,30 @@ public Directory() {
* @param executionState The execution state of the Component
*
* @attribute.action The action to perform (list, create, delete, rename)
*
*
* @attribute.directory The directory to perform the action on
*
*
* @attribute.name Name for output record set.
*
*
* @attribute.filter Filter applied to returned names. For example: *.bx You can use a pipe ("|") delimiter to specify multiple filters. For example:
* *.bxm|*.bx Filter pattern matches are case-sensitive on UNIX and Linux. Can also be a UDF/Closure which accepts the
* file/directory name and returns a Boolean value to indicate whether that item should be included in the result or not.
*
*
* @attribute.mode Applies only to UNIX and Linux. Permissions. Octal values of Unix chmod command. Assigned to owner, group, and other, respectively.
* For example: 777
*
*
* @attribute.sort Query column(s) by which to sort directory listing. Delimited list of columns from query output.
*
* @attribute.newdirectory The new directory name for the rename action.
*
*
* @attribute.newDirectory The new directory name for the rename action.
*
* @attribute.recurse Recurse into subdirectories.
*
*
* @attribute.type Type of directory listing to return (dir, file, all).
*
*
* @attribute.listInfo Information to return in the listing (name, all).
*
*
* @attribute.createPath Whether to create all paths necessary to create the directory path.
*
*
*/
public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBody body, IStruct executionState ) {
Key action = Key.of( attributes.getAsString( Key.action ) );
Expand All @@ -111,7 +131,8 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
String filter = attributes.getAsString( Key.filter );
String mode = attributes.getAsString( Key.mode );
String sort = attributes.getAsString( Key.sort );
String newdirectory = attributes.getAsString( Key.newdirectory );
String newDirectory = attributes.getAsString( Key.newDirectory );
String destination = attributes.getAsString( Key.destination );
Boolean recurse = attributes.getAsBoolean( Key.recurse );
String type = attributes.getAsString( Key.type );
String listInfo = attributes.getAsString( Key.listInfo );
Expand All @@ -124,69 +145,91 @@ public BodyResult _invoke( IBoxContext context, IStruct attributes, ComponentBod
} else if ( action.equals( Key.delete ) ) {
delete( context, directory, recurse );
} else if ( action.equals( Key.rename ) ) {
rename( context, directory, newdirectory, createPath, mode );
rename( context, directory, newDirectory, createPath );
} else if ( action.equals( Key.copy ) ) {
copy( context, directory, destination, recurse, filter, createPath );
} else {
throw new BoxRuntimeException( "Unimplemeted action: " + action.getName() );
throw new BoxRuntimeException( "Unimplemeted directory action: " + action.getName() );
}

return DEFAULT_RETURN;
}

private void rename( IBoxContext context, String directory, String newdirectory, Boolean createPath, String mode ) {
// TODO: Extract this logic in the directoryMove BIF to the FileSystem service and share this code
Path destinationPath = Path.of( newdirectory );
Path sourcePath = Path.of( directory );
if ( !createPath && !Files.exists( destinationPath.getParent() ) ) {
throw new BoxRuntimeException( "The directory [" + destinationPath.toAbsolutePath().toString()
+ "] cannot be created because the parent directory [" + destinationPath.getParent().toAbsolutePath().toString()
+ "] does not exist. To prevent this error set the createPath argument to true." );
} else if ( Files.exists( destinationPath ) ) {
throw new BoxRuntimeException( "The target directory [" + destinationPath.toAbsolutePath().toString() + "] already exists" );
} else {
try {
Files.createDirectories( destinationPath.getParent() );
Files.move( sourcePath, destinationPath );
if ( mode != null ) {
FileSystemUtil.setPosixPermissions( newdirectory, mode );
}
} catch ( IOException e ) {
throw new BoxIOException( e );
}
}
private void rename( IBoxContext context, String directory, String newDirectory, Boolean createPath ) {
actionsMap.get( Key.rename )
.invoke(
context,
Map.of(
Key.oldPath, directory,
Key.newPath, newDirectory,
Key.createPath, createPath
),
false,
Key.directoryMove
);
}

private void delete( IBoxContext context, String directory, Boolean recurse ) {
FileSystemUtil.deleteDirectory( directory, recurse );
actionsMap.get( Key.delete )
.invoke(
context,
Map.of(
Key.path, directory,
Key.recursive, recurse
),
false,
Key.directoryDelete
);
}

private void create( IBoxContext context, String directory, Boolean createPath, String mode ) {
// TODO: Extract this logic in the directoryCreate BIF to the FileSystem service and share this code
try {
if ( createPath ) {
Files.createDirectories( Path.of( directory ) );
} else {
Files.createDirectory( Path.of( directory ) );
}
if ( mode != null ) {
FileSystemUtil.setPosixPermissions( directory, mode );
}
} catch ( IOException e ) {
throw new BoxIOException( e );
Map<Key, Object> argumentsMap = Map.of(
Key.path, directory,
Key.createPath, createPath,
Key.ignoreExists, false
);
if ( mode != null ) {
argumentsMap.put( Key.mode, mode );
}
actionsMap.get( Key.create ).invoke(
context,
argumentsMap,
false,
Key.directoryCreate
);
}

private void list( IBoxContext context, String directory, String name, String filter, String sort, Boolean recurse, String type, String listInfo ) {
// TODO: Extract this logic in the directoryList BIF to the FileSystem service and add optional filters
// for type (dir, file, all), and listInfo (name, all)
Object result = runtime.getFunctionService().getGlobalFunction( Key.directoryList ).invoke( context, Map.of(
Key.path, directory,
private void copy( IBoxContext context, String directory, String newDirectory, Boolean recurse, String filter, Boolean createPath ) {
IStruct argumentsMap = Struct.of(
Key.source, directory,
Key.destination, newDirectory,
Key.recurse, recurse,
Key.listInfo, "query",
Key.filter, filter != null ? filter : "",
Key.sort, sort != null ? sort : "",
Key.type, type
), false, Key.directoryList );
ExpressionInterpreter.setVariable( context, name, result );
Key.filter, filter,
Key.createPath, createPath
);
System.out.println( argumentsMap.asString() );
actionsMap.get( Key.copy ).invoke( context, argumentsMap, false, Key.directoryCopy );
}

private void list( IBoxContext context, String directory, String name, String filter, String sort, Boolean recurse, String type, String listInfo ) {
ExpressionInterpreter.setVariable(
context,
name,
actionsMap.get( Key.list )
.invoke(
context,
Map.of(
Key.path, directory,
Key.recurse, recurse,
Key.listInfo, listInfo,
Key.filter, filter != null ? filter : "",
Key.sort, sort != null ? sort : "",
Key.type, type
),
false,
Key.directoryList
)
);
}

}
Loading

0 comments on commit e07a535

Please sign in to comment.