From 7d91affb0b498449cebdc085eb3a14947c22a497 Mon Sep 17 00:00:00 2001 From: Dutchman101 Date: Tue, 13 Mar 2018 23:46:57 +0100 Subject: [PATCH] Initial commit --- .gitignore | 22 ++ README.md | 32 ++ SpaceTrimmer.iml | 11 + src/META-INF/MANIFEST.MF | 3 + .../spacetrimmer/launcher/Launcher.java | 19 ++ .../spacetrimmer/model/ProcessableFile.java | 18 + .../dutchman/spacetrimmer/resources/icon.png | Bin 0 -> 3207 bytes .../spacetrimmer/resources/processing.gif | Bin 0 -> 25126 bytes .../dutchman/spacetrimmer/resources/ready.png | Bin 0 -> 764 bytes .../dutchman/spacetrimmer/ui/MainWindow.form | 229 +++++++++++++ .../dutchman/spacetrimmer/ui/MainWindow.java | 64 ++++ .../spacetrimmer/ui/WindowController.java | 312 ++++++++++++++++++ .../spacetrimmer/utils/DialogManager.java | 28 ++ .../utils/LimitLinesDocumentListener.java | 108 ++++++ .../spacetrimmer/utils/MessageConsole.java | 243 ++++++++++++++ .../utils/ProcessInformation.java | 21 ++ 16 files changed, 1110 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 SpaceTrimmer.iml create mode 100644 src/META-INF/MANIFEST.MF create mode 100644 src/nl/dutchman/spacetrimmer/launcher/Launcher.java create mode 100644 src/nl/dutchman/spacetrimmer/model/ProcessableFile.java create mode 100644 src/nl/dutchman/spacetrimmer/resources/icon.png create mode 100644 src/nl/dutchman/spacetrimmer/resources/processing.gif create mode 100644 src/nl/dutchman/spacetrimmer/resources/ready.png create mode 100644 src/nl/dutchman/spacetrimmer/ui/MainWindow.form create mode 100644 src/nl/dutchman/spacetrimmer/ui/MainWindow.java create mode 100644 src/nl/dutchman/spacetrimmer/ui/WindowController.java create mode 100644 src/nl/dutchman/spacetrimmer/utils/DialogManager.java create mode 100644 src/nl/dutchman/spacetrimmer/utils/LimitLinesDocumentListener.java create mode 100644 src/nl/dutchman/spacetrimmer/utils/MessageConsole.java create mode 100644 src/nl/dutchman/spacetrimmer/utils/ProcessInformation.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6143e53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/README.md b/README.md new file mode 100644 index 0000000..8296b21 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Trailing space trimmer + +This JAVA application allows you to trim trailing spaces and empty tabs over a whole project. +It's able to get rid of all trailing spaces for all code files in a directory and its subdirectories, so you could work on a whole repository at once. + +All programming languages are supported, you can input a comma-separated list of code-file extensions into the app's GUI, so that all matching files are included in the operation. + +Definition of GUI option 1 ("Remove trailing spaces/tabs"): +- empty whitespaces at the end of a line consisting of characters (only the very end of it, no loss of indent) +- actual tabs/spaces content in empty lines (it won't delete the empty lines itself, only bring it back to 0 bytes) + +[Example] See below image for the effects of this option: + +[img] https://i.imgur.com/i35spWx.png + +Definition of GUI option 2 ("Remove empty lines"): +- delete empty lines by completely removing them. This will most often be separator lines between blocks of code, added by the developer's preference over the lifespan of a project. + +[Example] See below image for the effects of this option: + +[img] https://i.imgur.com/KGGnhz8.png + + +The stable, multi-threaded approach and progress bar, should allow you to work on an high amount of files simultaneously and large repositories. + +This is freeware and free to use, distribute, compilate and modify on private and corporate level. No further updates will be commited by myself, although you're free to submit Pull requests. + +GUI interface: + +https://i.imgur.com/N5dEWo3.png + +Processing the 1100 files in above example GUI image, costed just under 3 seconds. \ No newline at end of file diff --git a/SpaceTrimmer.iml b/SpaceTrimmer.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/SpaceTrimmer.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..a039fd1 --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: nl.dutchman.spacetrimmer.launcher.Launcher + diff --git a/src/nl/dutchman/spacetrimmer/launcher/Launcher.java b/src/nl/dutchman/spacetrimmer/launcher/Launcher.java new file mode 100644 index 0000000..d78abcc --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/launcher/Launcher.java @@ -0,0 +1,19 @@ +package nl.dutchman.spacetrimmer.launcher; + +import nl.dutchman.spacetrimmer.ui.WindowController; + +public class Launcher +{ + public static void main(String[] args) + { + try + { + WindowController windowController = new WindowController(); + windowController.display(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/src/nl/dutchman/spacetrimmer/model/ProcessableFile.java b/src/nl/dutchman/spacetrimmer/model/ProcessableFile.java new file mode 100644 index 0000000..3e99cdc --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/model/ProcessableFile.java @@ -0,0 +1,18 @@ +package nl.dutchman.spacetrimmer.model; + +import java.io.File; + +public class ProcessableFile +{ + private File file; + private long fileSize; + + public ProcessableFile(File file, long fileSize) + { + this.file = file; + this.fileSize = fileSize; + } + + public File getFile() { return file; } + public long getFileSize() { return fileSize; } +} diff --git a/src/nl/dutchman/spacetrimmer/resources/icon.png b/src/nl/dutchman/spacetrimmer/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d41d71e618cbf51da16fd4a6bb9c53a26858d6ff GIT binary patch literal 3207 zcmd5&`quY7 z&(7N74;UF*8bTltBcDxPfe;83bfFN$EYOe=u0?29iGhG8q^i^EEx3S3diZ%jAUBz_ zKZofP?BdU$4osDi;w=nV{m`s%&1MnlhoU)G-Ziwxq#A9UDT(rk<_Kgr&R5`B#+PJ zvv_5NZ*~dn&-V!;1@NZCy%d%B?eipS==-Uf#?(8i5M`N2FYLu`d}6Ju9&u8+R#S@i7tB0rGy~@j0D5($Y1i6I zn-{>PvSWl?0KKBpG)`a+t~L@t6kUmw#Hewh#yOW3{n&)TzOPyHuH!UJDsNF^UGy-1 zwvE*D_E)nR$k;c`Mhv#GX31EjW=rfK6Xag5nUh+n5`MRm)nt@vPlInfx&5$B$7#KF z{hY}Y02v;&bn5j1EjaBnhCsSh!lQ3raW}>dEq{Q)4%E!~oKidQ;mcV(8y7v8pY44@ z1d(AC+c6D-d=QM(g2z5P(%`eqgAE7cF=;zN-U(a%1P;^&Zp~mc7lMgsCWcHt| z6bJUmP{*Vxj{*ZM9sZs27%*U}bK{lA!KjuHcfmE%0LP@kjTeJT%3exOUd--sgjGCg zB}@9OtsK?sA~kd7a{|$==du?i|4W$%4A8a0=w0|^@&-O89zwa|o;R{f(L*uPXY#*k zDbx$`548|?L9dP@T)at(S>Z|ibmWT%m>XRjPjveoybW?x>YfRZZ73P;7Yp0_l7luh zVaAS9!rd>bL-~)Cw!Z}6VBo4?`0lc5wug}!lW#3OK_~4~mAJQJZe()ip!<5V6}>uS z-z!7FN z*qC9N=uzWP9qRexF+@l6_xGWZ`{c60XBkfTip6i4{63;Q1%9x9fhFx!J1#cB9Jh9P zoc*P{utkUGW)mknN&N$6@q5VR>e@I_QAOMy_UgFZT+Mix<^wrw)q0>?k-2{J*N7x-v~Y5DNw`mH_=4|DOpOSNNLbggvZU<>NX zN$ueN$^%E2w0u>Rwu0+};I>uGEp$B6&nXE?01t>T%lEuTZ2#6(+(2~Zu-8%n8aKM4 z5Zw8uq{}D4;;E;6wd3?hY4kV!pw0FrnJXu!ank(XN*@jO#{7?8^_Mnp9y&wY!!OC4 z@*S1r0KeP!J@a$_vXS_Inn$0f8^ABgtm39SSpV+Oq-}Hj8_v^zZ{DB0IKFT>H(3rl zWI=v+aMI~WKZ!SS(bX86%$o+M!cM}8VZw4k6%7$A_QXi^M`!+viP7j;+>nElw|-^$ zS61{&A^U{&)e8q@6AqEfgX8YSnZR?lDN=PpVXv7TiTf?{w@BL20(tPeeEQAxDQZ0v z_W*(?HA;TWbslytZ@8W(L?%4V8}lZ3rJJdCF)ZrE<08$cNp0FFU@E9}AQEc96A`A{ z%fJeI&D0HtSh&uMY!a8!YHe&$CmsR$4gaCX51|VrcGa7cr*@cPp5jIIuA&Lq2Jn&{ zLCkgl&V5!lS`tk7`#;!C`geW&G32o>_4|&kqj+AOTMxI1s%KFvekl$Yj?2PzD*Fzr z*9=J|>FfbXU9`#Uj(V|3943rO@o;$Pk8f^u$V?d!BoYgV!PR{*n6jK&OBE*VblX1T zgKx2QXjPrexRYH_$xg-5Ro(dB`l0nM()rIX)$Fs1c4M#4d#;gZQM+-=*h1CPC1>q< z1w1ydq^jP*EiUi6y0i>682pXF!3uT0uSRnnmmK^4=Hj-A&WX_pU<$dt-Uu`GSFo+sXcv#Ym`feDX5mhuK8bS?+CZhUF#N+ayLu-nX&z`wzte^7} zu^dl{GT3=xodwpsy?kBDj^6wRajIjIBN;jB)KxAU+!~d6utAk=Y=8_^yjPavdWfIy zpC=5_`CBFw5eqeGPmF~cXMPtP8$>jcd?dv5noXC%rq#zyS9I661x%Z)87ZDo{Nr=o z+w|-<6w3Kq?T4jyrtgSq9U&~D-BFcN_P~pGD)uVAO7E`TjGxLDbg`fMX#}1#!t4pj z*EG$7Ts?zp)N<;-sTI^pY86%C{hUHik)6}P#*yn-+q%1Fz@G_-kGH>9l_&Y|&wOvO AasU7T literal 0 HcmV?d00001 diff --git a/src/nl/dutchman/spacetrimmer/resources/processing.gif b/src/nl/dutchman/spacetrimmer/resources/processing.gif new file mode 100644 index 0000000000000000000000000000000000000000..5331edb5a7441c93e8f56f6bb80dd8649fd8488a GIT binary patch literal 25126 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 W_65Fufp1^n+ZXuu1^$2e0{;s|5foMc literal 0 HcmV?d00001 diff --git a/src/nl/dutchman/spacetrimmer/resources/ready.png b/src/nl/dutchman/spacetrimmer/resources/ready.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2bbec8ccf01d2cc6853857a38cc9fb41ec7c6d GIT binary patch literal 764 zcmXw%eJs>*9LL{^B|4$VNh{NI9x7U^Saftkab3bZ%tPW7a?;(9w21H{4{4mIxtoU* zM`dKuAIX$WOp_L=Kd6SxbXmxazP_#R-rx7<^?CT~^Zn)u`0gwnQyl;-PY<3CiTgIL z_GaR|I_5Q?;V$sa&UKHv!m06*XY5Ca4NSAZZu2qXg6fD|AW2m!7G z2|zTE1Y`gbKn%pufmGlQ@Bql9t#lxbc1e*;W&!sp6ajbXWDbxAFaUvkpajSTiYXKT z4*}w&SVoz+6ey)>1yBXZfEqwfy%tbV)dLN{KW_vc0Zo9Cwwful(BfmttpEX{HsA>r z0it%G19GQ* zM?Dg3=I8r@Cz$Tw^qn`?*4dW2=Mtku(}|ViEui}O9KZMyjT=t{nvR<4blT63n$ymm=tD-Gu+&HQvD?LUgl_dnr z8q?}LxR+Xds*F$D^UiP2P}yp)=0^;=a&1R-4HU_-nPEL%6~~QGmCEc_M_An+v(hr= z#P3-aD`p&KMQIH=y@$KJYknANo;aWu8Je?_)Hk9mls3I#BX;Feh1o}>UzNXtHwlLv qoXQ literal 0 HcmV?d00001 diff --git a/src/nl/dutchman/spacetrimmer/ui/MainWindow.form b/src/nl/dutchman/spacetrimmer/ui/MainWindow.form new file mode 100644 index 0000000..eecae88 --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/ui/MainWindow.form @@ -0,0 +1,229 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/nl/dutchman/spacetrimmer/ui/MainWindow.java b/src/nl/dutchman/spacetrimmer/ui/MainWindow.java new file mode 100644 index 0000000..6caaaea --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/ui/MainWindow.java @@ -0,0 +1,64 @@ +package nl.dutchman.spacetrimmer.ui; + +import javax.swing.*; +import java.awt.*; + +public class MainWindow extends JFrame +{ + public static final int WIDTH = 800, HEIGHT = 600; + + private JPanel contentPane; + private JPanel consoleContainer; + private JScrollPane consoleScrollContainer; + private JTextPane consoleArea; + private JPanel interfaceContainer; + private JPanel directoryContainer; + private JPanel optionsContainer; + private JPanel utilityContainer; + private JButton directoryButton; + private JTextField directoryField; + private JPanel processContainer; + private JButton processButton; + private JPanel progressContainer; + private JPanel statusContainer; + private JLabel statusLabel; + private JLabel statusIcon; + private JLabel threadsLabel; + private JLabel currentLabel; + private JProgressBar progressBar; + private JCheckBox endTrimCheckbox; + private JCheckBox emptyLineCheckBox; + private JPanel fileFormatContainer; + private JLabel fileFormatLabel; + private JTextField fileFormatField; + + public JTextPane getConsoleArea() { return consoleArea; } + public JButton getDirectoryButton() { return directoryButton; } + public JTextField getDirectoryField() { return directoryField; } + public JButton getProcessButton() { return processButton; } + public JLabel getStatusLabel() { return statusLabel; } + public JLabel getStatusIcon() { return statusIcon; } + public JLabel getThreadsLabel() { return threadsLabel; } + public JLabel getCurrentLabel() { return currentLabel; } + public JProgressBar getProgressBar() { return progressBar; } + public JCheckBox getEndTrimCheckbox() { return endTrimCheckbox; } + public JCheckBox getEmptyLineCheckBox() { return emptyLineCheckBox; } + public JTextField getFileFormatField() { return fileFormatField; } + + public MainWindow() + { + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setSize(WIDTH, HEIGHT); + this.setLocationRelativeTo(null); + this.setContentPane(contentPane); + this.setIconImage(Toolkit.getDefaultToolkit().getImage(MainWindow.class.getResource("/nl/dutchman/spacetrimmer/resources/icon.png"))); + this.setTitle("Space Trimmer - Remove trailing spaces/empty lines"); + this.setResizable(false); + + this.consoleArea.setFont(new Font("Consolas", Font.PLAIN, 12)); + this.consoleArea.setEditable(false); + + this.endTrimCheckbox.setSelected(true); + this.emptyLineCheckBox.setSelected(true); + } +} diff --git a/src/nl/dutchman/spacetrimmer/ui/WindowController.java b/src/nl/dutchman/spacetrimmer/ui/WindowController.java new file mode 100644 index 0000000..c9eeb75 --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/ui/WindowController.java @@ -0,0 +1,312 @@ +package nl.dutchman.spacetrimmer.ui; + +import nl.dutchman.spacetrimmer.model.ProcessableFile; +import nl.dutchman.spacetrimmer.utils.DialogManager; +import nl.dutchman.spacetrimmer.utils.MessageConsole; +import nl.dutchman.spacetrimmer.utils.ProcessInformation; + +import javax.swing.*; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DocumentFilter; +import java.awt.*; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.DecimalFormat; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class WindowController +{ + private MainWindow mainWindow; + private DialogManager dialogManager; + private int threadsToUse; + private ProcessInformation processInformation; + private ProcessInformation[] stepInformation = new ProcessInformation[2]; + private int filesToProcess, filesProcessed; + private long lengthToProcess, lengthProcessed; + + public WindowController() + { + threadsToUse = Runtime.getRuntime().availableProcessors(); + dialogManager = new DialogManager(); + setLookAndFeel(); + initializeComponents(); + initializeListeners(); + } + + private void setStatus(int step) + { + if (step == 0) + { + filesProcessed = 0; + filesToProcess = 0; + lengthProcessed = 0; + lengthToProcess = 0; + this.mainWindow.getProgressBar().setValue(0); + this.mainWindow.getStatusIcon().setIcon(new ImageIcon(WindowController.class.getResource("/nl/dutchman/spacetrimmer/resources/processing.gif"))); + this.mainWindow.getStatusLabel().setText("Status: PROCESSING"); + this.mainWindow.getThreadsLabel().setText("Analyzing directory"); + this.mainWindow.getCurrentLabel().setText("Processed 0,0 kB of 0,0 kB. Files processed: 0 out of 0"); + this.mainWindow.getProcessButton().setEnabled(false); + } + else if (step == 1) + this.mainWindow.getThreadsLabel().setText("Processing in parallel mode. Threads being used: " + threadsToUse); + else if (step == -1) + { + this.mainWindow.getStatusIcon().setIcon(new ImageIcon(WindowController.class.getResource("/nl/dutchman/spacetrimmer/resources/ready.png"))); + this.mainWindow.getStatusLabel().setText("Status: READY"); + this.mainWindow.getThreadsLabel().setText("Currently idle. Awaiting for input"); + this.mainWindow.getProcessButton().setEnabled(true); + } + } + + private void initializeProcess(String directory, String extensionStr, boolean trimSpaces, boolean trimLines) + { + setStatus(0); + + processInformation = new ProcessInformation(); + processInformation.start(); + System.out.println("Process starting at " + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalTime.now())); + + boolean noExtensions = extensionStr == null || extensionStr.trim().isEmpty(); + List extensions; + if (noExtensions) + extensions = new ArrayList(); // empty list, just in case, to avoid nullptr + else + { + extensions = Arrays.asList(extensionStr.split(",")); + } + + ExecutorService processService = Executors.newSingleThreadExecutor(); + processService.submit(() -> { + try + { + + BiFunction, Boolean> endsWithAny = (dirName, exts) -> { + for (String ext : exts) + { + if (dirName.endsWith(ext)) + return true; + } + return false; + }; + System.out.println("Step 0 --> Gathering files to modify"); + stepInformation[0] = new ProcessInformation(); + stepInformation[0].start(); + int c = 0; + List processableFiles = Files.walk(Paths.get(directory)). + map(p -> new ProcessableFile(p.toFile(), p.toFile().length())). + filter(p -> !p.getFile().isDirectory()). + filter(noExtensions ? p -> true : p -> endsWithAny.apply(p.getFile().getAbsolutePath(), extensions)). + collect(Collectors.toList()); + stepInformation[0].end(); + + filesToProcess = processableFiles.size(); + lengthToProcess = processableFiles.stream().map(p -> p.getFileSize()).mapToInt(i -> i.intValue()).sum(); + + System.out.println("Step 0 --> Files gathered in " + stepInformation[0].getTime() + "ms"); + + ExecutorService executorService = Executors.newFixedThreadPool(threadsToUse); + + setStatus(1); + + System.out.println("Step 1 --> Processing files. This may take a while..."); + System.out.println("Settings >- trimSpaces: " + trimSpaces + " | trimLines: " + trimLines); + this.mainWindow.getCurrentLabel().setText("Processed 0.0 kB of " + readableFileSize(lengthToProcess) + ". Files processed: 0 out of " + filesToProcess); + stepInformation[1] = new ProcessInformation(); + stepInformation[1].start(); + + for (ProcessableFile file : processableFiles) + { + executorService.submit(() -> { + try + { + List lineFiles = new ArrayList<>(); + for (String line : Files.readAllLines(file.getFile().toPath(), StandardCharsets.ISO_8859_1)) + { + if (trimSpaces) + line = line.replaceAll("\\s+$", ""); + if (!trimLines || (trimLines && line != null && !line.trim().isEmpty())) + lineFiles.add(line); + } + Files.write(file.getFile().toPath(), lineFiles); + } + catch (IOException e) + { + System.err.println("Could not process " + file.getFile().getAbsolutePath()); + e.printStackTrace(); + } + + synchronized (this) + { + filesProcessed++; + lengthProcessed += file.getFileSize(); + + this.mainWindow.getProgressBar().setValue((int) (lengthProcessed / (lengthToProcess / 100))); + this.mainWindow.getCurrentLabel().setText("Processed " + readableFileSize(lengthProcessed) + " of " + + readableFileSize(lengthToProcess) + ". Files processed: " + filesProcessed + " out of " + filesToProcess); + } + }); + } + + executorService.shutdown(); + + try + { + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } + catch (InterruptedException e) + { + dialogManager.displayError("ERR5 - Awaiting process", "The process has been interrupted"); + e.printStackTrace(); + } + finally + { + stepInformation[1].end(); + System.out.println("Step 1 --> Files processed in " + stepInformation[1].getTime() + "ms"); + processInformation.end(); + System.out.println("Process finished succesfully in " + processInformation.getTime() + "ms"); + } + } + catch (IOException e) + { + e.printStackTrace(); + dialogManager.displayError("ERR4 - Process Error", "Could not finish processing.\n" + + "Please check console for further information"); + return; + } + finally + { + setStatus(-1); + dialogManager.displayInfo("INF1 - Finished process", "Finish processing " + filesProcessed + " files"); + } + }); + } + + private String readableFileSize(long size) + { + if(size <= 0) return "0"; + final String[] units = new String[] { "B", "kB", "MB", "GB", "TB" }; + int digitGroups = (int) (Math.log10(size)/Math.log10(1024)); + return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups)) + " " + units[digitGroups]; + } + + private void setLookAndFeel() + { + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException | ClassNotFoundException e) + { + dialogManager.displayError("ERR1 - Look and Feel", "Could not set application's look and feel"); + e.printStackTrace(); + } + } + + public void display() + { + this.mainWindow.setVisible(true); + } + + private void initializeComponents() + { + this.mainWindow = new MainWindow(); + MessageConsole console = new MessageConsole(this.mainWindow.getConsoleArea()); + console.redirectOut(null, System.out); + console.redirectErr(Color.RED, null); + console.setMessageLines(1000); + } + + private void initializeListeners() + { + setExtensionsDocumentFilter(); + addFileChooserListener(); + addProcessListener(); + } + + private void setExtensionsDocumentFilter() + { + Pattern extensionPattern = Pattern.compile("[a-zA-Z0-9,]*"); + AbstractDocument document = (AbstractDocument) this.mainWindow.getFileFormatField().getDocument(); + document.setDocumentFilter(new DocumentFilter(){ + @Override + public void replace(DocumentFilter.FilterBypass byPass, int offset, int length, String text, AttributeSet attributes) throws BadLocationException + { + Matcher matcher = extensionPattern.matcher(text); + if (matcher.matches()) + super.replace(byPass, offset, length, text, attributes); + } + }); + } + + private void addFileChooserListener() + { + JButton fileChooserButton = mainWindow.getDirectoryButton(); + fileChooserButton.addActionListener(actionListener -> { + JTextField fileChooserField = mainWindow.getDirectoryField(); + String startPath = (!fileChooserField.getText().isEmpty() && + Files.isDirectory(Paths.get(fileChooserField.getText()))) ? + fileChooserField.getText() : System.getProperty("user.home"); + JFileChooser fileChooser = new JFileChooser(startPath); + fileChooser.setDialogTitle("Select a directory"); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (fileChooser.showDialog(mainWindow, "Select directory") == JFileChooser.APPROVE_OPTION) + { + fileChooserField.setText(fileChooser.getSelectedFile().getAbsolutePath()); + } + }); + } + + private void addProcessListener() + { + JButton processButton = mainWindow.getProcessButton(); + processButton.addActionListener(actionListener -> { + JTextField fileChooserField = mainWindow.getDirectoryField(); + JTextField fileExtensionField = mainWindow.getFileFormatField(); + String selectedDirectory = fileChooserField.getText(); + String selectedExtensions = fileExtensionField.getText(); + boolean trimSpaces = mainWindow.getEndTrimCheckbox().isSelected(); + boolean trimLines = mainWindow.getEmptyLineCheckBox().isSelected(); + + // Cleaning up empty extensions here. + while (selectedExtensions.contains(",,")) + selectedExtensions = selectedExtensions.replace(",,", ","); + if (selectedExtensions.startsWith(",")) + selectedExtensions = selectedExtensions.substring(1); + if (selectedExtensions.endsWith(",")) + selectedExtensions = selectedExtensions.substring(0, selectedExtensions.length() - 1); + + if (selectedDirectory == null || selectedDirectory.trim().isEmpty()) + { + dialogManager.displayError("ERR2 - No directory", "No directory has been selected"); + return; + } + if (!Files.isDirectory(Paths.get(selectedDirectory))) + { + dialogManager.displayError("ERR3 - Invalid directory", "Selected directory is invalid"); + return; + } + if (selectedExtensions == null || selectedExtensions.trim().isEmpty()) + { + dialogManager.displayError("ERR6 - No extensions selected", "No extensions have been selected."); + return; + } + initializeProcess(selectedDirectory, selectedExtensions, trimSpaces, trimLines); + }); + } +} diff --git a/src/nl/dutchman/spacetrimmer/utils/DialogManager.java b/src/nl/dutchman/spacetrimmer/utils/DialogManager.java new file mode 100644 index 0000000..8dea7c2 --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/utils/DialogManager.java @@ -0,0 +1,28 @@ +package nl.dutchman.spacetrimmer.utils; + +import javax.swing.*; +import java.awt.*; + +public class DialogManager +{ + public void displayInfo(String title, String message) + { + displayFrame(title, message, JOptionPane.INFORMATION_MESSAGE); + } + + public void displayWarning(String title, String message) + { + displayFrame(title, message, JOptionPane.WARNING_MESSAGE); + } + + public void displayError(String title, String message) + { + displayFrame(title, message, JOptionPane.ERROR_MESSAGE); + } + + private void displayFrame(String title, String message, int msgType) + { + Toolkit.getDefaultToolkit().beep(); + JOptionPane.showMessageDialog(null, message, title, msgType); + } +} diff --git a/src/nl/dutchman/spacetrimmer/utils/LimitLinesDocumentListener.java b/src/nl/dutchman/spacetrimmer/utils/LimitLinesDocumentListener.java new file mode 100644 index 0000000..bfbd93d --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/utils/LimitLinesDocumentListener.java @@ -0,0 +1,108 @@ +package nl.dutchman.spacetrimmer.utils; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; + +/** + * From http://tips4java.wordpress.com/2008/10/15/limit-lines-in-document/ + * + * @author Rob Camick + */ +public class LimitLinesDocumentListener implements DocumentListener { + private int maximumLines; + private boolean isRemoveFromStart; + + /** + * Specify the number of lines to be stored in the Document. Extra lines + * will be removed from the start or end of the Document, depending on + * the boolean value specified. + * + * @param maximumLines number of lines + * @param isRemoveFromStart true to remove from the start + */ + public LimitLinesDocumentListener(int maximumLines, + boolean isRemoveFromStart) { + setLimitLines(maximumLines); + this.isRemoveFromStart = isRemoveFromStart; + } + + /** + * Set the maximum number of lines to be stored in the Document + * + * @param maximumLines number of lines + */ + public void setLimitLines(int maximumLines) { + if (maximumLines < 1) { + throw new IllegalArgumentException("Maximum lines must be greater than 0"); + } + + this.maximumLines = maximumLines; + } + + @Override + public void insertUpdate(final DocumentEvent e) { + // Changes to the Document can not be done within the listener + // so we need to add the processing to the end of the EDT + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + removeLines(e); + } + }); + } + + @Override + public void removeUpdate(DocumentEvent e) { + } + + @Override + public void changedUpdate(DocumentEvent e) { + } + + private void removeLines(DocumentEvent e) { + // The root Element of the Document will tell us the total number + // of line in the Document. + + Document document = e.getDocument(); + Element root = document.getDefaultRootElement(); + + while (root.getElementCount() > maximumLines) { + if (isRemoveFromStart) { + removeFromStart(document, root); + } else { + removeFromEnd(document, root); + } + } + } + + private void removeFromStart(Document document, Element root) { + Element line = root.getElement(0); + int end = line.getEndOffset(); + + try { + document.remove(0, end); + } catch (BadLocationException ble) { + System.out.println(ble); + } + } + + private void removeFromEnd(Document document, Element root) { + // We use start minus 1 to make sure we remove the newline + // character of the previous line + + Element line = root.getElement(root.getElementCount() - 1); + int start = line.getStartOffset(); + int end = line.getEndOffset(); + + try { + document.remove(start - 1, end - start); + } catch (BadLocationException ble) { + System.out.println(ble); + } + } +} \ No newline at end of file diff --git a/src/nl/dutchman/spacetrimmer/utils/MessageConsole.java b/src/nl/dutchman/spacetrimmer/utils/MessageConsole.java new file mode 100644 index 0000000..d965c84 --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/utils/MessageConsole.java @@ -0,0 +1,243 @@ +package nl.dutchman.spacetrimmer.utils; + +import java.io.*; +import java.awt.*; +import javax.swing.event.*; +import javax.swing.text.*; + +/* + * Create a simple console to display text messages. + * + * Messages can be directed here from different sources. Each source can + * have its messages displayed in a different color. + * + * Messages can either be appended to the console or inserted as the first + * line of the console + * + * You can limit the number of lines to hold in the Document. + */ +public class MessageConsole +{ + private JTextComponent textComponent; + private Document document; + private boolean isAppend; + private DocumentListener limitLinesListener; + + public MessageConsole(JTextComponent textComponent) + { + this(textComponent, true); + } + + /* + * Use the text component specified as a simply console to display + * text messages. + * + * The messages can either be appended to the end of the console or + * inserted as the first line of the console. + */ + public MessageConsole(JTextComponent textComponent, boolean isAppend) + { + this.textComponent = textComponent; + this.document = textComponent.getDocument(); + this.isAppend = isAppend; + textComponent.setEditable( false ); + } + + /* + * Redirect the output from the standard output to the console + * using the default text color and null PrintStream + */ + public void redirectOut() + { + redirectOut(null, null); + } + + /* + * Redirect the output from the standard output to the console + * using the specified color and PrintStream. When a PrintStream + * is specified the message will be added to the Document before + * it is also written to the PrintStream. + */ + public void redirectOut(Color textColor, PrintStream printStream) + { + ConsoleOutputStream cos = new ConsoleOutputStream(textColor, printStream); + System.setOut( new PrintStream(cos, true) ); + } + + /* + * Redirect the output from the standard error to the console + * using the default text color and null PrintStream + */ + public void redirectErr() + { + redirectErr(null, null); + } + + /* + * Redirect the output from the standard error to the console + * using the specified color and PrintStream. When a PrintStream + * is specified the message will be added to the Document before + * it is also written to the PrintStream. + */ + public void redirectErr(Color textColor, PrintStream printStream) + { + ConsoleOutputStream cos = new ConsoleOutputStream(textColor, printStream); + System.setErr( new PrintStream(cos, true) ); + } + + /* + * To prevent memory from being used up you can control the number of + * lines to display in the console + * + * This number can be dynamically changed, but the console will only + * be updated the next time the Document is updated. + */ + public void setMessageLines(int lines) + { + if (limitLinesListener != null) + document.removeDocumentListener( limitLinesListener ); + + limitLinesListener = new LimitLinesDocumentListener(lines, isAppend); + document.addDocumentListener( limitLinesListener ); + } + + /* + * Class to intercept output from a PrintStream and add it to a Document. + * The output can optionally be redirected to a different PrintStream. + * The text displayed in the Document can be color coded to indicate + * the output source. + */ + class ConsoleOutputStream extends ByteArrayOutputStream + { + private final String EOL = System.getProperty("line.separator"); + private SimpleAttributeSet attributes; + private PrintStream printStream; + private StringBuffer buffer = new StringBuffer(1000); + private boolean isFirstLine; + + /* + * Specify the option text color and PrintStream + */ + public ConsoleOutputStream(Color textColor, PrintStream printStream) + { + if (textColor != null) + { + attributes = new SimpleAttributeSet(); + StyleConstants.setForeground(attributes, textColor); + } + + this.printStream = printStream; + + if (isAppend) + isFirstLine = true; + } + + /* + * Override this method to intercept the output text. Each line of text + * output will actually involve invoking this method twice: + * + * a) for the actual text message + * b) for the newLine string + * + * The message will be treated differently depending on whether the line + * will be appended or inserted into the Document + */ + public void flush() + { + String message = toString(); + + if (message.length() == 0) return; + + if (isAppend) + handleAppend(message); + else + handleInsert(message); + + reset(); + } + + /* + * We don't want to have blank lines in the Document. The first line + * added will simply be the message. For additional lines it will be: + * + * newLine + message + */ + private void handleAppend(String message) + { + // This check is needed in case the text in the Document has been + // cleared. The buffer may contain the EOL string from the previous + // message. + + if (document.getLength() == 0) + buffer.setLength(0); + + if (EOL.equals(message)) + { + buffer.append(message); + } + else + { + buffer.append(message); + clearBuffer(); + } + + } + /* + * We don't want to merge the new message with the existing message + * so the line will be inserted as: + * + * message + newLine + */ + private void handleInsert(String message) + { + buffer.append(message); + + if (EOL.equals(message)) + { + clearBuffer(); + } + } + + /* + * The message and the newLine have been added to the buffer in the + * appropriate order so we can now update the Document and send the + * text to the optional PrintStream. + */ + private void clearBuffer() + { + // In case both the standard out and standard err are being redirected + // we need to insert a newline character for the first line only + + if (isFirstLine && document.getLength() != 0) + { + buffer.insert(0, "\n"); + } + + isFirstLine = false; + String line = buffer.toString(); + + try + { + if (isAppend) + { + int offset = document.getLength(); + document.insertString(offset, line, attributes); + textComponent.setCaretPosition( document.getLength() ); + } + else + { + document.insertString(0, line, attributes); + textComponent.setCaretPosition( 0 ); + } + } + catch (BadLocationException ble) {} + + if (printStream != null) + { + printStream.print(line); + } + + buffer.setLength(0); + } + } +} diff --git a/src/nl/dutchman/spacetrimmer/utils/ProcessInformation.java b/src/nl/dutchman/spacetrimmer/utils/ProcessInformation.java new file mode 100644 index 0000000..4642aa8 --- /dev/null +++ b/src/nl/dutchman/spacetrimmer/utils/ProcessInformation.java @@ -0,0 +1,21 @@ +package nl.dutchman.spacetrimmer.utils; + +public class ProcessInformation +{ + private long startTime, endTime; + + public void start() + { + this.startTime = System.currentTimeMillis(); + } + + public void end() + { + this.endTime = System.currentTimeMillis(); + } + + public long getTime() + { + return this.endTime - this.startTime; + } +}