Old code, new UI.

master
Ivan Olexyn 6 years ago
commit 268736c6c7

@ -0,0 +1,24 @@
package app;
import java.io.File;
public class Artifacts {
Tools tools = new Tools();
public MFile getMFile(File file) {
MFile mfile = new MFile();
mfile.file = file;
mfile.md5 = tools.getMd5(file.getPath());
return mfile;
}
public class MFile {
public File file;
public String md5;
}
}

@ -0,0 +1,117 @@
package app;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.text.Text;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import app.Artifacts.MFile;
public class Controller {
Map<Integer, MFile> doubles;
@FXML
protected Text loadDirState;
@FXML
protected Text calcMd5State;
@FXML
protected Text sortFileState;
@FXML
protected Text findDoubleState;
@FXML
protected Text delDoubleState;
@FXML
protected Text fileNr;
@FXML
protected Text doubleNr;
@FXML
protected TextField dir;
@FXML
protected void loadDir() {
Task<Void> loadDirTask = new Task<Void>() {
@Override
public Void call() throws Exception {
loadDirState.setText("");
calcMd5State.setText("");
sortFileState.setText("");
findDoubleState.setText("");
delDoubleState.setText("");
fileNr.setText("Number of Files:");
doubleNr.setText("Number of Doubles:");
Path path = Paths.get(dir.getText());
if (!Files.isDirectory(path)) {
loadDirState.setText("ERROR.");
} else {
Map<Integer, File> pool = new Routines().loadPool(dir.getText(), "file");
new Write().textPool("pool", pool);
loadDirState.setText("OK.");
fileNr.setText("Number of Files: " + pool.size());
Map<Integer, MFile> md5Pool = new Routines().md5Pool(pool);
new Write().textMd5Pool("md5Pool", md5Pool);
calcMd5State.setText("OK.");
Map<Integer, MFile> qsMd5Pool = new QuicksortMd5().quicksortMd5(md5Pool);
new Write().textMd5Pool("qsMd5Pool", qsMd5Pool);
sortFileState.setText("OK.");
doubles = new Routines().doubles(qsMd5Pool);
new Write().textMd5Pool("doubles", doubles);
findDoubleState.setText("OK.");
doubleNr.setText("Number of Doubles: " + doubles.size());
}
return null;
}
};
new Thread(loadDirTask).start();
}
@FXML
protected void deleteDoubles() {
Task<Void> delDoubleTask = new Task<Void>() {
@Override
public Void call() throws Exception {
for (int i = 0; i < doubles.size(); i++) {
new Execute().execute(new String[]{"rm", doubles.get(i).file.getAbsolutePath()});
}
delDoubleState.setText("OK.");
return null;
}
};
new Thread(delDoubleTask).start();
}
}

@ -0,0 +1,34 @@
package app;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Execute {
/**
*
* @param cmd an array representing a shell command
* @return <i>TwoBr</i> class, containing two BufferedReaders,
* <i>output</i> and <i>error</i>
* @see <i>output</i> BufferedReader, corresponds to STDOUT
* <i>error</i> BufferedReader, corresponds to STDERR
*/
public TwoBr execute(String cmd[]) {
TwoBr twobr = new TwoBr();
try {
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
twobr.output = new BufferedReader(new InputStreamReader(process.getInputStream()));
twobr.error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
} catch (Exception e) {
e.printStackTrace();
}
return twobr;
}
public class TwoBr {
public BufferedReader output;
public BufferedReader error;
}
}

@ -0,0 +1,34 @@
package app;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class Main extends Application {
static String foo;
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("layout.fxml"));
Scene scene = new Scene(root, 300, 275);
primaryStage.setTitle("mucc");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

@ -0,0 +1,76 @@
package app;
import app.Artifacts.MFile;
import java.util.Map;
public class QuicksortMd5 {
private Map<Integer, MFile> md5Pool;
private int i;
private int j;
private int p;
public Map<Integer, MFile> quicksortMd5(Map<Integer, MFile> md5Pool) {
this.md5Pool = md5Pool;
quicksort(0, md5Pool.size() - 1);
return this.md5Pool;
}
private void quicksort(int low, int high) {
i = low;
j = high;
p = low + (high - low) / 2;
while (i < j) {
// a < b a.compareto(b) = -1
// a = b a.compareto(b) = 0
// a > b a.compareto(b) = 1
while (i < p) {
if (imd5().compareTo(pmd5()) <= 0) {
i++;
} else {
swap(i, p);
j = high;
}
}
while (p < j) {
if (jmd5().compareTo(pmd5()) >= 0) {
j--;
} else {
swap(p, j);
i = low;
}
}
}
if (high - p > 2) {
quicksort(p, high);
}
if (p - low > 2) {
quicksort(low, p);
}
}
private void swap(int a, int b) {
MFile temp = md5Pool.get(a);
md5Pool.put(a, md5Pool.get(b));
md5Pool.put(b, temp);
}
private String imd5() {
return md5Pool.get(i).md5;
}
private String jmd5() {
return md5Pool.get(j).md5;
}
private String pmd5() {
return md5Pool.get(p).md5;
}
}

@ -0,0 +1,35 @@
### What is `mucc`?
`mucc` is an example for interactions between Linux, Java and JavaFX.
It is a tool for purging duplicates from
a filesystem.
It does this by calculating a hash of each file
and sorting the files by hash.
If two files have the same hash, the older file will be deleted.
It also displays some information about what is happening.
This ensures the user never suspects something went horribly wrong.
### Package Contents
| Class | Description |
|---------------|-------------|
| Artifacts | Simple objects used by other classes.|
| Controller | JavaFX class containing application logic. |
| Execute | Issues shell commands.|
| layout.fxml | Contains layout data.|
| Main | Main JavaFX class. Run from here.|
| QuicksortMd5 | Quicksort algorithm.|
| README.md | You are here.|
| Routines | Higher level routines called by Controller.|
| Tools | Simple tools.|
| Write | Writes to /tmp. Used for data storage.|
### Issues and Features
- Add proper directory selection.
- Fix issues where nested duplicates would not be deleted on first pass.
- Make UI prettier.
- Make code prettier.

@ -0,0 +1,92 @@
package app;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import app.Artifacts.MFile;
public class Routines {
Execute x = null;
Tools tools = new Tools();
Write write = new Write();
public Routines() {
this.x = new Execute();
this.tools = new Tools();
}
/**
* [1] Write output of <b>find srcdir</b> to <b>/tmp/find</b> <br>
* [2] Read <b>/tmp/find</b> into <b>List>String></b> <br>
* [3] Add <b>List>String></b> entries to <b>Map>String,File></b> , where
* <b>String</b> is an <b>int</b> key. <br>
*
* @param srcdir
* @param type file OR directory
* @return filepool
*/
public Map<Integer, File> loadPool(String srcdir, String type) {
// [1]
x.execute(new String[]{System.getProperty("user.dir") + "/src/app/toFile.sh", "find", srcdir, "/tmp/find"});
// [2]
List<String> lines = null;
try {
lines = Files.readAllLines(Paths.get("/tmp/find"));
} catch (IOException e) {
e.printStackTrace();
}
// [3]
Map<Integer, File> filepool = new HashMap<Integer, File>();
int j = 0;
for (int i = 0; i < lines.size(); i++) {
File file = new File(lines.get(i));
if (type == "directory" && file.isDirectory() || type == "file" && file.isFile()) {
filepool.put(j, file);
j++;
}
}
return filepool;
}
public Map<Integer, MFile> md5Pool(Map<Integer, File> pool) {
Map<Integer, MFile> md5Pool = new HashMap<>();
for (int i = 0; i < pool.size(); i++) {
File file = pool.get(i);
md5Pool.put(i, new Artifacts().getMFile(file));
}
return md5Pool;
}
public Map<Integer, MFile> doubles(Map<Integer, MFile> md5Pool) {
Map<Integer, MFile> doubles = new HashMap<>();
int d = 0;
for (int i = 0; i < md5Pool.size() - 1; i++) {
MFile iF = md5Pool.get(i);
MFile jF = md5Pool.get(i + 1);
if (iF.md5.equals(jF.md5)) {
if (iF.file.lastModified() >= jF.file.lastModified()) {
doubles.put(d, iF);
d++;
} else {
doubles.put(d, jF);
d++;
}
}
}
return doubles;
}
}

@ -0,0 +1,29 @@
package app;
import java.io.BufferedReader;
import java.io.IOException;
public class Tools {
Execute x;
public Tools() {
x = new Execute();
}
/**
* @return Md5 of File at @param path
*/
public String getMd5(String path) {
// output of md5sum: "md5 filepath"
BufferedReader md5reader = x.execute(new String[] { "md5sum", path }).output;
String md5 = null;
try {
md5 = md5reader.readLine().split(" ")[0];
} catch (IOException e) {
e.printStackTrace();
}
return md5;
}
}

@ -0,0 +1,56 @@
package app;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.Map;
import app.Artifacts.MFile;
public class Write {
/**
* writes <b>text</b> to file at <b>path</b>
* <p>
*/
public void textFile(String path, StringBuilder text) {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(new File(path)));
bw.write(text.toString());
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* append all elements of <b>pool</b> to StringBuilder and write to
* <b>/tmp/name</b>
* <p>
*
* @param pool
*/
public void textPool(String name, Map<Integer, File> pool) {
StringBuilder text = new StringBuilder();
for (int i = 0; i < pool.size(); i++) {
text.append(i + " " + pool.get(i) + "\n");
}
textFile("/tmp/" + name, text);
}
/**
* append all elements of <b>md5Pool</b> to StringBuilder and write to
* <b>/tmp/name</b>
* <p>
*
* @param md5Pool
*/
public void textMd5Pool(String name, Map<Integer, MFile> md5Pool) {
StringBuilder text = new StringBuilder();
for (int i = 0; i < md5Pool.size(); i++) {
text.append(i + " " + md5Pool.get(i).md5 + " " + md5Pool.get(i).file + "\n");
}
textFile("/tmp/" + name, text);
}
}

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<GridPane fx:controller="app.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
<padding>
<Insets top="25" right="25" bottom="10" left="25"/>
</padding>
<Text
text="Task"
GridPane.rowIndex="0"
GridPane.columnIndex="0"
/>
<Text
text="State"
GridPane.rowIndex="0"
GridPane.columnIndex="1"
/>
<Text
text="Loading Directory."
GridPane.rowIndex="1"
GridPane.columnIndex="0"
/>
<Text fx:id="loadDirState"
text=""
GridPane.rowIndex="1"
GridPane.columnIndex="1"
/>
<Text
text="Calculating Md5."
GridPane.rowIndex="2"
GridPane.columnIndex="0"
/>
<Text fx:id="calcMd5State"
text=""
GridPane.rowIndex="2"
GridPane.columnIndex="1"
/>
<Text
text="Sorting Files."
GridPane.rowIndex="3"
GridPane.columnIndex="0"
/>
<Text fx:id="sortFileState"
text=""
GridPane.rowIndex="3"
GridPane.columnIndex="1"
/>
<Text
text="Finding Doubles."
GridPane.rowIndex="4"
GridPane.columnIndex="0"
/>
<Text fx:id="findDoubleState"
text=""
GridPane.rowIndex="4"
GridPane.columnIndex="1"
/>
<Text
text="Deleting Doubles."
GridPane.rowIndex="5"
GridPane.columnIndex="0"
/>
<Text fx:id="delDoubleState"
text=""
GridPane.rowIndex="5"
GridPane.columnIndex="1"
/>
<TextField fx:id="dir"
text="Select Directory."
GridPane.rowIndex="6"
GridPane.columnIndex="0"
/>
<Button text="Load"
GridPane.rowIndex="6"
GridPane.columnIndex="1"
onAction="#loadDir"/>
<Text fx:id="fileNr"
text="Number of Files:"
GridPane.rowIndex="7"
GridPane.columnIndex="0"
/>
<Text fx:id="doubleNr"
text="Number of Doubles:"
GridPane.rowIndex="8"
GridPane.columnIndex="0"
/>
<Button text="Delete"
GridPane.rowIndex="8"
GridPane.columnIndex="1"
onAction="#deleteDoubles"/>
</GridPane>

@ -0,0 +1,6 @@
#!/bin/bash
a=$1
b=$2
c=$3
$a $b > $c
Loading…
Cancel
Save