Architecture Overview
This implementation uses three main components working together:
- CommandLineTool: Handles user input parsing
- LogicController: Routes commands to appropriate handlers
- FileHandler: Executes file system operations
An interface-based approach provides a clean contract for all file operations.
Command Input Handler
package kfile
import java.util.Scanner
/**
* Handles command-line input and displays prompts
*/
class CommandLineTool {
companion object {
val getInstance: CommandLineTool by lazy {
CommandLineTool()
}
}
fun run() {
displayWelcome()
while (true) {
processInput()
}
}
private fun displayWelcome() {
println("Welcome to the File Manager. Enter commands:")
}
private fun processInput() {
print("[$currentPathName]# ")
val scanner = Scanner(System.`in`)
val input = scanner.nextLine().trim()
val tokens = input.split(" ")
LogicController.getInstance.route(tokens.size, input, tokens)
}
}
Command Routing Logic
package kfile
import kotlin.system.exitProcess
/**
* Routes parsed commands to appropriate file operations
*/
class LogicController {
companion object {
val getInstance: LogicController by lazy {
LogicController()
}
}
fun route(argCount: Int, command: String, parts: List<String>) {
when (argCount) {
1 -> handleSingleArg(command)
2 -> handleTwoArgs(parts)
3 -> handleThreeArgs(parts)
else -> println("Invalid command format")
}
}
private fun handleSingleArg(cmd: String) {
when (cmd) {
"ls" -> FileHandler().listContents(workingDirectory)
"exit" -> exitProcess(-1)
"pwd" -> FileHandler().printWorkingDir(workingDirectory)
else -> println("Unknown command")
}
}
private fun handleTwoArgs(parts: List<String>) {
val cmd1 = parts[0]
val cmd2 = parts[1]
when {
cmd1 == "ls" && cmd2 == "-l" -> FileHandler().listWithDetails(workingDirectory)
cmd1 == "mkdirs" -> FileHandler().createNestedDirs(workingDirectory, cmd2)
cmd1 == "mkdir" -> FileHandler().createDirectory(workingDirectory, cmd2)
cmd1 == "touch" -> FileHandler().createFile(workingDirectory, cmd2)
cmd1 == "cd" && cmd2 == ".." -> FileHandler().navigateUp(workingDirectory)
cmd1 == "cd" -> FileHandler().navigateTo(workingDirectory, cmd2)
cmd1 == "cat" -> FileHandler().readFile(workingDirectory, cmd2)
cmd1 == "rm" -> FileHandler().remove(workingDirectory, cmd2)
else -> println("Unknown command")
}
}
private fun handleThreeArgs(parts: List<String>) {
val cmd1 = parts[0]
val cmd2 = parts[1]
val cmd3 = parts[2]
when {
cmd1 == "mv" -> FileHandler().moveFile(workingDirectory, cmd2, cmd3)
cmd1 == "rn" -> FileHandler().renameItem(workingDirectory, cmd2, cmd3)
cmd1 == "cp" -> FileHandler().duplicateFile(workingDirectory, cmd2, cmd3)
else -> println("Unknown command")
}
}
}
Global Variables
package kfile
import java.util.ArrayList
// Modify this to match your target directory
var workingDirectory: String = "C:/Users/Username/Desktop"
var currentPathName: String = "desktop"
val pathHistory: ArrayList<String> = arrayListOf()
File Operations Implementation
package kfile
import java.io.File
import java.io.IOException
import java.lang.String.format
class FileHandler : FileOperations {
override fun listContents(path: String) {
File(path).list()?.forEach { name ->
println(name)
}
}
override fun listWithDetails(path: String) {
File(path).listFiles()?.forEach { file ->
val sizeMB = file.length().toDouble() / (1024 * 1024)
println("${file.name} ${format("%.3f", sizeMB)} MB")
}
}
override fun printWorkingDir(path: String) {
println(path)
}
override fun createNestedDirs(path: String, name: String) {
val directory = File(path, name)
if (directory.mkdirs()) {
println("Directory created")
} else {
println("Creation failed")
}
}
override fun createDirectory(path: String, name: String) {
val directory = File("$path/$name")
if (directory.mkdir()) {
println("Directory created")
} else {
println("Creation failed")
}
}
override fun createFile(path: String, name: String) {
val file = File(path, name)
if (file.createNewFile()) {
println("File created")
} else {
println("Creation failed")
}
}
override fun navigateTo(path: String, name: String) {
pathHistory.add(name)
workingDirectory = "$path/$name"
currentPathName = "$currentPathName/$name"
}
override fun navigateUp(path: String) {
if (pathHistory.isNotEmpty()) {
val segmentLength = pathHistory[pathHistory.size - 1].length + 1
workingDirectory = trimEndChars(workingDirectory, segmentLength)
currentPathName = trimEndChars(currentPathName, segmentLength)
pathHistory.removeAt(pathHistory.size - 1)
}
}
override fun readFile(path: String, name: String) {
val file = File(path, name)
if (file.exists()) {
println(file.readText())
} else {
println("File not found")
}
}
override fun remove(path: String, name: String) {
val target = File(path, name)
if (!target.exists()) {
println("Target does not exist")
return
}
if (recursiveDelete(target)) {
println("Removed successfully")
} else {
println("Removal failed")
}
}
private fun recursiveDelete(item: File): Boolean {
if (item.exists()) {
item.listFiles()?.forEach { file ->
if (file.isDirectory) {
recursiveDelete(file)
} else {
file.delete()
}
}
}
return item.delete()
}
override fun moveFile(path: String, source: String, dest: String) {
val sourceFile = File(path, source)
val destFolder = File(path, dest)
val destination = File(destFolder, source)
try {
if (sourceFile.renameTo(destination)) {
println("Moved successfully")
} else {
println("Move failed")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun renameItem(path: String, source: String, dest: String) {
val sourceFile = File(path, source)
val destFile = File(path, dest)
try {
if (sourceFile.renameTo(destFile)) {
println("Renamed successfully")
} else {
println("Rename failed")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun duplicateFile(path: String, source: String, dest: String) {
val sourceFile = File(path, source)
val destFolder = File(path, dest)
val resultFile = File(destFolder, source)
try {
deepCopy(sourceFile, resultFile)
println("Copied successfully")
} catch (e: IOException) {
e.printStackTrace()
println("Copy failed: ${e.message}")
}
}
private fun deepCopy(src: File, dest: File) {
if (src.isDirectory) {
if (!dest.exists()) {
dest.mkdirs()
}
src.listFiles()?.forEach { file ->
deepCopy(file, File(dest, file.name))
}
} else {
src.copyTo(dest, overwrite = true)
}
}
private fun trimEndChars(input: String, count: Int): String {
return if (count >= input.length) "" else input.substring(0, input.length - count)
}
}
Operation Contract Interface
package kfile
interface FileOperations {
fun listContents(path: String)
fun listWithDetails(path: String)
fun printWorkingDir(path: String)
fun createNestedDirs(path: String, name: String)
fun createDirectory(path: String, name: String)
fun createFile(path: String, name: String)
fun navigateTo(path: String, name: String)
fun navigateUp(path: String)
fun readFile(path: String, name: String)
fun remove(path: String, name: String)
fun moveFile(path: String, source: String, dest: String)
fun renameItem(path: String, source: String, dest: String)
fun duplicateFile(path: String, source: String, dest: String)
}
Entry Point
package kfile
fun main() {
CommandLineTool.getInstance.run()
}
Supported Commands
| Command | Arguments | Description |
|---|---|---|
| ls | 0 | List directory contents |
| ls -l | 0 | List with file sizes |
| pwd | 0 | Print working directory |
| mkdir | 1 | Create single directory |
| mkdirs | 1 | Create nested directories |
| touch | 1 | Create empty file |
| cd | 1 | Navigate to directory |
| cd .. | 0 | Navigate to parent |
| cat | 1 | Display file contents |
| rm | 1 | Delete file or folder |
| mv | 2 | Move source to destination |
| rn | 2 | Raname item |
| cp | 2 | Copy source to destination |
| exit | 0 | Terminate program |