From f61b084769786797aec1f8cefc8767a60f6de4b5 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Thu, 29 Oct 2009 01:22:00 +0000 Subject: [PATCH] * improved support for move/rename --- .../rename/ExpressionFormatDocument.java | 1 - .../rename/HighlightListCellRenderer.java | 10 ++-- .../ui/panel/rename/HistoryDialog.java | 17 +++++- .../filebot/ui/panel/rename/RenameAction.java | 11 +++- .../ui/panel/rename/ValidateDialog.java | 57 +++++++++++++----- .../net/sourceforge/tuned/FileUtilities.java | 58 +++++++++++++++++-- 6 files changed, 125 insertions(+), 29 deletions(-) diff --git a/source/net/sourceforge/filebot/ui/panel/rename/ExpressionFormatDocument.java b/source/net/sourceforge/filebot/ui/panel/rename/ExpressionFormatDocument.java index 57a3a292..b701ebfb 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/ExpressionFormatDocument.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/ExpressionFormatDocument.java @@ -49,7 +49,6 @@ class ExpressionFormatDocument extends PlainDocument { RoundBrackets("()"), SquareBrackets("[]"), CurlyBrackets("{}"), - RegexLiteral("//"), SingleQuoteStringLiteral("''"), DoubleQuoteStringLiteral("\"\""); diff --git a/source/net/sourceforge/filebot/ui/panel/rename/HighlightListCellRenderer.java b/source/net/sourceforge/filebot/ui/panel/rename/HighlightListCellRenderer.java index c6f1ff61..207beb1e 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/HighlightListCellRenderer.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/HighlightListCellRenderer.java @@ -25,12 +25,12 @@ import net.sourceforge.tuned.ui.TunedUtilities; class HighlightListCellRenderer extends AbstractFancyListCellRenderer { - private final JTextComponent textComponent = new JTextField(); - - private Pattern pattern; - private Highlighter.HighlightPainter highlightPainter; + protected final JTextComponent textComponent = new JTextField(); + protected final Pattern pattern; + protected final Highlighter.HighlightPainter highlightPainter; + public HighlightListCellRenderer(Pattern pattern, Highlighter.HighlightPainter highlightPainter, int padding) { super(new Insets(0, 0, 0, 0)); @@ -84,7 +84,7 @@ class HighlightListCellRenderer extends AbstractFancyListCellRenderer { } } - + private class HighlightUpdateListener implements DocumentListener { @Override diff --git a/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java b/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java index 1cd3b138..1f5ad2ef 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java @@ -531,8 +531,14 @@ class HistoryDialog extends JDialog { File dir = directory != null ? directory : element.dir(); // reverse - File from = new File(dir, element.to()); - File to = new File(dir, element.from()); + File from = new File(element.to()); + File to = new File(element.from()); + + // resolve against given directory or against the original base directory if the path is not absolute + if (!from.isAbsolute()) + from = new File(dir, directory == null ? from.getPath() : from.getName()); + if (!to.isAbsolute()) + to = new File(dir, directory == null ? to.getPath() : to.getName()); renameMap.put(from, to); } @@ -847,7 +853,12 @@ class HistoryDialog extends JDialog { public boolean isBroken(int row) { Element element = data.get(row); - File file = new File(element.dir(), element.to()); + + File file = new File(element.to()); + + // resolve relative path + if (!file.isAbsolute()) + file = new File(element.dir(), file.getPath()); return !file.exists(); } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java b/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java index 24db1e5b..2cc75c82 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java @@ -91,10 +91,15 @@ class RenameAction extends AbstractAction { private File rename(File file, String path) throws IOException { - // same folder, different name - File destination = new File(file.getParentFile(), path); + File destination = new File(path); - // name may be a relative path, so we can't use file.getParentFile() + // resolve destination + if (!destination.isAbsolute()) { + // same folder, different name + destination = new File(file.getParentFile(), path); + } + + // make sure we that we can create the destination folder structure File destinationFolder = destination.getParentFile(); // create parent folder if necessary diff --git a/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java b/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java index 37dc17dd..bcf38d1e 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/ValidateDialog.java @@ -10,11 +10,15 @@ import java.awt.Color; import java.awt.Component; import java.awt.Window; import java.awt.event.ActionEvent; +import java.io.File; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; import javax.swing.AbstractAction; import javax.swing.Action; @@ -25,6 +29,7 @@ import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.KeyStroke; +import javax.swing.text.BadLocationException; import net.miginfocom.swing.MigLayout; import net.sourceforge.filebot.ResourceManager; @@ -47,13 +52,37 @@ class ValidateDialog extends JDialog { list = new JList(model); list.setEnabled(false); - list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4)); + list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4) { + + @Override + protected void updateHighlighter() { + textComponent.getHighlighter().removeAllHighlights(); + + Matcher matcher = pattern.matcher(textComponent.getText()); + File file = new File(textComponent.getText()); + + // highlight path components separately to ignore "illegal characters" that are either path separators or part of the drive letter (e.g. ':' in 'E:') + for (File element : listPath(file)) { + int limit = element.getPath().length(); + matcher.region(limit - element.getName().length(), limit); + + while (matcher.find()) { + try { + textComponent.getHighlighter().addHighlight(matcher.start(0), matcher.end(0), highlightPainter); + } catch (BadLocationException e) { + //should not happen + Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.toString(), e); + } + } + } + } + }); JLabel label = new JLabel("Some names contain invalid characters:"); JComponent content = (JComponent) getContentPane(); - content.setLayout(new MigLayout("insets dialog, nogrid, fill")); + content.setLayout(new MigLayout("insets dialog, nogrid, fill", "", "[pref!][fill][pref!]")); content.add(label, "wrap"); content.add(new JScrollPane(list), "grow, wrap 2mm"); @@ -94,7 +123,8 @@ class ValidateDialog extends JDialog { public void actionPerformed(ActionEvent e) { // validate names for (int i = 0; i < model.length; i++) { - model[i] = validateFileName(model[i]); + // remove illegal characters + model[i] = validateFilePath(new File(model[i])).getPath(); } // update view @@ -126,22 +156,23 @@ class ValidateDialog extends JDialog { public static boolean validate(Component parent, List source) { - IndexView invalid = new IndexView(source); + IndexView invalidFilePaths = new IndexView(source); for (int i = 0; i < source.size(); i++) { - String name = source.get(i); + String path = source.get(i); - if (isInvalidFileName(name)) { - invalid.addIndex(i); + // invalid file names are also invalid file paths + if (isInvalidFilePath(new File(path))) { + invalidFilePaths.addIndex(i); } } - if (invalid.isEmpty()) { - // nothing to do + // check if there is anything to do in the first place + if (invalidFilePaths.isEmpty()) { return true; } - ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalid); + ValidateDialog dialog = new ValidateDialog(getWindow(parent), invalidFilePaths); // show and block dialog.setVisible(true); @@ -151,11 +182,11 @@ class ValidateDialog extends JDialog { return false; } - List valid = dialog.getModel(); + List validatedFilePaths = dialog.getModel(); // validate source list via index view - for (int i = 0; i < invalid.size(); i++) { - invalid.set(i, valid.get(i)); + for (int i = 0; i < invalidFilePaths.size(); i++) { + invalidFilePaths.set(i, validatedFilePaths.get(i)); } return true; diff --git a/source/net/sourceforge/tuned/FileUtilities.java b/source/net/sourceforge/tuned/FileUtilities.java index dd7f83d8..ada7d7bb 100644 --- a/source/net/sourceforge/tuned/FileUtilities.java +++ b/source/net/sourceforge/tuned/FileUtilities.java @@ -9,6 +9,8 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -140,6 +142,17 @@ public final class FileUtilities { } + public static List listPath(File file) { + LinkedList nodes = new LinkedList(); + + for (File node = file; node != null; node = node.getParentFile()) { + nodes.addFirst(node); + } + + return nodes; + } + + public static List listFiles(Iterable folders, int maxDepth) { List files = new ArrayList(); @@ -167,28 +180,65 @@ public final class FileUtilities { /** - * Invalid filename characters: \, /, :, *, ?, ", <, >, |, \r and \n + * Invalid file name characters: \, /, :, *, ?, ", <, >, |, \r and \n */ public static final Pattern ILLEGAL_CHARACTERS = Pattern.compile("[\\\\/:*?\"<>|\\r\\n]"); /** - * Strip filename of invalid characters + * Strip file name of invalid characters * * @param filename original filename - * @return valid filename stripped of invalid characters + * @return valid file name stripped of invalid characters */ public static String validateFileName(CharSequence filename) { - // strip invalid characters from filename + // strip invalid characters from file name return ILLEGAL_CHARACTERS.matcher(filename).replaceAll(""); } public static boolean isInvalidFileName(CharSequence filename) { + // check if file name contains any illegal characters return ILLEGAL_CHARACTERS.matcher(filename).find(); } + public static File validateFileName(File file) { + // windows drives (e.g. c:, d:, etc.) are never invalid because name will be an empty string + if (!isInvalidFileName(file.getName())) + return file; + + // validate file name only + return new File(file.getParentFile(), validateFileName(file.getName())); + } + + + public static File validateFilePath(File path) { + Iterator nodes = listPath(path).iterator(); + + // initialize with root node, keep original root object if possible (so we don't loose the drive on windows) + File validatedPath = validateFileName(nodes.next()); + + // validate the rest of the path + while (nodes.hasNext()) { + validatedPath = new File(validatedPath, validateFileName(nodes.next().getName())); + } + + return validatedPath; + } + + + public static boolean isInvalidFilePath(File path) { + // check if file name contains any illegal characters + for (File node = path; node != null; node = node.getParentFile()) { + if (isInvalidFileName(node.getName())) + return true; + } + + return false; + } + + public static final long KILO = 1024; public static final long MEGA = KILO * 1024; public static final long GIGA = MEGA * 1024;