ADD: added first version

This commit is contained in:
2025-07-15 21:46:39 +02:00
commit 04e82ce1bd
19 changed files with 597 additions and 0 deletions

51
pom.xml Normal file
View File

@@ -0,0 +1,51 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>filesorter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>13</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>13</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.6</version>
<executions>
<execution>
<!-- Default configuration for running -->
<!-- Usage: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>com.example.App</mainClass>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -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();
}
}

View File

@@ -0,0 +1,26 @@
package com.example;
import javafx.concurrent.Service;
import java.util.Map;
import javafx.concurrent.Task;
public class FileScannerService extends Service<Map<String,Integer>> {
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<Map<String,Integer>> createTask() {
return new FileScannerTask(directoryPath);
}
}

View File

@@ -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<Map<String, Integer>> {
/**
* 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<String, Integer> 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<String, Integer>();
}
/**
* 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<String, Integer> 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<String, Integer> getFileTypeCounts() {
return fileTypeCounts;
}
}

View File

@@ -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<String, Number> 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<String, Integer> 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<String, Integer> fileTypeCounts) {
fileTypeCounts.forEach((fileType, count) -> {
javafx.scene.chart.XYChart.Series<String, Number> 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<String, Integer> 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();
}
}

View File

@@ -0,0 +1,25 @@
package com.example;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
public class FileSorterService extends Service<Integer> {
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<Integer> createTask() {
return new FileSorterTask(directoryPath);
}
}

View File

@@ -0,0 +1,67 @@
package com.example;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.concurrent.Task;
public class FileSorterTask extends Task<Integer> {
/**
* 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<String> 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
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.BarChart?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Text?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.FileSorterController">
<children>
<MenuBar prefHeight="22.0" prefWidth="600.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<ProgressBar fx:id="progressBar" layoutY="377.0" prefHeight="18.0" prefWidth="600.0" progress="0.0" />
<BarChart fx:id="filesDiagram" layoutX="14.0" layoutY="134.0" prefHeight="215.0" prefWidth="382.0">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</BarChart>
<TextField id="path" fx:id="path" layoutX="56.0" layoutY="64.0" onMouseClicked="#openFileChooser" />
<Label layoutX="14.0" layoutY="68.0" text="Path" />
<Button id="scan" layoutX="228.0" layoutY="64.0" mnemonicParsing="false" onAction="#scan" text="scan" />
<Label layoutX="56.0" layoutY="349.0" text="Files " />
<Text fx:id="filesamount" layoutX="26.0" layoutY="362.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0" />
<Button fx:id="sortfiles" layoutX="293.0" layoutY="64.0" mnemonicParsing="false" onAction="#SortFiles" text="Sort Files" />
<Text fx:id="errormessages" layoutX="58.0" layoutY="122.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Ready" />
</children>
</Pane>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.BarChart?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Text?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/24.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.FileSorterController">
<children>
<MenuBar prefHeight="22.0" prefWidth="600.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<ProgressBar fx:id="progressBar" layoutY="377.0" prefHeight="18.0" prefWidth="600.0" progress="0.0" />
<BarChart fx:id="filesDiagram" layoutX="14.0" layoutY="134.0" prefHeight="215.0" prefWidth="382.0">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</BarChart>
<TextField id="path" fx:id="path" layoutX="56.0" layoutY="64.0" onMouseClicked="#openFileChooser" />
<Label layoutX="14.0" layoutY="68.0" text="Path" />
<Button id="scan" layoutX="228.0" layoutY="64.0" mnemonicParsing="false" onAction="#scan" text="scan" />
<Label layoutX="56.0" layoutY="349.0" text="Files " />
<Text fx:id="filesamount" layoutX="26.0" layoutY="362.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0" />
<Button fx:id="sortfiles" layoutX="293.0" layoutY="64.0" mnemonicParsing="false" onAction="#SortFiles" text="Sort Files" />
<Text fx:id="errormessages" layoutX="58.0" layoutY="122.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Ready" />
</children>
</Pane>

Binary file not shown.