diff --git a/source/net/sourceforge/filebot/resources/dialog.default.png b/source/net/sourceforge/filebot/resources/dialog.default.png new file mode 100644 index 00000000..16db2211 Binary files /dev/null and b/source/net/sourceforge/filebot/resources/dialog.default.png differ diff --git a/source/net/sourceforge/filebot/ui/EpisodeFormatDialog.java b/source/net/sourceforge/filebot/ui/EpisodeFormatDialog.java new file mode 100644 index 00000000..9ef4d1a7 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/EpisodeFormatDialog.java @@ -0,0 +1,432 @@ + +package net.sourceforge.filebot.ui; + + +import static java.awt.Font.BOLD; +import static java.awt.Font.MONOSPACED; +import static java.awt.Font.PLAIN; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.text.Format; +import java.text.ParseException; +import java.util.Arrays; +import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.script.ScriptException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.InputVerifier; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.DefaultFormatterFactory; + +import net.miginfocom.swing.MigLayout; +import net.sourceforge.filebot.ResourceManager; +import net.sourceforge.filebot.Settings; +import net.sourceforge.filebot.web.Episode; +import net.sourceforge.filebot.web.Episode.EpisodeFormat; +import net.sourceforge.tuned.ExceptionUtilities; +import net.sourceforge.tuned.ui.GradientStyle; +import net.sourceforge.tuned.ui.LinkButton; +import net.sourceforge.tuned.ui.TunedUtilities; +import net.sourceforge.tuned.ui.notification.SeparatorBorder; +import net.sourceforge.tuned.ui.notification.SeparatorBorder.Position; + + +public class EpisodeFormatDialog extends JDialog { + + private Format selectedFormat = null; + + protected JFormattedTextField preview = new JFormattedTextField(getPreviewSample()); + + protected JLabel errorMessage = new JLabel(ResourceManager.getIcon("dialog.cancel")); + protected JTextField editor = new JTextField(); + + protected Color defaultColor = preview.getForeground(); + protected Color errorColor = Color.red; + + + public EpisodeFormatDialog(Window owner) { + super(owner, "Episode Format", ModalityType.DOCUMENT_MODAL); + + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + editor.setFont(new Font(MONOSPACED, PLAIN, 14)); + editor.setText(Settings.userRoot().get("dialog.format")); + + preview.setBorder(BorderFactory.createEmptyBorder()); + + // update preview to current format + checkEpisodeFormat(); + + // bold title label in header + JLabel title = new JLabel(this.getTitle()); + title.setFont(title.getFont().deriveFont(BOLD)); + + // status.setVisible(false); + JPanel header = new JPanel(new MigLayout("insets dialog, nogrid, fillx")); + + header.setBackground(Color.white); + header.setBorder(new SeparatorBorder(1, new Color(0xB4B4B4), new Color(0xACACAC), GradientStyle.LEFT_TO_RIGHT, Position.BOTTOM)); + + header.add(title, "wrap unrel:push"); + header.add(errorMessage, "gap indent, hidemode 3"); + header.add(preview, "gap indent, hidemode 3, growx"); + + JPanel content = new JPanel(new MigLayout("insets dialog, nogrid, fill")); + + content.add(editor, "wmin 120px, h 40px!, growx, wrap 8px"); + + content.add(new JLabel("Syntax"), "gap indent+unrel, wrap 0"); + content.add(createSyntaxPanel(), "gapx indent indent, wrap 8px"); + + content.add(new JLabel("Examples"), "gap indent+unrel, wrap 0"); + content.add(createExamplesPanel(), "gapx indent indent, wrap 25px:push"); + + content.add(new JButton(useDefaultFormatAction), "tag left"); + content.add(new JButton(useCustomFormatAction), "tag apply"); + content.add(new JButton(cancelAction), "tag cancel"); + + JComponent pane = (JComponent) getContentPane(); + pane.setLayout(new MigLayout("insets 0, fill")); + + pane.add(header, "h 60px, growx, dock north"); + pane.add(content, "grow"); + + pack(); + setLocation(TunedUtilities.getPreferredLocation(this)); + + TunedUtilities.putActionForKeystroke(pane, KeyStroke.getKeyStroke("released ESCAPE"), cancelAction); + + // update format on change + editor.getDocument().addDocumentListener(new DocumentAdapter() { + + @Override + public void update(DocumentEvent evt) { + checkEpisodeFormat(); + } + }); + + // keep focus on preview, if current text doesn't fit episode format + preview.setInputVerifier(new InputVerifier() { + + @Override + public boolean verify(JComponent input) { + return checkPreviewSample(); + } + }); + + // check edit format on change + preview.getDocument().addDocumentListener(new DocumentAdapter() { + + @Override + public void update(DocumentEvent evt) { + checkPreviewSample(); + } + }); + + // focus editor by default + addWindowFocusListener(new WindowAdapter() { + + @Override + public void windowGainedFocus(WindowEvent e) { + editor.requestFocusInWindow(); + } + }); + } + + + protected JPanel createSyntaxPanel() { + JPanel panel = new JPanel(new MigLayout("fill, nogrid")); + + panel.setBorder(new LineBorder(new Color(0xACA899))); + panel.setBackground(new Color(0xFFFFE1)); + panel.setOpaque(true); + + panel.add(new JLabel(ResourceBundle.getBundle(getClass().getName()).getString("syntax"))); + + return panel; + } + + + protected JPanel createExamplesPanel() { + JPanel panel = new JPanel(new MigLayout("fill, wrap 3")); + + panel.setBorder(new LineBorder(new Color(0xACA899))); + panel.setBackground(new Color(0xFFFFE1)); + panel.setOpaque(true); + + ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName()); + + // sort keys + String[] keys = bundle.keySet().toArray(new String[0]); + Arrays.sort(keys); + + for (String key : keys) { + if (key.startsWith("example")) { + String format = bundle.getString(key); + + LinkButton formatLink = new LinkButton(new ExampleFormatAction(format)); + formatLink.setFont(new Font(MONOSPACED, PLAIN, 11)); + + panel.add(formatLink); + panel.add(new JLabel("...")); + panel.add(new ExampleFormatLabel(format)); + } + } + + return panel; + } + + + protected Episode getPreviewSample() { + String sample = Settings.userRoot().get("dialog.sample"); + + if (sample != null) { + try { + return EpisodeFormat.getInstance().parseObject(sample); + } catch (Exception e) { + Logger.getLogger("global").log(Level.WARNING, e.getMessage(), e); + } + } + + return new Episode("Dark Angel", "3", "1", "Labyrinth"); + } + + + protected boolean checkPreviewSample() { + // check if field is being edited + if (preview.hasFocus()) { + try { + // try to parse text + preview.getFormatter().stringToValue(preview.getText()); + } catch (Exception e) { + preview.setForeground(errorColor); + // failed to parse text + return false; + } + } + + preview.setForeground(defaultColor); + return true; + } + + + protected DefaultFormatterFactory createFormatterFactory(Format display) { + DefaultFormatterFactory factory = new DefaultFormatterFactory(); + + factory.setEditFormatter(new SimpleFormatter(EpisodeFormat.getInstance())); + + if (display != null) { + factory.setDisplayFormatter(new SimpleFormatter(display)); + } + + return factory; + } + + + protected boolean checkEpisodeFormat() { + Exception exception = null; + + try { + Format format = new EpisodeScriptFormat(editor.getText().trim()); + + // check if format produces empty strings + if (format.format(preview.getValue()).trim().isEmpty()) { + throw new IllegalArgumentException("Format must not be empty."); + } + + // update preview + preview.setFormatterFactory(createFormatterFactory(format)); + } catch (Exception e) { + exception = e; + } + + errorMessage.setText(exception != null ? ExceptionUtilities.getRootCauseMessage(exception) : null); + errorMessage.setVisible(exception != null); + + preview.setVisible(exception == null); + editor.setForeground(exception == null ? defaultColor : errorColor); + + return exception == null; + } + + + public Format getSelectedFormat() { + return selectedFormat; + } + + + private void finish(Format format) { + this.selectedFormat = format; + + setVisible(false); + dispose(); + + if (checkEpisodeFormat()) { + Settings.userRoot().put("dialog.format", editor.getText()); + } + + if (checkPreviewSample()) { + Settings.userRoot().put("dialog.sample", preview.getValue().toString()); + } + } + + protected final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) { + + @Override + public void actionPerformed(ActionEvent e) { + finish(null); + } + }; + + protected final Action useDefaultFormatAction = new AbstractAction("Default", ResourceManager.getIcon("dialog.default")) { + + @Override + public void actionPerformed(ActionEvent e) { + finish(EpisodeFormat.getInstance()); + } + }; + + protected final Action useCustomFormatAction = new AbstractAction("Use Format", ResourceManager.getIcon("dialog.continue")) { + + @Override + public void actionPerformed(ActionEvent evt) { + try { + finish(new EpisodeScriptFormat(editor.getText())); + } catch (ScriptException e) { + Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e); + } + } + }; + + + public static Format showDialog(Component parent) { + EpisodeFormatDialog dialog = new EpisodeFormatDialog(parent != null ? SwingUtilities.getWindowAncestor(parent) : null); + + dialog.setVisible(true); + + return dialog.getSelectedFormat(); + } + + + protected class ExampleFormatAction extends AbstractAction { + + public ExampleFormatAction(String format) { + super(format); + } + + + @Override + public void actionPerformed(ActionEvent e) { + editor.setText(getValue(Action.NAME).toString()); + } + } + + + protected class ExampleFormatLabel extends JLabel { + + private final String format; + + + public ExampleFormatLabel(String format) { + this.format = format; + + // initialize text + updateText(preview.getValue()); + + // bind text to preview + preview.addPropertyChangeListener("value", new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + updateText(evt.getNewValue()); + } + }); + } + + + public void updateText(Object episode) { + try { + setText(new EpisodeScriptFormat(format).format(episode)); + setForeground(defaultColor); + } catch (Exception e) { + setText(ExceptionUtilities.getRootCauseMessage(e)); + setForeground(errorColor); + } + } + } + + + protected static class SimpleFormatter extends AbstractFormatter { + + private final Format format; + + + public SimpleFormatter(Format format) { + this.format = format; + } + + + @Override + public String valueToString(Object value) throws ParseException { + return format.format(value); + } + + + @Override + public Object stringToValue(String text) throws ParseException { + return format.parseObject(text); + } + + } + + + protected static class DocumentAdapter implements DocumentListener { + + @Override + public void changedUpdate(DocumentEvent e) { + update(e); + } + + + @Override + public void insertUpdate(DocumentEvent e) { + update(e); + } + + + @Override + public void removeUpdate(DocumentEvent e) { + update(e); + } + + + public void update(DocumentEvent e) { + + } + + } + +} diff --git a/source/net/sourceforge/filebot/ui/EpisodeFormatDialog.properties b/source/net/sourceforge/filebot/ui/EpisodeFormatDialog.properties new file mode 100644 index 00000000..7523490b --- /dev/null +++ b/source/net/sourceforge/filebot/ui/EpisodeFormatDialog.properties @@ -0,0 +1,13 @@ +syntax: { } ... expression, n ... name, s ... season, e ... episode, t ... title + +# basic 1.01 +example[0]: {n} - {s}.{e} - {t} + +# 1x01 +example[1]: {n} - {if (s) s+'x'}{e.pad(2)} + +# S01E01 +example[2]: {n} - {if (s) 'S'+s.pad(2)}E{e.pad(2)} + +# uglyfy name +example[3]: {n.replace(/\\s+/g,'.').toLowerCase()} diff --git a/source/net/sourceforge/filebot/ui/EpisodeScriptFormat.java b/source/net/sourceforge/filebot/ui/EpisodeScriptFormat.java new file mode 100644 index 00000000..b8237c5d --- /dev/null +++ b/source/net/sourceforge/filebot/ui/EpisodeScriptFormat.java @@ -0,0 +1,32 @@ + +package net.sourceforge.filebot.ui; + + +import javax.script.Bindings; +import javax.script.ScriptException; +import javax.script.SimpleBindings; + +import net.sourceforge.filebot.web.Episode; + + +public class EpisodeScriptFormat extends ScriptFormat { + + public EpisodeScriptFormat(String format) throws ScriptException { + super(format); + } + + + @Override + protected Bindings getBindings(Object object) { + Episode episode = (Episode) object; + + Bindings bindings = new SimpleBindings(); + + bindings.put("n", episode.getSeriesName()); + bindings.put("s", episode.getSeasonNumber()); + bindings.put("e", episode.getEpisodeNumber()); + bindings.put("t", episode.getTitle()); + + return bindings; + } +} diff --git a/source/net/sourceforge/filebot/ui/ScriptFormat.global.js b/source/net/sourceforge/filebot/ui/ScriptFormat.global.js new file mode 100644 index 00000000..26d26ffa --- /dev/null +++ b/source/net/sourceforge/filebot/ui/ScriptFormat.global.js @@ -0,0 +1,16 @@ + +String.prototype.pad = function(length, padding) { + if (padding == undefined) { + padding = '0'; + } + + var s = this; + + if (parseInt(this) >= 0 && padding.length >= 1) { + while (s.length < length) { + s = padding.concat(s) + } + } + + return s; +}; diff --git a/source/net/sourceforge/filebot/ui/ScriptFormat.java b/source/net/sourceforge/filebot/ui/ScriptFormat.java new file mode 100644 index 00000000..e4db9aed --- /dev/null +++ b/source/net/sourceforge/filebot/ui/ScriptFormat.java @@ -0,0 +1,113 @@ + +package net.sourceforge.filebot.ui; + + +import java.io.InputStreamReader; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + + +public abstract class ScriptFormat extends Format { + + private final String format; + + private final Object[] expressions; + + + public ScriptFormat(String format) throws ScriptException { + this.format = format; + this.expressions = compile(format, (Compilable) initScriptEngine()); + } + + + protected ScriptEngine initScriptEngine() throws ScriptException { + ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); + + engine.eval(new InputStreamReader(getClass().getResourceAsStream("ScriptFormat.global.js"))); + + return engine; + } + + + public String getFormat() { + return format; + } + + + protected Object[] compile(String format, Compilable engine) throws ScriptException { + List expression = new ArrayList(); + + Matcher matcher = Pattern.compile("\\{([^\\{]*?)\\}").matcher(format); + + int position = 0; + + while (matcher.find()) { + if (position < matcher.start()) { + // literal before + expression.add(format.substring(position, matcher.start())); + } + + String script = matcher.group(1); + + if (script.length() > 0) { + // compiled script, or literal + expression.add(engine.compile(script)); + } + + position = matcher.end(); + } + + if (position < format.length()) { + // tail + expression.add(format.substring(position, format.length())); + } + + return expression.toArray(); + } + + + protected abstract Bindings getBindings(Object object); + + + @Override + public StringBuffer format(Object object, StringBuffer sb, FieldPosition pos) { + Bindings bindings = getBindings(object); + + try { + for (Object snipped : expressions) { + if (snipped instanceof String) { + sb.append(snipped); + } else { + Object value = ((CompiledScript) snipped).eval(bindings); + + if (value != null) { + sb.append(value); + } + } + } + } catch (ScriptException e) { + throw new IllegalArgumentException(e); + } + + return sb; + } + + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/net/sourceforge/filebot/ui/panel/rename/NamesListTransferablePolicy.java b/source/net/sourceforge/filebot/ui/panel/rename/NamesListTransferablePolicy.java index 0231f1e1..1be1fe6d 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/NamesListTransferablePolicy.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/NamesListTransferablePolicy.java @@ -21,8 +21,6 @@ import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; -import ca.odell.glazedlists.EventList; - import net.sourceforge.filebot.torrent.Torrent; import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy; import net.sourceforge.tuned.FileUtilities; @@ -30,10 +28,10 @@ import net.sourceforge.tuned.FileUtilities; class NamesListTransferablePolicy extends FileTransferablePolicy { - private final EventList model; + private final List model; - public NamesListTransferablePolicy(EventList model) { + public NamesListTransferablePolicy(List model) { this.model = model; } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/NamesViewEventList.java b/source/net/sourceforge/filebot/ui/panel/rename/NamesViewEventList.java new file mode 100644 index 00000000..021f25f4 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/rename/NamesViewEventList.java @@ -0,0 +1,134 @@ + +package net.sourceforge.filebot.ui.panel.rename; + + +import static net.sourceforge.filebot.FileBotUtilities.isInvalidFileName; + +import java.awt.Component; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.List; + +import ca.odell.glazedlists.EventList; +import ca.odell.glazedlists.TransformedList; +import ca.odell.glazedlists.event.ListEvent; + + +public class NamesViewEventList extends TransformedList { + + private final List names = new ArrayList(); + + private final Component parent; + + + public NamesViewEventList(Component parent, EventList source) { + super(source); + + this.parent = parent; + + // connect to source list + source.addListEventListener(this); + } + + + @Override + protected boolean isWritable() { + return true; + } + + + @Override + public String get(int index) { + return names.get(index); + } + + + protected String format(Object object) { + return object.toString(); + } + + + @Override + public void listChanged(ListEvent listChanges) { + EventList source = listChanges.getSourceList(); + IndexView newValues = new IndexView(names); + + while (listChanges.next()) { + int index = listChanges.getIndex(); + int type = listChanges.getType(); + + switch (type) { + case ListEvent.INSERT: + names.add(index, format(source.get(index))); + newValues.getIndexFilter().add(index); + break; + case ListEvent.UPDATE: + names.set(index, format(source.get(index))); + newValues.getIndexFilter().add(index); + break; + case ListEvent.DELETE: + names.remove(index); + break; + } + } + + submit(newValues); + + listChanges.reset(); + updates.forwardEvent(listChanges); + } + + + protected void submit(List values) { + IndexView invalidValues = new IndexView(values); + + for (int i = 0; i < values.size(); i++) { + if (isInvalidFileName(values.get(i))) { + invalidValues.getIndexFilter().add(i); + } + } + + if (invalidValues.size() > 0) { + // validate names + ValidateNamesDialog.showDialog(parent, invalidValues); + } + } + + + protected static class IndexView extends AbstractList { + + private final List source; + + private final List indexFilter = new ArrayList(); + + + public IndexView(List source) { + this.source = source; + } + + + public List getIndexFilter() { + return indexFilter; + } + + + @Override + public E get(int index) { + return source.get(indexFilter.get(index)); + } + + + @Override + public E set(int index, E element) { + return source.set(indexFilter.get(index), element); + }; + + + @Override + public int size() { + return indexFilter.size(); + } + + } + +} diff --git a/source/net/sourceforge/filebot/web/Episode.java b/source/net/sourceforge/filebot/web/Episode.java index 88d451c7..254dec9f 100644 --- a/source/net/sourceforge/filebot/web/Episode.java +++ b/source/net/sourceforge/filebot/web/Episode.java @@ -3,7 +3,12 @@ package net.sourceforge.filebot.web; import java.io.Serializable; -import java.text.NumberFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class Episode implements Serializable { @@ -47,69 +52,78 @@ public class Episode implements Serializable { } - public void setSeriesName(String seriesName) { - this.seriesName = seriesName; - } - - - public void setSeasonNumber(String seasonNumber) { - this.seasonNumber = seasonNumber; - } - - - public void setEpisodeNumber(String episodeNumber) { - this.episodeNumber = episodeNumber; - } - - - public void setTitle(String episodeName) { - this.title = episodeName; - } - - @Override public String toString() { - StringBuilder sb = new StringBuilder(40); - - sb.append(seriesName).append(" - "); - - if (seasonNumber != null) { - sb.append(seasonNumber).append("x"); - } - - sb.append(episodeNumber).append(" - ").append(title); - - return sb.toString(); + return EpisodeFormat.getInstance().format(this); } + + public static class EpisodeFormat extends Format { + + private static final EpisodeFormat instance = new EpisodeFormat(); + + + public static EpisodeFormat getInstance() { + return instance; + } + - public static > T formatEpisodeNumbers(T episodes, int minDigits) { - // find max. episode number length - for (Episode episode : episodes) { - try { - String episodeNumber = episode.getEpisodeNumber(); - - if (episodeNumber.length() > minDigits && Integer.parseInt(episodeNumber) > 0) { - minDigits = episodeNumber.length(); + @Override + public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) { + Episode episode = (Episode) obj; + + sb.append(episode.getSeriesName()).append(" - "); + + if (episode.getSeasonNumber() != null) { + sb.append(episode.getSeasonNumber()).append('x'); + } + + sb.append(formatEpisodeNumber(episode.getEpisodeNumber())); + + return sb.append(" - ").append(episode.getTitle()); + } + + + protected String formatEpisodeNumber(String number) { + if (number.length() < 2) { + try { + return String.format("%02d", Integer.parseInt(number)); + } catch (NumberFormatException e) { + // ignore } - } catch (NumberFormatException e) { - // ignore } + + return number; } - // pad episode numbers with zeros (e.g. %02d) so all episode numbers have the same number of digits - NumberFormat numberFormat = NumberFormat.getIntegerInstance(); - numberFormat.setMinimumIntegerDigits(minDigits); - numberFormat.setGroupingUsed(false); - - for (Episode episode : episodes) { - try { - episode.setEpisodeNumber(numberFormat.format(Integer.parseInt(episode.getEpisodeNumber()))); - } catch (NumberFormatException e) { - // ignore + + @Override + public Episode parseObject(String source, ParsePosition pos) { + Pattern pattern = Pattern.compile("(.*) - (?:(\\w+?)x)?(\\w+)? - (.*)"); + + Matcher matcher = pattern.matcher(source).region(pos.getIndex(), source.length()); + + if (!matcher.matches()) { + pos.setErrorIndex(matcher.regionStart()); + return null; } + + // episode number must not be null + if (matcher.group(3) == null) { + pos.setErrorIndex(matcher.start(3)); + return null; + } + + pos.setIndex(matcher.end()); + return new Episode(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4)); + } + + + @Override + public Episode parseObject(String source) throws ParseException { + return (Episode) super.parseObject(source); } - return episodes; } + } diff --git a/test/net/sourceforge/filebot/ui/ScriptFormatTest.java b/test/net/sourceforge/filebot/ui/ScriptFormatTest.java new file mode 100644 index 00000000..491e7640 --- /dev/null +++ b/test/net/sourceforge/filebot/ui/ScriptFormatTest.java @@ -0,0 +1,69 @@ + +package net.sourceforge.filebot.ui; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptException; +import javax.script.SimpleBindings; + +import org.junit.Test; + + +public class ScriptFormatTest { + + @Test + public void compile() throws Exception { + ScriptFormat format = new TestScriptFormat(""); + + Object[] expression = format.compile("name: {name}, number: {number}", (Compilable) format.initScriptEngine()); + + assertEquals(4, expression.length, 0); + + assertTrue(expression[0] instanceof String); + assertTrue(expression[1] instanceof CompiledScript); + assertTrue(expression[2] instanceof String); + assertTrue(expression[3] instanceof CompiledScript); + } + + + @Test + public void format() throws Exception { + assertEquals("X5-452", new TestScriptFormat("X5-{value}").format("452")); + + // test pad + assertEquals("[007]", new TestScriptFormat("[{value.pad(3)}]").format("7")); + + // choice + assertEquals("not to be", new TestScriptFormat("{if (value) 'to be'; else 'not to be'}").format(null)); + + // empty choice + assertEquals("", new TestScriptFormat("{if (value) 'to be'}").format(null)); + + // loop + assertEquals("0123456789", new TestScriptFormat("{var s=''; for (var i=0; i