commit 04e82ce1bdf928bbfc8c950bd5c6579cc483e912 Author: Henry Winkel Date: Tue Jul 15 21:46:39 2025 +0200 ADD: added first version diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1fb7c51 --- /dev/null +++ b/pom.xml @@ -0,0 +1,51 @@ + + 4.0.0 + com.example + filesorter + 1.0-SNAPSHOT + + UTF-8 + 11 + 11 + + + + org.openjfx + javafx-controls + 13 + + + org.openjfx + javafx-fxml + 13 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 11 + + + + org.openjfx + javafx-maven-plugin + 0.0.6 + + + + + default-cli + + com.example.App + + + + + + + diff --git a/src/main/java/com/example/App.java b/src/main/java/com/example/App.java new file mode 100644 index 0000000..0dfbe27 --- /dev/null +++ b/src/main/java/com/example/App.java @@ -0,0 +1,41 @@ +package com.example; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.io.IOException; + +/** + * JavaFX App + */ +public class App extends Application { + + private static Scene scene; + + @Override + public void start(Stage stage) throws IOException { + scene = new Scene(loadFXML("primary"), 640, 480); + stage.setScene(scene); + stage.show(); + + + + } + + static void setRoot(String fxml) throws IOException { + scene.setRoot(loadFXML(fxml)); + } + + private static Parent loadFXML(String fxml) throws IOException { + FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml")); + return fxmlLoader.load(); + } + + public static void main(String[] args) { + launch(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/FileScannerService.java b/src/main/java/com/example/FileScannerService.java new file mode 100644 index 0000000..d23e9fb --- /dev/null +++ b/src/main/java/com/example/FileScannerService.java @@ -0,0 +1,26 @@ +package com.example; + +import javafx.concurrent.Service; + +import java.util.Map; + +import javafx.concurrent.Task; + +public class FileScannerService extends Service> { + + private String directoryPath; + + // Setter for argument + public void setDirectoryPath(String directoryPath) { + this.directoryPath = directoryPath; + } + /** + * Create and return the task for fetching the data. Note that this method + * is called on the background thread (all other code in this application is + * on the JavaFX Application Thread!). + */ + @Override + protected Task> createTask() { + return new FileScannerTask(directoryPath); + } + } \ No newline at end of file diff --git a/src/main/java/com/example/FileScannerTask.java b/src/main/java/com/example/FileScannerTask.java new file mode 100644 index 0000000..84e8e89 --- /dev/null +++ b/src/main/java/com/example/FileScannerTask.java @@ -0,0 +1,119 @@ +package com.example; + +import java.io.File; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javafx.concurrent.Task; + +public class FileScannerTask extends Task> { + + /** + * The path to the directory to be scanned. + */ + private String directoryPath; + /** + * A map to hold the count of files for each file type. + * The key is the file type (extension), and the value is the count of files of that type. + */ + private Map fileTypeCounts; + + /** + * Constructor for FileSorterTask. + * + * @param directoryPath The path to the directory to be scanned. + */ + public FileScannerTask(String directoryPath) { + this.directoryPath = directoryPath; + this.fileTypeCounts = new ConcurrentHashMap(); + } + + /** + * The main method that will be executed in a background thread. + * It scans the specified directory and counts the number of files of each type. + * + * @return A map containing file types as keys and their counts as values. + * @throws Exception If an error occurs during the file scanning process. + */ + protected Map call() throws Exception { + int filesAmount = countFilesInDirectory(); + + + java.io.File directory = new java.io.File(directoryPath); + if (directory.isDirectory()) { + java.io.File[] files = directory.listFiles(); + if (files != null) { + int i = 0; + for (java.io.File file : files) { + if (file.isFile()) { + String fileType = getFileExtension(file); + fileTypeCounts.put(fileType, fileTypeCounts.getOrDefault(fileType, 0) + 1); + } + + updateProgress(i, filesAmount); + i++; + } + } + } + + return fileTypeCounts; + } + + /** + * Sets the directory path to be scanned. + * + * @param directoryPath The path to the directory. + */ + public int countFilesInDirectory() { + java.io.File directory = new java.io.File(directoryPath); + + if (directory.isDirectory()) { + java.io.File[] files = directory.listFiles(); + if (files != null) { + return files.length; + } + } + return 0; + } + + /** + * Counts the number of files in a given directory. + * + * @param directoryPath The path to the directory. + * @return The number of files in the directory, or 0 if the path is not a directory. + */ + public static int countFilesInDirectory(String directoryPath) { + java.io.File directory = new java.io.File(directoryPath); + if (directory.isDirectory()) { + java.io.File[] files = directory.listFiles(); + if (files != null) { + return files.length; + } + } + return 0; + } + /** + * Returns the file extension of a given file. + * + * @param file The file to check. + * @return The file extension in lowercase, or "unknown" if no extension is found. + */ + public String getFileExtension(File file) { + String fileName = file.getName(); + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex > 0 && dotIndex < fileName.length() - 1) { + return fileName.substring(dotIndex + 1).toLowerCase(); + } + return "unknown"; // Return "unknown" if no extension is found + } + /** + * Returns the map containing file type counts. + * + * @return A map where keys are file types and values are their respective counts. + */ + public Map getFileTypeCounts() { + return fileTypeCounts; + } + + +} diff --git a/src/main/java/com/example/FileSorterController.java b/src/main/java/com/example/FileSorterController.java new file mode 100644 index 0000000..188fdaf --- /dev/null +++ b/src/main/java/com/example/FileSorterController.java @@ -0,0 +1,150 @@ +package com.example; + +import java.io.IOException; +import java.util.Map; + +import javafx.fxml.FXML; +import javafx.scene.chart.BarChart; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.TextField; +import javafx.scene.text.Text; + + +public class FileSorterController { + + private boolean isScanned = false; + + java.io.File directory = null; + + @FXML + private TextField path ; + + @FXML + private Text errormessages; + + @FXML + private Text filesamount; + + @FXML + private BarChart filesDiagram; + + @FXML + private ProgressBar progressBar; + + + @FXML + private void scan() throws IOException { + errormessages.setText(""); // Clear previous error messages + filesDiagram.getData().clear(); // Clear previous data from the bar chart + // Check if the directory is valid + directory = checkDirectory(path.getText()); + + final FileScannerService service = new FileScannerService(); + service.setDirectoryPath(directory.getAbsolutePath()); + + progressBar.progressProperty().bind(service.progressProperty()); + service.setOnSucceeded(event -> { + Map result = service.getValue(); // this is the result from the task + System.out.println("Result: " + result); + updateBarChart(result); + filesamount.setText(String.valueOf(result.values().stream().mapToInt(Integer::intValue).sum())); + }); + service.start(); + + isScanned = true; + } + + /** + * Checks if the provided path is a valid directory. + * + * @param Path The path to the directory to be checked. + * @return A java.io.File object representing the directory if valid, null otherwise. + */ + private java.io.File checkDirectory(String Path) { + if (path.getText().isEmpty()) { + System.out.println("Please select a directory to scan."); + errormessages.setText("Please select a directory to scan."); + return null; + } + java.io.File directory = new java.io.File(path.getText()); + if (!directory.exists() || !directory.isDirectory()) { + System.out.println("Invalid directory: " + path.getText()); + errormessages.setText("Invalid directory: " + path.getText()); + return null; + } + return directory; + } + + /** + * Updates the bar chart with the file type counts. + * + * @param fileTypeCounts A map where keys are file types and values are their respective counts. + */ + private void updateBarChart(Map fileTypeCounts) { + fileTypeCounts.forEach((fileType, count) -> { + javafx.scene.chart.XYChart.Series series = new javafx.scene.chart.XYChart.Series<>(); + series.setName(fileType); + series.getData().add(new javafx.scene.chart.XYChart.Data<>(fileType, count)); + filesDiagram.getData().add(series); + }); + } + + + @FXML + private void openFileChooser() { + javafx.stage.DirectoryChooser directoryChooser = new javafx.stage.DirectoryChooser(); + directoryChooser.setTitle("Select a Directory"); + javafx.stage.Window window = javafx.stage.Window.getWindows().stream() + .filter(javafx.stage.Window::isShowing) + .findFirst() + .orElse(null); + if (window != null) { + java.io.File selectedDirectory = directoryChooser.showDialog(window); + if (selectedDirectory != null) { + System.out.println("Selected directory: " + selectedDirectory.getAbsolutePath()); + path.setText(selectedDirectory.getAbsolutePath()); + // javafx.scene.control.TextField path = (javafx.scene.control.TextField) loader.getNamespace().get("path"); + // path.setText(selectedDirectory.getAbsolutePath()); + } + } + } + + + + @FXML + private void SortFiles() { + // Implement sorting logic here + System.out.println("Sorting files..."); + // This method can be used to sort files based on the file type counts + // For example, you can move files to different directories based on their types + + if (!isScanned) { + System.out.println("Please scan the directory first."); + + directory = checkDirectory(path.getText()); + + final FileScannerService service = new FileScannerService(); + + service.setDirectoryPath(directory.getAbsolutePath()); + + progressBar.progressProperty().bind(service.progressProperty()); + service.setOnSucceeded(event -> { + Map result = service.getValue(); // this is the result from the task + System.out.println("Result: " + result); + updateBarChart(result); + filesamount.setText(String.valueOf(result.values().stream().mapToInt(Integer::intValue).sum())); + }); + service.start(); + } + progressBar.paddingProperty().unbind(); + // progressBar.setProgress(0); // Reset progress bar before starting the service + + final FileSorterService service = new FileSorterService(); + service.setDirectoryPath(directory.getAbsolutePath()); + progressBar.progressProperty().bind(service.progressProperty()); + + service.start(); + } + + +} diff --git a/src/main/java/com/example/FileSorterService.java b/src/main/java/com/example/FileSorterService.java new file mode 100644 index 0000000..1d34516 --- /dev/null +++ b/src/main/java/com/example/FileSorterService.java @@ -0,0 +1,25 @@ +package com.example; + +import javafx.concurrent.Service; + + +import javafx.concurrent.Task; + +public class FileSorterService extends Service { + + private String directoryPath; + + // Setter for argument + public void setDirectoryPath(String directoryPath) { + this.directoryPath = directoryPath; + } + /** + * Create and return the task for fetching the data. Note that this method + * is called on the background thread (all other code in this application is + * on the JavaFX Application Thread!). + */ + @Override + protected Task createTask() { + return new FileSorterTask(directoryPath); + } + } diff --git a/src/main/java/com/example/FileSorterTask.java b/src/main/java/com/example/FileSorterTask.java new file mode 100644 index 0000000..9b4d26e --- /dev/null +++ b/src/main/java/com/example/FileSorterTask.java @@ -0,0 +1,67 @@ +package com.example; + +import java.util.concurrent.atomic.AtomicInteger; + +import javafx.concurrent.Task; + +public class FileSorterTask extends Task { + + /** + * The path to the directory to be scanned. + */ + private String directoryPath; + + /** + * Constructor for FileSorterTask. + * + * @param directoryPath The path to the directory to be scanned. + */ + public FileSorterTask(String directoryPath) { + this.directoryPath = directoryPath; + } + + /** + * The main method that will be executed in a background thread. + * It scans the specified directory and counts the number of files of each type. + * + * @return A map containing file types as keys and their counts as values. + * @throws Exception If an error occurs during the file scanning process. + */ + protected Integer call() throws Exception { + System.out.println("Scanning directory: " + directoryPath); + int filesAmount = FileScannerTask.countFilesInDirectory(directoryPath); + // Use a Set to track which year folders have already been created + AtomicInteger i = new AtomicInteger(0); + java.util.Set createdFolders = new java.util.HashSet<>(); + java.nio.file.Files.list(java.nio.file.Paths.get(directoryPath)) + .filter(java.nio.file.Files::isRegularFile) + .forEach(path -> { + try { + java.nio.file.attribute.BasicFileAttributes attrs = java.nio.file.Files.readAttributes(path, java.nio.file.attribute.BasicFileAttributes.class); + // Use the earlier of creationTime and lastModifiedTime + java.time.Instant creationInstant = attrs.creationTime().toInstant(); + java.time.Instant modifiedInstant = attrs.lastModifiedTime().toInstant(); + java.time.Instant instant = creationInstant.isBefore(modifiedInstant) ? creationInstant : modifiedInstant; + java.time.ZonedDateTime zdt = java.time.ZonedDateTime.ofInstant(instant, java.time.ZoneId.systemDefault()); + String year = String.valueOf(zdt.getYear()); + java.nio.file.Path targetDir = java.nio.file.Paths.get(directoryPath, year); + + // Only create the directory if it hasn't been created yet + if (createdFolders.add(year)) { + java.nio.file.Files.createDirectories(targetDir); + } + + java.nio.file.Path targetPath = targetDir.resolve(path.getFileName()); + java.nio.file.Files.copy(path, targetPath, java.nio.file.StandardCopyOption.REPLACE_EXISTING); + updateProgress(i.get(), filesAmount); + i.incrementAndGet(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + // Implementation of file scanning logic goes here + return 0; // Placeholder return value + } + + +} diff --git a/src/main/java/com/example/PrimaryController.java b/src/main/java/com/example/PrimaryController.java new file mode 100644 index 0000000..a23d23c --- /dev/null +++ b/src/main/java/com/example/PrimaryController.java @@ -0,0 +1,12 @@ +package com.example; + +import java.io.IOException; +import javafx.fxml.FXML; + +public class PrimaryController { + + @FXML + private void switchToSecondary() throws IOException { + App.setRoot("secondary"); + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..9a6ca7e --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,8 @@ +module com.example { + requires javafx.controls; + requires javafx.fxml; + requires javafx.graphics; + + opens com.example to javafx.fxml; + exports com.example; +} diff --git a/src/main/resources/com/example/primary.fxml b/src/main/resources/com/example/primary.fxml new file mode 100644 index 0000000..ef52f2d --- /dev/null +++ b/src/main/resources/com/example/primary.fxml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +