A simple/small/fast expression interpolator written in java. An interpolation expression is defined by:
${id:expression}
where:
- $ is the interpolation char initiator
- id is the immutable scope id for your scope implementation (see Scope interface API)
- : is the fixed char separator between id and expression
- { and } are char delimiters
- expression is an expression to be evaluated by the scope id implementation
Interpolator i = ...
String expr = "This is a ${map:adjective} string literal in ${map:language}";
String eval = i.interpolate(expr);
String target = "This is a simple string literal in java";
boolean great = target.equals(eval);
//Create a scope provider
ScopeProvider provider = new BasicScopeProvider();
//Register a scope implementation
provider.register(new MathScope("math"));//This is the scope id 'math'
//Build an interpolator
Interpolator i = provider.build();
//Prepare an expression
String expression = "a quick brown fox jumps over ${math:(5-2)} dogs";
//Interpolate and get the evaluated expression
String actual = i.interpolate(expression);
//The expected is (math scope eval to double)
boolean success = "a quick brown fox jumps over 3.0 dogs".equals(actual);
System.out.println("Success: " + success);
/*
${math:(5-2)} -> 3.0
*/
//...
Scope map = new SimpleMapScope.Builder()
.map("1", "one")
.map("2", "two")
.map("3", "three")
.build("map"); //<-- This is your scope id 'map'
//We can register multiples scope's (ex: 'map' and 'math')
Interpolator i = provider
.register(map)
.register(new MathScope("math"))
.build();
String expression = "${math:(5-4)} quick brown fox jumps over ${map:3} dogs";
String actual = i.interpolate(expression);
boolean success = "1.0 quick brown fox jumps over three dogs".equals(actual);
/*
${math:(5-4)} -> 1.0
${map:3} -> three
*/
public class CastToLongScope extends AbstractScope {
public CastToLongScope(String id) {
super(id);
}
@Override
protected String doEval(String expression) {
return Long.toString((long)Double.parseDouble(expression)); //eval to long
}
}
//...
Interpolator i = provider
.register(new CastToLongScope("long")) //scope id is 'long'
.register(map)
.register(new MathScope("math"))
.build();
String expression = "${long:${math:(3-2)}} quick brown fox jumps over ${long:${math:sqrt(9)}} dogs";
String actual = i.interpolate(expression);
boolean success = "1 quick brown fox jumps over 3 dogs".equals(actual);
/*
${math:(3-2)} -> 1.0
${long:1.0} -> 1
${math:sqrt(9)} -> 3.0
${long:3.0} -> 3
*/
//...
class Address {
private String street;
public Address(String street){ this.street = street;}
public String getStreet() { return street;}
}
class User {
private String name;
private Address address;
public User(String name, Address address){
this.name = name;
this.address = address;
}
public String getName() { return this.name;}
public Address getAddress() { return this.address;}
}
//...
User user = new User("John", new Address("West Main"));
Interpolator i = provider
//scope id below is 'pojo' (you might want to rename it to 'user' instead)
.register(new BeanScope("pojo", user))
.register(new CastToLongScope("long"))
.register(map)
.register(new MathScope("math"))
.build();
String expression = "The user ${pojo:name} lives on ${pojo:address.street} " +
"street number ${map:${long:${math:sqrt(9)}}}";
String actual = i.interpolate(expression);
boolean success = ("The user John lives on West Main " +
"street number three").equals(actual);
/*
${pojo:name} -> John
${pojo:address.street} -> West Main
${math:sqrt(9)} -> 3.0
${long:3.0} -> 3
${map:3} -> three
*/
//...
SupplierScope rt = new SupplierScope.Builder()
.map("availableProcessors", Runtime.getRuntime()::availableProcessors)
.map("totalMemory", Runtime.getRuntime()::totalMemory)
.map("freeMemory", Runtime.getRuntime()::freeMemory)
.map("maxMemory", Runtime.getRuntime()::maxMemory)
.build("runtime");
Interpolator i = provider
.register(rt)
.register(new CastToLongScope("long"))
.register(new BeanScope("pojo", user))
.register(map)
.register(new MathScope("math"))
.build();
String expression = "The user ${pojo:name} lives on ${pojo:address.street} " +
"street number ${map:${long:${math:sqrt(9)}}} and his macbook " +
"has ${runtime:availableProcessors} available processors";
String actual = i.interpolate(expression);
boolean success = ("The user John lives on West Main " +
"street number three and his macbook " +
"has 4 available processors").equals(actual);
//...
//...
String expression = "The user ${pojo:name} lives on ${pojo:address.street} " +
"street number ${map:${long:${math:sqrt(9)}} and his macbook " +
"has ${runtime:availableProcessors} available processors";
//second argument is debugging output instance
String actual = i.interpolate(expression, DebugOption.SYSOUT);
boolean success = ("The user John lives on West Main " +
"street number three and his macbook " +
"has 4 available processors").equals(actual);
/*
The console output will be:
${pojo:name} -> John
${pojo:address.street} -> West Main
${math:sqrt(9)} -> 3.0
${runtime:availableProcessors} -> 4
${long:3.0} -> 3
${map:3} -> three
*/
class FileDebuggingOutput implements DebugOutput {
public FileDebuggingOutput(File file) throws IOException {
this.out = new PrintWriter(new FileWriter(file));
}
@Override //Reimplement this method if you want
public void debug(String expression, String evaluated) {
this.out.println(expression + " -> " + evaluated);
}
}
//output to ./debugging.log file
DebugOutput out = new FileDebuggingOutput(new File("./debugging.log"));
String actual = i.interpolate(expression, out);
//...
ScopeProvider provider = new BasicScopeProvider();
Interpolator i = provider
.register(new MathScope("math"))
.build(DefaultCharConfig.HASH_BRACKETS); //config to #[]. See options below
/*
Options:
DefaultCharConfig.DOLLAR_BRACES //set ${}
DefaultCharConfig.DOLLAR_BRACKETS //set $[]
DefaultCharConfig.HASH_BRACKETS //set #[]
DefaultCharConfig.HASH_BRACES //set #{}
DefaultCharConfig.AT_BRACKETS //set @[]
DefaultCharConfig.AT_BRACES //set @{}
DefaultCharConfig.AND_BRACKETS //set &[]
DefaultCharConfig.AND_BRACES //set &{}
DefaultCharConfig.PERCENT_BRACKETS //set %[]
DefaultCharConfig.PERCENT_BRACES //set %{}
*/
String expression = "Interpolator version #[math:(9-8)] avaiable";
String actual = i.interpolate(expression);
boolean success = "Interpolator version 1.0 available".equals(actual);
- DefaultScopeProvider
//this class automatically register all DefaultScope API
Interpolator i = new DefaultScopeProvider().build();
//Line above is equivalent to
ScopeProvider provider = new BasicScopeProvider();
Interpolator i =
provider.register(DefaultScope.SYSTEM) //'system' id
.register(DefaultScope.MATH) //'math' id
.register(DefaultScope.RUNTIME) //'runtime' id
.register(DefaultScope.JAVASCRIPT) //'js' id
.register(DefaultScope.SYSOUT) //'sysout' id
.register(DefaultScope.LONG) //'long' id
.register(DefaultScope.UNDEFINED) //'undefined' id (eval to 'scope not found')
.build();
- AndScope
"${and:exp1,exp2}" -> (exp1) AND (exp2)
"(${and:exp1,exp2,exp3})" -> ((exp1) AND (exp2) AND (exp3))
"${and:field;exp1,exp2}" -> (field:"exp1") AND (field:"exp2") //Lucene sintaxe integration
"(${and:field;exp1,exp2;exp3})" -> ((field:"exp1") AND (field:"exp2") AND (field:"exp3"))
- OrScope
"${or:exp1,exp2}" -> (exp1) OR (exp2)
"(${or:exp1,exp2,exp3})" -> ((exp1) OR (exp2) OR (exp3))
"${or:field;exp1,exp2}" -> (field:"exp1") OR (field:"exp2") //Lucene sintaxe integration
"(${or:field;exp1,exp2;exp3})" -> ((field:"exp1") OR (field:"exp2") OR (field:"exp3"))
- BeanScope
User user = new User("John", "Kennedy");
"Hello ${user:name} ${user:surname}" -> "Hello John Kennedy"
- CacheScope
Interpolator i = provider.register(new CacheScope(new MathScope("math"))).build();
String e1 = "Number ${math:(3+2*sqrt(9)}";
String e2 = i.interpolate(e1); //eval to "Number 9.0"
//The string expression '(3+2*sqrt(9))' was cached, so it will not eval again
String e3 = i.interpolate(e1); //eval to "Number 9.0" too using previous cached value
- ConstScope
Interpolator i = provider.register(new ConstScope("const", "World")).build();
String e1 = "Hello ${const:Whatever}";
//eval to "Hello World" (Whatever is ignored and replaced to "World")
String e2 = i.interpolate(e1);
- DefaultScope.SYSTEM
Interpolator i = provider.register(DefaultScope.SYSTEM).build();
"Hello ${system:user.home}" -> "Hello /home/user" //eval to System.getProperty("user.home", "")
- PropertiesScope
Properties properties = ....
Interpolator i = provider.register(new PropertiesScope("prop", properties)).build();
"Hello ${prop:key}" -> "Hello World" //eval to properties.getProperty("key", "");
- JavaScriptScope
String code = "function sum(a, b) { return a + b; } sum(3,4);";
Interpolator i = provider
.register(new ConstScope("source", code))
.register(new JavaScriptScope("js"))
.build(DefaultCharConfig.HASH_BRACKETS); //prevent braces conflicts with javascript code!
String e1 = "Result is #[js:#[source:code]]";
String actual = i.interpolate(e1);
String expected = "Result is 7.0";
- SysoutScope
"Hello ${sysout:World}" -> "Hello World"
/*
The console output will be:
World
*/
- DefaultScope.RUNTIME
Interpolator i = provider.register(DefaultScope.RUNTIME).build();
"Available processors: ${runtime:availableProcessors}" -> "Available processors: 4"
"Total memory: ${runtime:totalMemory}" -> "Total memory: 567892"
"Free memory: ${runtime:freeMemory}" -> "Free memory: 8927836"
"Max memory: ${runtime:maxMemory}" -> "Max memory: 1225612"
- DefaultScope.LONG
Interpolator i = provider
.register(DefaultScope.LONG)
.register(new MathScope())
.build();
"sqrt(9) is ${long:${math:sqrt(9)}}" -> "sqrt(9) is 3" //cast double 3.0 to long 3
- MathScope
${math:(((2 + 5*(-10^2)/0.5)^2-100*sqrt((7-5)))+cos(45)^sin(30)*4-tan(60))} -> 995864.8192086903
- BinaryScope (base class api used by AndScope and OrScope)
- ScopeWrapper (base class api)
- MapScope (base class api for custom implementation Mapping Scope)
- SimpleMapScope
- SupplierScope
- PrintStreamScope