mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-23 00:08:51 -05:00
+ use Groovy instead of JavaScript in ExpressionFormat
This commit is contained in:
parent
384486631a
commit
b04f89b7fd
@ -92,12 +92,9 @@
|
||||
<include name="com/sun/jna/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/js-engine.jar">
|
||||
<include name="com/sun/phobos/script/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/js.jar">
|
||||
<include name="org/mozilla/**" />
|
||||
<zipfileset src="${dir.lib}/groovy.jar">
|
||||
<include name="groovy*/**" />
|
||||
<include name="org/codehaus/groovy/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/sublight-ws.jar">
|
||||
|
BIN
lib/groovy.jar
Normal file
BIN
lib/groovy.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
lib/js.jar
BIN
lib/js.jar
Binary file not shown.
@ -2,6 +2,9 @@
|
||||
package net.sourceforge.filebot.format;
|
||||
|
||||
|
||||
import groovy.lang.GroovyObject;
|
||||
import groovy.lang.MetaClass;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.HashMap;
|
||||
@ -10,10 +13,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
|
||||
public class AssociativeScriptObject implements Scriptable {
|
||||
public class AssociativeScriptObject implements GroovyObject {
|
||||
|
||||
private final Map<String, Object> properties;
|
||||
|
||||
@ -23,24 +24,14 @@ public class AssociativeScriptObject implements Scriptable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines properties available by name.
|
||||
*
|
||||
* @param name the name of the property
|
||||
* @param start the object where lookup began
|
||||
*/
|
||||
public boolean has(String name, Scriptable start) {
|
||||
return properties.containsKey(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the property with the given name.
|
||||
*
|
||||
* @param name the property name
|
||||
* @param start the object where the lookup began
|
||||
*/
|
||||
public Object get(String name, Scriptable start) {
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
Object value = properties.get(name);
|
||||
|
||||
if (value == null)
|
||||
@ -50,52 +41,28 @@ public class AssociativeScriptObject implements Scriptable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines properties available by index.
|
||||
*
|
||||
* @param index the index of the property
|
||||
* @param start the object where lookup began
|
||||
*/
|
||||
public boolean has(int index, Scriptable start) {
|
||||
// get property by index not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get property by index.
|
||||
*
|
||||
* @param index the index of the property
|
||||
* @param start the object where the lookup began
|
||||
*/
|
||||
public Object get(int index, Scriptable start) {
|
||||
// get property by index not supported
|
||||
throw new BindingException(String.valueOf(index), "undefined");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get property names.
|
||||
*/
|
||||
public Object[] getIds() {
|
||||
return properties.keySet().toArray();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of this JavaScript class.
|
||||
*/
|
||||
public String getClassName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the string value of this object.
|
||||
*/
|
||||
@Override
|
||||
public Object getDefaultValue(Class<?> typeHint) {
|
||||
return this.toString();
|
||||
public void setProperty(String name, Object value) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object invokeMethod(String name, Object args) {
|
||||
// ignore, object is merely a structure
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MetaClass getMetaClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setMetaClass(MetaClass clazz) {
|
||||
// ignore, don't care about MetaClass
|
||||
}
|
||||
|
||||
|
||||
@ -106,51 +73,6 @@ public class AssociativeScriptObject implements Scriptable {
|
||||
}
|
||||
|
||||
|
||||
public void put(String name, Scriptable start, Object value) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public void put(int index, Scriptable start, Object value) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public void delete(String id) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public void delete(int index) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public Scriptable getPrototype() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void setPrototype(Scriptable prototype) {
|
||||
// ignore, don't care about prototype
|
||||
}
|
||||
|
||||
|
||||
public Scriptable getParentScope() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void setParentScope(Scriptable parent) {
|
||||
// ignore, don't care about scope
|
||||
}
|
||||
|
||||
|
||||
public boolean hasInstance(Scriptable value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map allowing look-up of values by a fault-tolerant key as specified by the defining key.
|
||||
*
|
||||
|
@ -1,142 +0,0 @@
|
||||
// System, Math, Integer, etc.
|
||||
importPackage(java.lang);
|
||||
|
||||
// Collection, Scanner, Random, UUID, etc.
|
||||
importPackage(java.util);
|
||||
|
||||
// other useful classes
|
||||
importClass(net.sourceforge.filebot.similarity.SeriesNameMatcher);
|
||||
importClass(net.sourceforge.filebot.similarity.SeasonEpisodeMatcher);
|
||||
|
||||
|
||||
/**
|
||||
* Convenience methods for String.toLowerCase() and String.toUpperCase().
|
||||
*/
|
||||
String.prototype.lower = String.prototype.toLowerCase;
|
||||
String.prototype.upper = String.prototype.toUpperCase;
|
||||
|
||||
|
||||
/**
|
||||
* Pad strings or numbers with given characters ('0' by default).
|
||||
*
|
||||
* e.g. "1" -> "01"
|
||||
*/
|
||||
String.prototype.pad = Number.prototype.pad = function(length, padding) {
|
||||
var s = this.toString();
|
||||
|
||||
// use default padding, if padding is undefined or empty
|
||||
var p = padding ? padding.toString() : '0';
|
||||
|
||||
while (s.length < length) {
|
||||
s = p + s;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace space characters with a given characters.
|
||||
*
|
||||
* e.g. "Doctor Who" -> "Doctor_Who"
|
||||
*/
|
||||
String.prototype.space = function(replacement) {
|
||||
return this.replace(/\s+/g, replacement);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Upper-case all initials.
|
||||
*
|
||||
* e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born"
|
||||
*/
|
||||
String.prototype.upperInitial = function() {
|
||||
return this.replace(/\b[a-z]/g, function(letter) { return letter.toUpperCase() });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lower-case all letters that are not initials.
|
||||
*
|
||||
* e.g. "Gundam SEED" -> "Gundam Seed"
|
||||
*/
|
||||
String.prototype.lowerTrail = function() {
|
||||
return this.replace(/\b([a-z])([a-z]+)\b/gi, function(match, initial, trail) { return initial + trail.toLowerCase() });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove leading and trailing whitespace.
|
||||
*/
|
||||
String.prototype.trim = function() {
|
||||
return this.replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return substring before the given delimiter.
|
||||
*/
|
||||
String.prototype.before = function(delimiter) {
|
||||
var endIndex = delimiter instanceof RegExp ? this.search(delimiter) : this.indexOf(delimiter);
|
||||
|
||||
// delimiter was found, return leading substring, else return original value
|
||||
return endIndex >= 0 ? this.substring(0, endIndex) : this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return substring after the given delimiter.
|
||||
*/
|
||||
String.prototype.after = function(delimiter) {
|
||||
if (delimiter instanceof RegExp) {
|
||||
var match = this.match(delimiter);
|
||||
|
||||
if (match == null)
|
||||
return this;
|
||||
|
||||
// use pattern match as delimiter
|
||||
delimiter = match[0];
|
||||
}
|
||||
|
||||
var startIndex = this.indexOf(delimiter);
|
||||
|
||||
// delimiter was found, return trailing substring, else return original value
|
||||
return startIndex >= 0 ? this.substring(startIndex + delimiter.length, this.length) : this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace trailing parenthesis including any leading whitespace.
|
||||
*
|
||||
* e.g. "The IT Crowd (UK)" -> "The IT Crowd"
|
||||
*/
|
||||
String.prototype.replaceTrailingBraces = function(replacement) {
|
||||
// use empty string as default replacement
|
||||
var r = replacement ? replacement : "";
|
||||
|
||||
return this.replace(/\s*[(]([^)]*)[)]$/, r);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace 'part section'.
|
||||
*
|
||||
* e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1"
|
||||
* "Today Is the Day (1)" -> "Today Is the Day, Part 1"
|
||||
*/
|
||||
String.prototype.replacePart = function (replacement) {
|
||||
// use empty string as default replacement
|
||||
var r = replacement ? replacement : "";
|
||||
|
||||
// handle '(n)', '(Part n)' and ': Part n' like syntax
|
||||
var pattern = [/\s*[(](\w+)[)]$/i, /\W*Part (\w+)\W*$/i];
|
||||
|
||||
for (var i = 0; i < pattern.length; i++) {
|
||||
if (pattern[i].test(this)) {
|
||||
return this.replace(pattern[i], r);
|
||||
}
|
||||
}
|
||||
|
||||
// no pattern matches, nothing to replace
|
||||
return this;
|
||||
}
|
@ -2,6 +2,10 @@
|
||||
package net.sourceforge.filebot.format;
|
||||
|
||||
|
||||
import static net.sourceforge.tuned.ExceptionUtilities.*;
|
||||
import groovy.lang.GroovyRuntimeException;
|
||||
import groovy.lang.MissingPropertyException;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.AccessControlContext;
|
||||
@ -18,8 +22,6 @@ import java.text.ParsePosition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
@ -29,9 +31,8 @@ import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleScriptContext;
|
||||
|
||||
import org.mozilla.javascript.EcmaError;
|
||||
|
||||
import com.sun.phobos.script.javascript.RhinoScriptEngine;
|
||||
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
|
||||
import org.codehaus.groovy.jsr223.GroovyScriptEngineFactory;
|
||||
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
|
||||
@ -52,10 +53,10 @@ public class ExpressionFormat extends Format {
|
||||
|
||||
|
||||
protected ScriptEngine initScriptEngine() throws ScriptException {
|
||||
// don't use jdk rhino so we can use rhino specific features and classes (e.g. Scriptable)
|
||||
ScriptEngine engine = new RhinoScriptEngine();
|
||||
// use groovy script engine
|
||||
ScriptEngine engine = new GroovyScriptEngineFactory().getScriptEngine();
|
||||
|
||||
engine.eval(new InputStreamReader(ExpressionFormat.class.getResourceAsStream("ExpressionFormat.global.js")));
|
||||
engine.eval(new InputStreamReader(ExpressionFormat.class.getResourceAsStream("ExpressionFormat.lib.groovy")));
|
||||
|
||||
return engine;
|
||||
}
|
||||
@ -69,29 +70,71 @@ public class ExpressionFormat extends Format {
|
||||
protected Object[] compile(String expression, Compilable engine) throws ScriptException {
|
||||
List<Object> compilation = new ArrayList<Object>();
|
||||
|
||||
Matcher matcher = Pattern.compile("\\{([^\\{]*?)\\}").matcher(expression);
|
||||
char open = '{';
|
||||
char close = '}';
|
||||
|
||||
int position = 0;
|
||||
StringBuilder token = new StringBuilder();
|
||||
int level = 0;
|
||||
|
||||
while (matcher.find()) {
|
||||
if (position < matcher.start()) {
|
||||
// literal before
|
||||
compilation.add(expression.substring(position, matcher.start()));
|
||||
// parse expressions and literals
|
||||
for (int i = 0; i < expression.length(); i++) {
|
||||
char c = expression.charAt(i);
|
||||
|
||||
if (c == open) {
|
||||
if (level == 0) {
|
||||
if (token.length() > 0) {
|
||||
compilation.add(token.toString());
|
||||
token.setLength(0);
|
||||
}
|
||||
} else {
|
||||
token.append(c);
|
||||
}
|
||||
|
||||
level++;
|
||||
} else if (c == close) {
|
||||
if (level == 1) {
|
||||
if (token.length() > 0) {
|
||||
try {
|
||||
compilation.add(engine.compile(token.toString()));
|
||||
} catch (ScriptException e) {
|
||||
// try to extract syntax exception
|
||||
ScriptException illegalSyntax = e;
|
||||
|
||||
try {
|
||||
String message = findCause(e, MultipleCompilationErrorsException.class).getErrorCollector().getSyntaxError(0).getOriginalMessage();
|
||||
illegalSyntax = new ScriptException("SyntaxError: " + message);
|
||||
} catch (Exception ignore) {
|
||||
// ignore, just use original exception
|
||||
}
|
||||
|
||||
throw illegalSyntax;
|
||||
} finally {
|
||||
token.setLength(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
token.append(c);
|
||||
}
|
||||
|
||||
level--;
|
||||
} else {
|
||||
token.append(c);
|
||||
}
|
||||
|
||||
String script = matcher.group(1);
|
||||
|
||||
if (script.length() > 0) {
|
||||
// compiled script, or literal
|
||||
compilation.add(engine.compile(script));
|
||||
// sanity check
|
||||
if (level < 0) {
|
||||
throw new ScriptException("SyntaxError: unexpected token: " + close);
|
||||
}
|
||||
|
||||
position = matcher.end();
|
||||
}
|
||||
|
||||
if (position < expression.length()) {
|
||||
// tail
|
||||
compilation.add(expression.substring(position, expression.length()));
|
||||
// sanity check
|
||||
if (level != 0) {
|
||||
throw new ScriptException("SyntaxError: missing token: " + close);
|
||||
}
|
||||
|
||||
// append tail
|
||||
if (token.length() > 0) {
|
||||
compilation.add(token.toString());
|
||||
}
|
||||
|
||||
return compilation.toArray();
|
||||
@ -129,16 +172,7 @@ public class ExpressionFormat extends Format {
|
||||
sb.append(value);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
EcmaError ecmaError = ExceptionUtilities.findCause(e, EcmaError.class);
|
||||
|
||||
// try to unwrap EcmaError
|
||||
if (ecmaError != null) {
|
||||
lastException = new ExpressionException(String.format("%s: %s", ecmaError.getName(), ecmaError.getErrorMessage()), e);
|
||||
} else {
|
||||
lastException = e;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
lastException = new ExpressionException(e);
|
||||
handleException(e);
|
||||
}
|
||||
} else {
|
||||
sb.append(snipped);
|
||||
@ -149,6 +183,17 @@ public class ExpressionFormat extends Format {
|
||||
}
|
||||
|
||||
|
||||
protected void handleException(ScriptException exception) {
|
||||
if (findCause(exception, MissingPropertyException.class) != null) {
|
||||
lastException = new ExpressionException(new BindingException(findCause(exception, MissingPropertyException.class).getProperty(), "undefined", exception));
|
||||
} else if (findCause(exception, GroovyRuntimeException.class) != null) {
|
||||
lastException = new ExpressionException(findCause(exception, GroovyRuntimeException.class).getMessage(), exception);
|
||||
} else {
|
||||
lastException = exception;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ScriptException caughtScriptException() {
|
||||
return lastException;
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
// Collection, Scanner, Random, UUID, etc.
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* Convenience methods for String.toLowerCase()and String.toUpperCase()
|
||||
*/
|
||||
String.metaClass.lower = { toLowerCase() }
|
||||
String.metaClass.upper = { toUpperCase() }
|
||||
|
||||
|
||||
/**
|
||||
* Pad strings or numbers with given characters ('0' by default).
|
||||
*
|
||||
* e.g. "1" -> "01"
|
||||
*/
|
||||
String.metaClass.pad = Number.metaClass.pad = { length = 2, padding = "0" -> delegate.toString().padLeft(length, padding) }
|
||||
|
||||
|
||||
/**
|
||||
* Replace space characters with a given characters.
|
||||
*
|
||||
* e.g. "Doctor Who" -> "Doctor_Who"
|
||||
*/
|
||||
String.metaClass.space = { replacement -> replaceAll(/\s+/, replacement) }
|
||||
|
||||
|
||||
/**
|
||||
* Upper-case all initials.
|
||||
*
|
||||
* e.g. "The Day a new Demon was born" -> "The Day A New Demon Was Born"
|
||||
*/
|
||||
String.metaClass.upperInitial = { replaceAll(/\b[a-z]/, { it.toUpperCase() }) }
|
||||
|
||||
|
||||
/**
|
||||
* Lower-case all letters that are not initials.
|
||||
*
|
||||
* e.g. "Gundam SEED" -> "Gundam Seed"
|
||||
*/
|
||||
String.metaClass.lowerTrail = { replaceAll(/\b(\p{Alpha})(\p{Alpha}+)\b/, { match, initial, trail -> initial + trail.toLowerCase() }) }
|
||||
|
||||
|
||||
/**
|
||||
* Return substring before the given delimiter.
|
||||
*/
|
||||
String.metaClass.before = {
|
||||
def matcher = delegate =~ it
|
||||
|
||||
// delimiter was found, return leading substring, else return original value
|
||||
return matcher.find() ? delegate.substring(0, matcher.start()) : delegate
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return substring after the given delimiter.
|
||||
*/
|
||||
String.metaClass.after = {
|
||||
def matcher = delegate =~ it
|
||||
|
||||
// delimiter was found, return trailing substring, else return original value
|
||||
return matcher.find() ? delegate.substring(matcher.end(), delegate.length()) : delegate
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace trailing parenthesis including any leading whitespace.
|
||||
*
|
||||
* e.g. "The IT Crowd (UK)" -> "The IT Crowd"
|
||||
*/
|
||||
String.metaClass.replaceTrailingBraces = { replacement = "" -> replaceAll(/\s*[(]([^)]*)[)]$/, replacement) }
|
||||
|
||||
|
||||
/**
|
||||
* Replace 'part section'.
|
||||
*
|
||||
* e.g. "Today Is the Day: Part 1" -> "Today Is the Day, Part 1"
|
||||
* "Today Is the Day (1)" -> "Today Is the Day, Part 1"
|
||||
*/
|
||||
String.metaClass.replacePart = { replacement = "" ->
|
||||
// handle '(n)', '(Part n)' and ': Part n' like syntax
|
||||
for (pattern in [/\s*[(](\w+)[)]$/, /(?i)\W*Part (\w+)\W*$/]) {
|
||||
if ((delegate =~ pattern).find()) {
|
||||
return replaceAll(pattern, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
// no pattern matches, nothing to replace
|
||||
return delegate;
|
||||
}
|
@ -21,8 +21,6 @@ public class ExpressionFormatTest {
|
||||
|
||||
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);
|
||||
@ -34,20 +32,100 @@ public class ExpressionFormatTest {
|
||||
public void format() throws Exception {
|
||||
assertEquals("X5-452", new TestScriptFormat("X5-{value}").format("452"));
|
||||
|
||||
// test pad
|
||||
// padding
|
||||
assertEquals("[007]", new TestScriptFormat("[{value.pad(3)}]").format("7"));
|
||||
assertEquals("[xx7]", new TestScriptFormat("[{value.pad(3, 'x')}]").format("7"));
|
||||
|
||||
// case
|
||||
assertEquals("ALL_CAPS", new TestScriptFormat("{value.upper()}").format("all_caps"));
|
||||
assertEquals("lower_case", new TestScriptFormat("{value.lower()}").format("LOWER_CASE"));
|
||||
|
||||
// normalize
|
||||
assertEquals("Doctor_Who", new TestScriptFormat("{value.space('_')}").format("Doctor Who"));
|
||||
assertEquals("The Day A New Demon Was Born", new TestScriptFormat("{value.upperInitial()}").format("The Day a new Demon was born"));
|
||||
assertEquals("Gundam Seed", new TestScriptFormat("{value.lowerTrail()}").format("Gundam SEED"));
|
||||
|
||||
// substring
|
||||
assertEquals("first", new TestScriptFormat("{value.before(/[^a-z]/)}").format("first|second"));
|
||||
assertEquals("second", new TestScriptFormat("{value.after(/[^a-z]/)}").format("first|second"));
|
||||
|
||||
// replace trailing braces
|
||||
assertEquals("The IT Crowd", new TestScriptFormat("{value.replaceTrailingBraces()}").format("The IT Crowd (UK)"));
|
||||
|
||||
// replace part
|
||||
assertEquals("Today Is the Day, Part 1", new TestScriptFormat("{value.replacePart(', Part $1')}").format("Today Is the Day (1)"));
|
||||
assertEquals("Today Is the Day, Part 1", new TestScriptFormat("{value.replacePart(', Part $1')}").format("Today Is the Day: part 1"));
|
||||
|
||||
// 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<parseInt(value);i++) s+=i;}").format("10"));
|
||||
assertEquals("not to be", new TestScriptFormat("{value ? 'to be' : 'not to be'}").format(null));
|
||||
assertEquals("default", new TestScriptFormat("{value ?: 'default'}").format(null));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void closures() throws Exception {
|
||||
assertEquals("[ant, cat]", new TestScriptFormat("{['ant', 'buffalo', 'cat', 'dinosaur'].findAll{ it.size() <= 3 }}").format(null));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void illegalSyntax() throws Exception {
|
||||
try {
|
||||
// will throw exception
|
||||
new TestScriptFormat("{value.}");
|
||||
// exception must be thrown
|
||||
fail("exception expected");
|
||||
} catch (ScriptException e) {
|
||||
// check message
|
||||
assertEquals("SyntaxError: unexpected token: .", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void illegalClosingBracket() throws Exception {
|
||||
try {
|
||||
// will throw exception
|
||||
new TestScriptFormat("{{ it -> 'value' }}}");
|
||||
// exception must be thrown
|
||||
fail("exception expected");
|
||||
} catch (ScriptException e) {
|
||||
// check message
|
||||
assertEquals("SyntaxError: unexpected token: }", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void illegalBinding() throws Exception {
|
||||
TestScriptFormat format = new TestScriptFormat("{xyz}");
|
||||
format.format(new SimpleBindings());
|
||||
|
||||
// check message
|
||||
assertEquals("BindingError: \"xyz\": undefined", format.caughtScriptException().getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void illegalProperty() throws Exception {
|
||||
TestScriptFormat format = new TestScriptFormat("{value.xyz}");
|
||||
format.format("test");
|
||||
|
||||
// check message
|
||||
assertEquals("BindingError: \"xyz\": undefined", format.caughtScriptException().getMessage());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void illegalMethod() throws Exception {
|
||||
TestScriptFormat format = new TestScriptFormat("{value.xyz()}");
|
||||
format.format("test");
|
||||
|
||||
// check message
|
||||
assertEquals("No signature of method: java.lang.String.xyz() is applicable for argument types: () values: []", format.caughtScriptException().getMessage());
|
||||
}
|
||||
|
||||
|
||||
protected static class TestScriptFormat extends ExpressionFormat {
|
||||
|
||||
public TestScriptFormat(String format) throws ScriptException {
|
||||
|
Loading…
Reference in New Issue
Block a user