diff --git a/source/net/sourceforge/filebot/format/ExpressionBindings.java b/source/net/sourceforge/filebot/format/ExpressionBindings.java index ca295fcf..96c63809 100644 --- a/source/net/sourceforge/filebot/format/ExpressionBindings.java +++ b/source/net/sourceforge/filebot/format/ExpressionBindings.java @@ -1,7 +1,5 @@ - package net.sourceforge.filebot.format; - import java.lang.reflect.Method; import java.util.AbstractMap; import java.util.HashSet; @@ -13,59 +11,61 @@ import javax.script.Bindings; import net.sourceforge.tuned.ExceptionUtilities; - public class ExpressionBindings extends AbstractMap implements Bindings { - + protected final Object bindingBean; - + protected final Map bindings = new TreeMap(String.CASE_INSENSITIVE_ORDER); - + protected final Method undefined; - public ExpressionBindings(Object bindingBean) { this.bindingBean = bindingBean; - + // get method bindings for (Method method : bindingBean.getClass().getMethods()) { Define define = method.getAnnotation(Define.class); - + if (define != null) { for (String name : define.value()) { Method existingBinding = bindings.put(name, method); - + if (existingBinding != null) throw new IllegalArgumentException(String.format("Illegal binding {%s} on %s", name, method.getName())); } } } - + // extract mapping that handles undefined bindings undefined = bindings.remove(Define.undefined); } - + + protected boolean isUndefined(Object value) { + if (value instanceof CharSequence) { + return ((CharSequence) value).length() <= 0; + } + return value == null; + } public Object getBindingBean() { return bindingBean; } - protected Object evaluate(final Method method) throws Exception { Object value = method.invoke(bindingBean); - - if (value != null) { + + if (!isUndefined(value)) { return value; } - + // invoke fallback method return undefined.invoke(bindingBean); } - @Override public Object get(Object key) { Method method = bindings.get(key); - + if (method != null) { try { return evaluate(method); @@ -73,61 +73,53 @@ public class ExpressionBindings extends AbstractMap implements B throw new BindingException(key.toString(), ExceptionUtilities.getRootCauseMessage(e), e); } } - + return null; } - @Override public Object put(String key, Object value) { // bindings are immutable return null; } - @Override public Object remove(Object key) { // bindings are immutable return null; } - @Override public boolean containsKey(Object key) { return bindings.containsKey(key); } - @Override public Set keySet() { return bindings.keySet(); } - @Override public boolean isEmpty() { return bindings.isEmpty(); } - @Override public Set> entrySet() { Set> entrySet = new HashSet>(); - + for (final String key : keySet()) { entrySet.add(new Entry() { - + @Override public String getKey() { return key; } - @Override public Object getValue() { return get(key); } - @Override public Object setValue(Object value) { @@ -135,8 +127,8 @@ public class ExpressionBindings extends AbstractMap implements B } }); } - + return entrySet; } - + } diff --git a/source/net/sourceforge/filebot/format/ExpressionFormat.lib.groovy b/source/net/sourceforge/filebot/format/ExpressionFormat.lib.groovy index 77ad0500..65e5e23a 100644 --- a/source/net/sourceforge/filebot/format/ExpressionFormat.lib.groovy +++ b/source/net/sourceforge/filebot/format/ExpressionFormat.lib.groovy @@ -172,7 +172,7 @@ String.metaClass.ascii = { fallback = ' ' -> delegate.transliterate("Any-Latin;L /** * General helpers and utilities */ -def c(c) { +def c(Closure c) { try { return c.call() } catch (Throwable e) { @@ -180,6 +180,26 @@ def c(c) { } } +def any(Closure... closures) { + return closures.findResult{ c -> + try { + return c.call() + } catch (Throwable e) { + return null + } + } +} + +def allOf(Closure... closures) { + return closures.toList().findResults{ c -> + try { + return c.call() + } catch (Throwable e) { + return null + } + } +} + def csv(path, delim = ';', keyIndex = 0, valueIndex = 1) { def f = path as File def values = [:]