https://en.wikipedia.org/wiki/MVEL

import java.util.*;

// the main quicksort algorithm
def quicksort(list) {
if (list.size() <= 1) {
list;
}
else {
pivot = list[0];
concat(quicksort(($ in list if $ < pivot)), pivot, quicksort(($ in list if $ > pivot)));
}
} // define method to concatenate lists.
def concat(list1, pivot, list2) {
concatList = new ArrayList(list1);
concatList.add(pivot);
concatList.addAll(list2);
concatList;
} // create a list to sort
list = [5,2,4,1,18,10,15,1,0]; // sort it!
quicksort(list);

MVEL relies on Java namespaces and classes, but does not possess the ability to declare namespaces or classes.

     System.out.println("Hello, world!");
MVEL
Developer(s) Mike Brock and Various Contributors
Stable release
2.3.0 / June 15, 2016
Repository

Written in Java
Operating system Cross-platform
Type Expression Language (EL)
License Apache License
Website https://github.com/mvel/mvel

MVFLEX Expression Language (MVEL) is a hybrid dynamic/statically typed, embeddable Expression Language and runtime for the Java Platform. Originally started as a utility language for an application framework, the project is now developed completely independently.

MVEL is typically used for exposing basic logic to end-users and programmers through configuration such as XML files or annotations. It may also be used to parse simple JavaBean expressions.

The runtime allows MVEL expressions to be executed either interpretively, or through a pre-compilation process with support for runtime bytecode generation to remove overhead.

Since MVEL is meant to augment Java-based software, it borrows most of its syntax directly from the Java programming language with some minor differences and additional capabilities. For example: as a side effect of MVEL's typing model, which treats class and method references as regular variables, it is possible to use both class and function pointers (but only for static methods).

http://mvel.documentnode.com/

Language Guide for 2.0

MVEL has largely been inspired by Java syntax, but has some fundamental differences aimed at making it more efficient as an expression language, such as operators that directly support collection, array and string matching, as well as regular expressions. MVEL is used to evaluate expressions written using Java syntax.

In addition to the expression language, MVEL serves as a templating language for configuration and string construction. Another wiki page for a summary information is: https://en.wikipedia.org/wiki/MVEL.

MVEL 2.x expressions may consist of:

  • Property expressions
  • Boolean expressions
  • Method invocations
  • Variable assignments
  • Function definitions

Basic Syntax

MVEL is an expression language based on Java-syntax, with some marked differences specific to MVEL.  Unlike Java however, MVEL is dynamically typed (with optional typing), meaning type qualification is not required in the source.

MVEL interpreters as downloadable libraries which can be integrated to the product is available. It needs to be downloaded from the [https://maven.apache.org/ maven] website. The libraries expose APIs. If an expression it passed to the interface of the library, the expression is evaluated and result is provided.

An MVEL expression can be as simple as a single identifier, or as complicated as a full blown boolean expression with method calls and inline collection creations.

Simple Property Expression

user.name

In this expression, we simply have a single identifier (user.name), which by itself, is what we refer to in MVEL as a property expression, in that the only purpose of the expression is to extract a property out of a variable or context object.  Property expressions are one of the most common uses, allowing MVEL to be used as a very high performance, easy to use, reflection-optimizer.

MVEL can even be used for evaluating a boolean expression:

user.name == 'John Doe'

Like Java, MVEL supports the full gambit of operator precedence rules, including the ability to use bracketing to control execution order.

(user.name == 'John Doe') &amp;&amp; ((x * 2) - 1) > 20

Multiple Statements

You may write scripts with an arbitrary number of statements using the semi-colon to denote the termination of a statement. This is required in all cases except in cases where there is only one statement, or for the last statement in a script.

statement1; statement2; statement3

Note the lack of semi-colon after statement3.

New lines are not substitutes for the use of the semi-colon in MVEL.

Returned Values

MVEL is designed to be an integration language at its core, allowing developers to provide simple scripting facilities for binding and logic. As such, MVEL expressions use a ‘’last value out’’ principle. This means, that although MVEL supports the return keyword, it is almost never needed. For example:

a = 10;
b = (a = a * 2) + 10;
a;

In this particular example, the expression returns the value of a as it is the last value of the expression. It is functionally identical to:

a = 10;
b = (a = a * 2) + 10;
return a;

Value Tests

All equality checks in MVEL are based on ‘’value’‘not’‘reference’’. Therefore, the expression foo == 'bar' is the equivalent to foo.equals("bar") in Java.

Testing for Value Emptiness

MVEL provides a special literal for testing for emptiness of a value, cleverly named empty.

For example:

foo == empty

The example expression will be ‘’true’’ if the value of foo satisfies any of the requirements of emptiness.

Testing for Null

MVEL allows both the use of the keyword null or nil to represent a null value.

foo == null;
foo == nil; // same as null

Value Coercion

MVEL’s type coercion system is applied in cases where two incomparable types are presented by attempting to coerce the ‘’right’‘value to that of the type of the’‘left’’ value, and then vice-versa.

For example:

"123" == 123;

This expression is ‘’true’‘in MVEL because the type coercion system will coerce the untyped number’‘123’’ to a String in order to perform the comparison.

Inline List, Maps and Arrays

MVEL allows you to express Lists, Maps and Arrays using simple elegant syntax. Consider the following example:

["Bob" : new Person("Bob"), "Michael" : new Person("Michael")]

This is functionally equivalent to the following code:

Map map = new HashMap();
map.put("Bob", new Person("Bob"));
map.put("Michael", new Person("Michael"));

This can be a very powerful way to express data structures inside of MVEL. You can use these constructs anywhere, even as a parameter to a method:

something.someMethod(["foo" : "bar"]);

Lists

Lists are expressed in the following format: ‘’[item1, item2, …]’’

For example:

["Jim", "Bob", "Smith"]

Maps

Maps are expressed in the following format: ‘’[key1 : value1, key2: value2, …]’’

For example:

["Foo" : "Bar", "Bar" : "Foo"]

Arrays

Arrays are expressed in the following format: ‘’{item1, item2, …}’’

For example:

{"Jim", "Bob", "Smith"}

Array Coercion

One important facet about inline arrays to understand is their special ability to be coerced to other array types. When you declare an inline array, it is untyped, but say for example you are passing to a method that accepts int[]. You simply can write your code as the following:

foo.someMethod({1,2,3,4});

In this case, MVEL will see that the target method accepts an int[] and automatically type the array as such.

Property Navigation

MVEL property navigation follows well-established conventions found in other bean property expressions found in other languages such as Groovy, OGNL, EL, etc.

Unlike some other languages which require qualification depending on the underlying method of access, MVEL provides a single, unified syntax for accessing properties, static fields, maps, etc.

Bean Properties

Most java developers are familiar with and user the getter/setter paradigm in their Java objects in order to encapsulate property accessors.   For example, you might access a property from an object as such:

user.getManager().getName();

In order to simplify this, you can access the same property using the following expression:

user.manager.name

Note: In situations where the field in the object is public, MVEL will still prefer to access the property via it’s getter method.

Null-Safe Bean Navigation

Sometimes you have property expressions which may contain a null element, requiring you to create a null-check. You can simplify this by using the null-safe operator:

user.?manager.name

This is functionally equivalent to writing:

if (user.manager != null) { return user.manager.name; } else { return null; }

Collections

Traversal of collections can also be achieved using abbreviated syntax.

List Access

Lists are accessed the same as array’s. For example:

user[5]

is the equivalent of the Java code:

user.get(5);

Map Access

Maps are accessed in the same way as array’s except any object can be passed as the index value. For example:

user["foobar"]

is the equivalent of the Java code:

user.get("foobar");

For Maps that use a String as a key, you may use another special syntax:

user.foobar

… Allowing you to treat the Map itself as a virtual object.

Strings as Arrays

For the purposes of using property indexes (as well as iteration) all Strings are treated as arrays. In MVEL you may refer to the first character in a String variable as such:

foo = "My String";
foo[0]; // returns 'M';

Literals

A literal is used to represent a fixed-value in the source of a particular script.

String literals

String literals may be denoted by single or double quotes.

"This is a string literal"
'This is also string literal'

String Escape Sequences

  • \\ - Double escape allows rendering of single backslash in string.
  • \n - Newline
  • \r - Return
  • \u#### - Unicode character (Example: \uAE00)
  • \### - Octal character (Example: \73)

Numeric Literals

Integers can be represented in decimal (base 10), octal (base 8), or hexadecimal (base 16).

A decimal integer can be expressed as any number that does not start with zero.

125 // decimal

An octal representation of an integer is possible by prefixing the number with a zero, followed by digits ranging from 0 to 7.

0353 // octal

Hexidecimal is represented by prefixing the integer with 0x followed by numbers ranging from 0-9..A-F.

0xAFF0 // hex

Floating Point Literals

A floating point number consists of a whole number and a factional part denoted by the point/period character, with an optional type suffix.

10.503 // a double
94.92d // a double
14.5f // a float

BigInteger and BigDecimal Literals

You can represent BigInteger and BigDecimal literals by using the suffixes B and I (uppercase is mandatory).


104.39484B // BigDecimal 8.4I // BigInteger ***

Boolean Literals

Boolean literals are represented by the reserved keywords true and false.

Null Literal

The null literal is denoted by the reserved keywords null or nil.

Type Literals

Type literals are treated pretty much the same as in Java, with the following format: ‘’<PackageName>.<ClassName>’’.

So a class may be qualified as such:

java.util.HashMap

Or if the class has been imported either inline by or by external configuration—it is simply referred to by its unqualified name:

HashMap

Nested Classes

Nested classes are not accessible through the standard dot-notation (as in Java) in MVEL 2.0. Rather, you must qualify these classes with the $symbol.

org.proctor.Person$BodyPart 

Flow Control

MVEL’s power goes beyond simple expressions. In fact, MVEL supports an assortment of control flow operators which will allow you to perform advanced scripting operations.

If-Then-Else

MVEL supports full, C/Java-style if-then-else blocks. For example:

if (var > 0) {
System.out.println("Greater than zero!");
}
else if (var == -1) {
System.out.println("Minus one!");
}
else {
System.out.println("Something else!");
}

Ternary Statements

Ternary statements are supported just as in Java:

var > 0 ? "Yes" : "No";

And nested ternary statements:

var > 0 ? "Yes" : (var == -1 ? "Minus One!" : "No")

Foreach

One of the most powerful features in MVEL is it’s foreach operator. It is similar to the for each operator in Java 1.5 in both syntax and functionality. It accepts two parameters separated by a colon, the first is the local variable for the current element, and the second is the collection or array to be iterated.

For example:

count = 0;
foreach (name : people) {
count++;
System.out.println("Person #" + count + ":" + name);
} System.out.println("Total people: " + count);

Since MVEL treats Strings as iterable objects you can iterate a String (character by character) with a foreach block:

str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

foreach (el : str) {
System.out.print("["+ el + "]");
}

The above example outputs:

[A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]

You can also use MVEL to count up to an integer value (from 1):

foreach (x : 9) {
System.out.print(x);
}

Which outputs:

123456789

Syntax Note: As of MVEL 2.0, it is now possible to simply abbreviate ‘’foreach’’ in the same way it is in Java 5.0, by using the for keyword. For example:

for (item : collection) { ... }

For Loop

MVEL 2.0 implements standard C for-loops:

for (int i =0; i < 100; i++) {
System.out.println(i);
}

Do While, Do Until

do while and do until are implemented in MVEL, following the same convention as Java, with until being the inverse of while.

do {
x = something();
}
while (x != null);

… is semantically equivalent to …

do {
x = something();
}
until (x == null);

While, Until

MVEL 2.0 implements standard while, with the addition of the inverse until.

while (isTrue()) {
doSomething();
}

… or …

until (isFalse()) {
doSomething();
}

Projections and Folds

Put simply, projections are a way of representing collections. Using a very simple syntax, you can inspect very complex object models inside collections.

Imagine you have a collection of User objects. Each of these objects has a Parent. Now say you want to get a list of all names of the parents (assuming the Parent class has a name field) in the hierarchy of users, you would write something like this:

parentNames = (parent.name in users);

You can even perform nested operations. Imagine instead, that the User object had a collection member called familyMembers, and we wanted a list of all the family members names:

familyMembers = (name in (familyMembers in users));

Assignments

MVEL allows you assign variable in your expression, either for extraction from the runtime, or for use inside the expression.

As MVEL is a dynamically typed language, you do not have to specify a type in order to declare a new variable. However, you may optionally do so.

str = "My String"; // valid
String str = "My String"; // valid

Unlike Java however, MVEL provides automatic type conversion (when possible) when assigning a value to a typed variable. For example:

String num = 1;
assert num instanceof String &amp;&amp; num == "1";

For dynamically typed variables where you simply want to perform a type conversion, you may simply cast the value to the type you desire:

num = (String) 1;
assert num instanceof String &amp;&amp; num == "1";

Function Definition

MVEL allows the definition of native functions with either the def or function keywords.

Functions are defined in-order of declaration, and cannot be foreword referenced. The only exception to this is ‘’within’’ functions themselves, it is possible to forward reference another function.

A Simple Example

‘’Defining a Simple Function’’

def hello() { System.out.println("Hello!"); }

This defines a simple function called “hello” that accepts no parameters. When called it prints “Hello!” to the console. An MVEL-defined function works just like any regular method call, and resolution preference is to MVEL functions over base context methods.

hello(); // calls function

Accepting Parameters and Returning Values

Functions can be declared to accept parameters, and can return a single value. Consider the following example:

def addTwo(a, b) {
a + b;
}

This function will accept two parameters (a and b) and then adds the two variables together. Since MVEL uses the ‘’last-value-out’’ principle, the resultant value will be returned. Therefore, you can use the function as follows:

val = addTwo(5, 2);
assert val == 10;

The return keyword can also be used to force a return a value from within the internal program flow of the function.

Closures

MVEL allows closures. However the functionality is not interoperable with native Java methods.

// define a function that accepts a parameter
def someFunction(f_ptr) { f_ptr(); } // define a var
var a = 10; // pass the function a closure
someFunction(def { a * 10 });

Lambda Expressions

MVEL allows the definition of lambda functions.

Example

‘’A simple lambda expression’’

threshold = def (x) { x >= 10 ? x : 0 }; result = cost + threshold(lowerBound);

The above example defines a lambda and assigns it to the variable “threshold”. Lambda’s are essentially functions that are assignable to variables. They are essentially closures and can be used as such.

Interceptors

MVEL provides the ability to place interceptors within a ‘’compiled’’ expression. This can be particularly useful for implementing change listeners or firing external events based from within an expression.

An interceptor declaration uses the ‘’@Syntax’’, similar to annotations in Java.

You declare interceptors before a statement you wish to enclose, and as such, an interceptor may implement either before or after listeners, or both. For example:

@Intercept
foreach (item : fooItems) {
total += fooItems.price;
}

In this particular statement, the interceptor encloses the entire foreach block, so if the interceptor implements the after listener, that action will be fired when the foreach has completed.

The org.mvel.integration.Interceptor Interface

public interface Interceptor {
public int doBefore(ASTNode node, VariableResolverFactory factory);
public int doAfter(Object exitStackValue, ASTNode node, VariableResolverFactory factory);
}

The Interceptor interface provides two methods to be implemented: doBefore and doAfter. Here we will describe the meaning of the values passed into the methods from the MVEL runtime.

doBefore

The doBefore method is called before the enclosing statement is executed.

org.mvel.ASTNode::node

The handle to the ASTNode is a reference to the ASTNode which is wrapped by the interceptor. This can be used to obtain information about the actual compiled code.

About AST API

While access to the AST is provided, there is no guarantee that changes to the underlying AST API will not be changed in maintenance releases and subsequent revisions to MVEL, as certain bug fixes and performance improvements. Also certain compiler options may effect the structure of the AST and produce unexpected results, so understand this is an advanced feature.

org.mvel.integration.VariableResolverFactory::factory

The variable resolver factory provides access to the current scope of variables in the expression. Please see 3Variable Resolvers for more information.

doAfter

The doAfter method is called after the enclosing statement has executed.

java.lang.Object::exitStackValue

The doAfter method is executed after the enclosing statement has executed, but ‘’not’’ before the end of the frame. Therefore, any value left on the stack at the end of the operation will still be present and therefore accessible to the interceptor. For example:

    @Intercept cost += value;

In this particular case, the reduced value of ‘’cost = cost + value’’ will be present on the stack until the end of the execution frame, and therefore this value will be available as the exitStackValue in the doAfter call.

org.mvel.ASTNode::node

This is the same AST element as is passed to doBefore. See the last section for more details.

org.mvel.integration.VariableResolverFactory::factory

See the last section for more details.

Using Interceptors in the Compiler

Interceptors must be provided to the compiler prior to compiling the expression in order to wire the interceptors into the expression. So it should be noted here that interceptors ‘’may not’’ be used with the MVEL interpreter.

A Map is used to provide the compiler with the interceptors with the key representing the name of the interceptor, and the value being the interceptor instance.

For example:

// Create a new ParserContext
ParserContext context = new ParserContext(); Map<String, Interceptor> myInterceptors = new HashMap<String, Interceptor>(); // Create a simple interceptor.
Interceptor myInterceptor = new Interceptor() {
public int doBefore(ASTNode node, VariableResolverFactory factory) {
System.out.println("BEFORE!");
} public int doAfter((Object value, ASTNode node, VariableResolverFactory factory) {
System.out.println("AFTER!");
}
}; // Now add the interceptor to the map.
myInterceptors.put("Foo", myInterceptor); // Add the interceptors map to the parser context.
context.setInterceptors(myInterceptors); // Compile the expression.
Serializable compiledExpression = MVEL.compileExpression(expression, context);

Serializability of Expressions

If you intend to serialize a compiled expression for re-use across multiple runtime instances, you must ensure that your interceptors are, themselves, Serializable otherwise you will encounter serialization problems since your Interceptor ‘’instance’’ is added to the AST directly.

Typing

MVEL is dynamically typed language, with static typing. Most users of MVEL will rely simply on dynamic typing. It’s simple, and easy to work with.

a = 10; // declare a variable 'a'
b = 15; // declare a variable 'b'; a + b;

Dynamic Typing and Coercion

The most important aspect of a language like MVEL, which interacts directly with native Java objects, which themselves are statically typed, is that of ‘’type coercion’‘. Since MVEL cannot truly treat say, a java.lang.String object as a java.lang.Integer object for math operations, it must be able to’‘coerce’’ one type to another.

Performance Considerations

This is of course a serious performance consideration when using something like MVEL as an integration point at a ‘’hot spot’’ of your application. For heavy processing load, coercion overload is mitigated by caches and the MVEL optimizers (available only for pre-compiled expressions). However it is not always possible to bypass the overhead of a hard coercion operation depending on what is being done.

However, if String variables are being injected into the runtime to be evaluated as Integers, it is simply not possible to prevent the runtime from needing to parse the strings into new Integer objects. This should always be considered.

Method Calls

Calling of methods is one of the most important aspects of coercion. Fundamentally, it allows you to directly call method without the need to finesse the inputs. Instead the interpreter or compiler will analyze the types being passed to the method, determine what coercion needs to be done to accomplish the call, and in the case of overloaded methods, choose the method which most closely matches the input types by avoiding using coercion if possible.

Arrays

Arrays are one of the more interesting aspects of the coercion system in MVEL, in that, MVEL uses untyped arrays by default (meaning Object[]arrays for all intents and purposes). Only when faced with an array type conflict, will MVEL attempt to ‘’coerce’’ the entire array to the needed input type. For example: in the case of a method call parameter.

Example:

myArray = {1,2,3};

// pass to method that accepts String[]
myObject.someMethod(myArray);

In this particular case, someMethod() accepts a String[] array. This will not fail in MVEL, instead MVEL will convert the array to a String[].

Static Typing

Static typing in MVEL functions just as it does in Java, but by default still works in concert with coercion.

int num = 10;

This declares a typed integer variable called num. When you do this, the MVEL runtime will enforce the type. For example, trying to assign an incompatible type after the declaration will result in an exception:

num = new HashMap(); // will throw an incompatible typing exception.

However, MVEL ‘’will’’ perform coercion to a statically typed variable if the value being assigned is coercable.

num = "100"; // will work -- parses String to an integer.

Only once in scope

Once you have declared a statically typed variable, you cannot declare a new variable of the same name within the scope. For example:

int i = 100;
int i = 200; // will throw an exception.

Strict Typing

Strict typing in MVEL is an optional mode for the compiler, in which all types must be fully qualified, either by declaration or inference.

Enabling Strict Mode

When compiling an expression, the compiler can be put into strict mode through the ParserContext by setting setStrictTypeEnforcement(true).

Satisfying type strictness can be accomplished both by requiring explicit declaration of types inside the expression, or by notifying the parser ahead of time of what the types of certain inputs are.

For example:

ExpressionCompiler compiler = new ExpressionCompiler(expr);

ParserContext context = new ParserContext();
context.setStrictTypeEnforcement(true); context.addInput("message", Message.class);
context.addInput("person", Person.class); compiler.compile(context);

In this example we inform the compiler that this expression will be accepting two external inputs: message and person and what the types of those inputs are. This allows the compiler to determine if a particular call is safe at compile time, instead of failing at runtime.

Shell

The MVEL interactive shell allows you to directly interface with MVEL, and explore features of the MVEL language.

See also https://en.wikipedia.org/wiki/Drools https://en.wikipedia.org/wiki/Unified_Expression_Language

Launching the Shell

To launch the MVEL shell, simply run the MVEL distribution JAR as an executable jar:

java -jar mvel.jar

Alternatively, you can run the shell from within your favorite IDE by setting up a run profile for the class: org.mvel2.sh.Main.

Language FAQ

Why doesn’t the .class reference work?

MVEL does not have a special ‘’.class’’ identifier to refer to type literals like Java. There are no class literals per se. Instead you refer to a class reference simply by its name. For example, if a method accepts type Class as a parameter you would call it just like this:

// MVEL
someMethod(String); // Java-equivalent
someMethod(String.class);

In fact, MVEL treats ‘’.class’’ as a regular bean property. So by writing String.class the value returned will be an instance of java.lang.Classreferring to java.lang.Class itself, since it would be the equivalent of writing String.class.getClass() in Java.

The principle reason for this, is that MVEL has a dynamic type system which treats types as regular variables, rather than qualified type-literals like in Java. And as such, MVEL allows for class types to be referenced as ordinary variables unlike Java, allowing for type-aliasing.

Why can’t I write object.class.name?

This is a limitation which may be addressed in a future version of MVEL, but bean properties are ‘’not’’ supported against Class references. This does not mean that you cannot call methods of Class, but that you must use full qualified method calls like:

someVar.class.getName();     // Yes!
someVar.class.name; // No! someVar.getClass().getName() // Yes!
someVar.getClass().name // No!

This limitation is completely limited to java.lang.Class as a consequence of property-resolution orders and the way that MVEL deals with class references.

MVEL 2.0 Templating Guide

MVEL 2.0 offers a new, more powerful, and unified templating engine to bring together many of the template concepts that were introduced in 1.2. Unfortunately, the architecture of the template engine in 1.2 was inadequate for regular maintenance, and the decision to completely re-write the template engine from the ground up was made.

MVEL 2.0 Basic Templating

MVEL Templates are comprised of orb-tags inside a plaintext document. Orb-tags denote dynamic elements of the template which the engine will evaluate at runtime.

If you are familiar with FreeMarker, this type of syntax will not be completely alien to you.

A Simple Template

Hello, @{person.getSex() == 'F' ? 'Ms.' : 'Mr.'} @{person.name}

This e-mail is to thank you for your interest in MVEL Templates 2.0.

This template shows a simple template with a simple embedded expression. When evaluated the output might look something like this:

Hello, Ms. Sarah Peterson

This e-mail is to thank you for your interest in MVEL Templates 2.0.

Escaping the @ Symbol

Naturally, since the @ symbol is used to denote the beginning of an orb-tag, you may need to escape it, to prevent it from being processed by the compiler. Thankfully, there is only one situation where this is necessary: when you actually need to produce the string ‘@{’ as output in your template.

Since the compiler requires a combination of @ and { to trigger the orb recognition, you can freely use @ symbols without escaping them. For example:

Email any questions to: foo@bar.com

@{date}
@include{'disclaimer.html'}

But in the case where you need an @ symbol up-against an orb-tag, you will need to escape it by repeating it twice:

@{username}@@@{domain}

That’s two @’s to escape one symbol, and the third @ being the beginning of the tag. If this looks too messy, you can always use the alternate approach of using an expression tag, like this:

@{username}@{'@'}@{domain}

MVEL 2.0 Orb Tags

This page contains a list of all orb-tags available out-of-the-box in the MVEL 2.0 templating engine.

@{} Expression Orb

The expression orb is the most rudimentary form of orb-tag. It contains a value expression which will be evaluated to a string, and appended to the output template. For example:

Hello, my name is @{person.name}

@code{} Silent Code Tag

The silent code tag allows you to execute MVEL expression code in your template. It does not return a value and does not affect the formatting of the template in any way.

@code{age = 23; name = 'John Doe'}
@{name} is @{age} years old.

This template will evaluate to: John Doe is 23 years old.

@if{}@else{} Control Flow Tags

The @if{} and @else{} tags provide full if-then-else functionality in MVEL Templates. For example:

@if{foo != bar}
Foo not a bar!
@else{bar != cat}
Bar is not a cat!
@else{}
Foo may be a Bar or a Cat!
@end{}

All blocks in MVEL Templates must be terminated with an @end{} orb, except in cases of an if-then-else structure, where @else{} tags denote the termination of the previous control statement.

@foreach{} Foreach iteration

The foreach tag allows you to iterate either collections or arrays in your template. Note: that the syntax for foreach has changed in MVEL Templates 2.0 to standardize the foreach notation with that of the MVEL language itself.

@foreach{item : products}
- @{item.serialNumber}
@end{}

MVEL 2.0 requires you specify an iteration variable. While MVEL 1.2 assumed the name item if you did not specify an alias, this has been dropped due to some complaints about that default action.

Multi-iteration

You can iterate more than one collection in a single foreach loop at one time by comma-separating the iterations:

@foreach{var1 : set1, var2 : set2}
@{var1}-@{var2}
@end{}

Delimiting

You can automatically add a text delimiter to an iteration by specifying the iterator in @end{} tag.

@foreach{item : people}@{item.name}@end{', '}

This would return something like: John, Mary, Joseph.

@include{} Include Template File

You may include a template file into an MVEL template using this tag.

@include{'header.mv'}

This is a test template.

You may also execute an MVEL expression inside an include tag by adding a semicolon after the template name:

@include{'header.mv'; title='Foo Title'}

@includeNamed{} Include a Named Template

Named templates are templates that have been precompiled and passed to the runtime via a TemplateRegistry, or templates that have been declared within the template itself. You simply include:

@includeNamed{'fooTemplate'}
@includeNamed{'footerTemplate', showSomething=true}

You may also execute MVEL code in an @includeNamed{} tag, just as with the @include{} tag.

@declare{} Declare a Template

In addition to including external templates from external files, and passing them in programmatically, you can declare a template from within a template. Which allows you to do things like this:

@declare{'personTemplate'}
Name: @{name}
Age: @{age}
@end{} @includeNamed{'personTemplate'; name='John Doe'; age=22}

@comment{} Comment tag

The comment tag allows you add an invisible comment to the template. For example:

@comment{
This is a comment
}
Hello: @{name}!

MVEL 2.0 Template Integration

Using MVEL templates is straight-forward and easy. Like regular MVEL expressions, they can be executed interpretively, or be pre-compiled and be re-used for faster evaluation.

The org.mvel.templates.TemplateRuntime Class

The TemplateRuntime class is the center of the template engine. You can pass a template to be evaluated to the template engine by way of the eval() method.

In general, the template engine follows all the same rules for context and variable binding, with an overloaded set of eval() methods.

Here’s a simple example of parsing a template interpretively:

String template = "Hello, my name is @{name.toUpperCase()}");
Map vars = new HashMap();
vars.put("name", "Michael"); String output = (String) TemplateRuntime.eval(template, vars);

At the end of execution, the “output” variable will contain the string:

Hello, my name is MICHAEL

The org.mvel.templates.TemplateCompiler Class

The TemplateCompiler class allows for pre-compilation of the templates.

When you compile a template, a compact, reusable evaluation tree is produced that can be quickly used to evaluate a template. It is used straightforwardly:

String template = "1 + 1 = @{1+1}";

// compile the template
CompiledTemplate compiled = TemplateCompiler.compileTemplate(template); // execute the template
String output = (String) TemplateRuntime.execute(compiled);

At the end of execution, the “output” variable will contain the string:

1 + 1 = 2

MVEL 2.x语法指南

MVEL全称为:MVFLEX Expression Language,是用来计算Java语法所编写的表达式值的表达式语言。MVEL的语法很大程度上受到Java语法的启发,但为了使表达式语法更高效,还是有一些基本差异,例如可以像正则表达式一样直接支持集合、数组和字符串匹配的运算。

除了表达式语言之外,MVEL还用作配置和字符串构造的模板语言。这里还有一个关于MVEL介绍信息的wiki页面是:https://en.wikipedia.org/wiki/MVEL。

MVEL 2.x表达式主要包括以下特性:

  • 属性表达式
  • 布尔表达式
  • 方法调用
  • 变量赋值
  • 函数定义

一、基本语法

MVEL是基于Java语法的表达式语言,具有特定于MVEL的一些明显差异。与Java不同,MVEL是动态类型化(可选类型化),意味着在源代码中不需要类型限定。

MVEL可以方便的集成到产品中使用。Maven的集成方式如下:

<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.2.8.Final</version>
</dependency>

一个MVEL表达式,简单的可以是单个标识符,复杂的则可能是一个充满了方法调用和内部集合创建的庞大的布尔表达式。使用MVEL提供的API。可以动态得到表达式的执行结果。

1. 简单属性表达式

user.name

在这个表达式中,我们只有一个标识符(user.name),在MVEL中我们称它为属性表达式,因为表达式的唯一目的就是从上下文中提取出变量或者对象的属性。属性表达式是最常见的用途之一,通过它,MVEL可以用来作为一个高性能,易使用的反射优化器。

MVEL甚至可以用来计算布尔表达式:

user.name =='John Doe'

与Java一样,MVEL支持所有优先级规则,包括通过括号来控制执行顺序。

(user.name == 'John Doe') && ((x * 2) - 1) > 20

2. 复合语句

您可以使用分号来表示语句的终止,使用任意数量的语句编写脚本。分号在所有情况下都是必需的,除非在脚本中只有一个语句或最后一个语句。

statement1; statement2; statement3

注意:statement3语句后可以缺少分号。

另外,换行不能替代分号来作为一个语句的结束标识。

3. 返回值

MVEL是被设计为一个集成语言作为核心,允许开发人员提供简单的脚本设置绑定和逻辑。因此,MVEL表达式使用“last value out”原则(输出最后值原则)。这意味着,尽管MVEL支持return关键字,但却没必要使用它。例如:

a = 10;
b = (a = a * 2) + 10;
a;

在该示例中,表达式返回a的值,因为a;是表达式的最后一个值。它在功能上与下面的脚本等价:

a = 10;
b = (a = a * 2) + 10;
return a;

二、值判断

在MVEL中所有的判断是否相等,都是对值的判断,而没有对引用的判断,因此表达式foo == 'bar'等价于Java中的foo.equals("bar")

1. 判断空值

MVEL提供了一个特殊的字符来表示值为空的情况,叫作empty,例如:

foo == empty

若foo满足空的任何条件,这个表达式值都为true。

2. 判断Null值

MVEL中,nullnil都可以用来表示一个Null值,如:

foo == null;
foo == nil; // 和null一样

3. 强制转换

当两个不同类型且没有可比性的值进行比较时,MVEL会应用类型强制转换系统,即将左边的值强制转换成右边的值的类型,反之亦然。如:

"123" == 123;

这个表达式的值为true,因为为了执行比较,强制类型转换系统会隐式的将数字123转换成字符串。

三、内联Lists、Maps和数组Arrays

MVEL允许你使用简单优雅的语法来表示Lists,Mpas和数组Arrays。 且看下面的示例:

["Bob" : new Person("Bob"), "Michael" : new Person("Michael")]

这个表达式的功能等价于:

Map map = new HashMap();
map.put("Bob", new Person("Bob"));
map.put("Michael", new Person("Michael"));

用这种结构描述MVEL内部数据结构,功能非常强大,你可以在任何地方使用它,甚至可以作为方法的参数使用,如:

something.someMethod(["foo" : "bar"]);

1. Lists

Lists用以下格式来表示:"[item1, item2, ...]",如:

["Jim", "Bob", "Smith"]

2. Maps

Maps用以下格式来表示:"[key1 : value1, key2: value2, …]",如:

["Foo" : "Bar", "Bar" : "Foo"]

3. 数组Arrays

数组Arrays用以下格式来表示:"{item1, item2, …}",如:

{"Jim", "Bob", "Smith"}

4. 数组强制转换

关于内联数组,需要知道的一个非常重要的方面是,它可以被强制转换成其它类型的数组,当你声明一个数组时,是不直接指定其类型的,但你可以通过将其传递给一个接收int[]类型参数的方法来指定。如:

foo.someMethod({1,2,3,4});

在这种情况下,当MVEL发现目标方法接收的是一个int[],会自动的将{1,2,3,4}转换成int[]类型。

四、属性导航

MVEL属性导航遵循在其他语言(如Groovy,OGNL,EL等)中bean属性表达式中公认惯例的使用方式。和其它语言必须通过底层的方法来控制权限不同的是,MVEL提供了一种单一的,统一的语法来访问属性,静态字段和maps等。

1. Bean属性

大多数java开发者都熟悉getter/setter模式,并在java对象中用它来封装属性的访问权限。例如,你可能会通过下面的方式访问一个对象的属性:

user.getManager().getName();

为了简化此操作,您可以使用以下表达式访问相同的属性:

user.manager.name

注意:当一个对象中的字段的作用域是public时,MVEL仍然倾向于通过get方法来访问其属性。

2. Bean的安全属性导航

有时,当你的表达式中会含有null元素时,这时就需要你进行一个为空判断,否则就会发生错误。当你使用null-safe操作符时你可以简化这个操作:

user.?manager.name

它的功能相当于:

if (user.manager != null) { return user.manager.name; } else { return null; }

3. 集合

集合的遍历也可以通过简单的语法来实现:

(1). List的访问

List可以像访问数组一样访问,如:

user[5]

这等价与java中的代码:

user.get(5);

(2). Map的访问

Map的访问和访问数组也非常相似,不同的是,在访问Map时索引值可以是任意对象,如:

user["foobar"]

这等价与java中的代码:

user.get("foobar");

当Map的key是String类型时,还可以使用特殊的方式来访问,如:

user.foobar

4. 字符串作数组

为了能使用属性的索引(迭代也是如此),所有的字符串都可以看成是一个数组,在MVEL中你可以用下面的方式来获取一个字符串变量的第一个字符:

foo = "My String";
foo[0]; // returns 'M'

五、文字常量

在脚本语言中,一段文字(常量)用来代表一个固定的值。

1. 字符串常量

字符串常量可以用一对单引号或一对双引号来界定。如:

"This is a string literal"
'This is also string literal'

字符串转义字符

  • \ - 代表一个反斜杠。
  • \n - 换行符
  • \r - 回车符
  • \u#### - Unicode字符 (如: /uAE00)
  • ### - 八进制字符 (如: /73)

2. 数字常量

整数可以表示为十进制(基数为10),8进制(基数为8),或十六进制(基数为16)。

一个十进制数字,不从零开始(相对于8进制、16进制而言),可以表示任意数,如:

125 // 十进制

一个八进制数,以0为前缀,后面跟着0到7内的数字。

0353 // 八进制

一个十六进制,以0X为前缀,后面可以跟着0-9,A-F范围内的数字。

0xAFF0 // 十六进制

3. 浮点型常量

浮点数由整数和由点/周期字符表示的小数部分组成,带有可选的类型后缀。

10.503 // double型
94.92d // double型
14.5f // float型

4. 大数字常量

您可以使用后缀B和I(必须大写)来表示BigDecimal和BigInteger文字,如:

104.39484B // BigDecimal
8.4I // BigInteger

5. 布尔常量

布尔型常量用保留关键字true和false来表示。

6. 空常量

用null或nil来表示。

六、类型常量

类型常量的处理方式与Java中的相同,格式为:"."。

所以一个类可以这样限定:

java.util.HashMap

或者如果类已经通过或者通过外部配置被导入,则它被简单地通过其非限定名称来引用:

HashMap

嵌套类

嵌套类不能通过MVEL 2.0中的标准点表示法(如Java中)来访问。 相反,你必须用$符号限定这些类。

org.proctor.Person$BodyPart

七、流程控制

MVEL的强大已经超出了简单的表达式。事实上,MVEL提供了一系列的程序流程控制操作符来方便你进行高级的脚本操作。

1. If-Then-Else

MVEL提供了完整的C/Java式的if-then-else块,如:

if (var > 0) {
System.out.println("Greater than zero!");
} else if (var == -1) {
System.out.println("Minus one!");
} else {
System.out.println("Something else!");
}

2. 三目运算符

其实就是Java中的条件表达式,如:

var > 0 ? "Yes" : "No";

可以嵌套三目运算符

var > 0 ? "Yes" : (var == -1 ? "Minus One!" : "No")

3. Foreach

MVEL的强大特性之一就是其Foreach操作符,在功能和语法上,他都类似于java1.5中的for each操作符,它接收用冒号隔开的两个参数,第一个是当前元素的一个域变量,而第二个是要迭代的集合或数组。如下所示:

count = 0;
foreach (name : people) {
count++;
System.out.println("Person #" + count + ":" + name);
} System.out.println("Total people: " + count);

因为MVEL将字符串视作一个可以迭代的对象,所以你可以用foreach语句来迭代一个字符串(一个字符接一个字符的):

str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

foreach (el : str) {
System.out.print("["+ el + "]");
}

上面的示例将会输出:

[A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]

你也可以利用MVEL进行计数(从1开始):

foreach (x : 9) {
System.out.print(x);
}

这会输出:

123456789

注意:像java5.0一样,在MVEL2.0中,可以将foreach简化成关键字for来使用,如:

for (item : collection) { ... }

4. for循环

MVEL实现了标准的C语言的for循环:

for (int i =0; i < 100; i++) {
System.out.println(i);
}

5. Do While, Do Until

和java中的意义一样,MVEL也实现了Do While,Do Until,While和Until意义正好相反。

do {
x = something();
}
while (x != null);

在语义上相当于:

do {
x = something();
}
until (x == null);

6. While, Until

MVEL中实现了标准的While,并添加了一个与之相反的Until。

while (isTrue()) {
doSomething();
}

或者写成

until (isFalse()) {
doSomething();
}

八、投影和交集

简单地说,投影是一种描述集合的方式。 通过非常简单的语法,您可以检索集合中非常复杂的对象模型。

假设,你有一个User对象的集合。 每个对象都有一个Parent。 现在你想获得集合users中的所有parent的name的列表(假设Parent中有字段name),你可以这样来写:

parentNames = (parent.name in users);

您甚至可以执行嵌套操作,假设,User对象有个集合成员叫做familyMembers,现在我们想获得一个所有家庭成员姓名的集合:

familyMembers = (name in (familyMembers in users));

九、赋值

MMVEL允许你对表达式中的变量进行赋值,以便在运行时获取,或在表达式内部使用。因为MVEL是动态类型语言,所以你不必为了声明一个变量而指定其类型。当然,你也可以选择指定。

str =“My String”; // valid
String str =“My String”; // valid

与java语言不同的是,当给一个指定类型的变量赋值时,MVEL会提供自动的类型转换(可行的话),如:

String num = 1;
assert num instanceof String&amp;&amp; num ==“1”;

对于动态类型变量而言,你要想对其进行类型转换,你只需要将值转换成相应的类型既可:

num =(String)1;
assert num instanceof String&amp;&amp; num ==“1”;

十、函数定义

MVEL可以使用def或function关键字来定义本地函数。

函数必须是先声明后引用,唯一例外的是递归调用的时候。

1. 一个简单示例

定义一个简单函数:

def hello() { System.out.println("Hello!"); }

定义了一个没有参数的函数hello.当调用该函数时会在控制台打印"Hello!" 一个MVEL定义的函数就像任何常规的方法调用。

hello(); // 调用函数

2. 传参和返回值

函数可以接收参数和返回一个值,看下面的示例:

def addTwo(a, b) {
a + b;
}

这个函数会接收两个参数(a和b),然后将这两个变量相加。因为MVEL遵循last-value-out原则,所以结果将会被返回。因此,你可以这样来使用这个函数:

val = addTwo(5, 2);
assert val == 10;

当然,也可以使用return关键字来强制从程序内部返回一个函数值。

3. 闭包

MVEL支持闭包,然而其功能与本地java函数没有任何关联。

// 定义一个接收一个参数的函数
def someFunction(f_ptr) { f_ptr(); } // 定义变量a
var a = 10; // 传递函数闭包
someFunction(def { a * 10 });

十一、Lambda表达式

MVEL允许定义Lambda方法,如下所示:

threshold = def (x) { x >= 10 ? x : 0 };
result = cost + threshold(lowerBound);

上面的例子定义了一个Lambda,并将其赋值给变量"threshold".Lambda实质上就是一个用来给变量赋值的函数,也是闭包。

http://blinkfox.com/mvel-2-xyu-fa-zhi-nan/

MVEL 2.x语法指南

https://github.com/mvel/mvel

mvel的更多相关文章

  1. Drools mvel方言drl断点调试方法

    开发环境:myeclipse2014,  jdk1.8.0.91,drools6.4.0.Final, drools-eclipse-plugin,mvel2-2.2.6.Final问题描述:drl使 ...

  2. [Mvel]Mvel2.0使用指南一 基础

    MVEL在很大程度上受到Java语法的启发,作为一个表达式语言,也有一些根本的区别,旨在更高的效率,例如:直接支持集合.数组和字符串匹配等操作以及正则表达式. MVEL用于执行使用Java语法编写的表 ...

  3. mvel语法指南[翻译]

    mvel受到了java语法的启发,但是存在一些根本性的差异,mvel旨在使其成为更有效的表达式语言.比如直接支持集合.数组和字符串匹配,正则表达式的运算操作等. mvel2.x有以下几个部分组成:  ...

  4. mvel 配合正则表达式实现文本替换

    mvel 依赖 <dependency> <groupId>org.mvel</groupId> <artifactId>mvel2</artif ...

  5. ES 学习总结

    ES 总结: es 是基于lucene的, 是java 实现的, 很多概念和lucene是相同的 索引-- 对应数据库的表,mongoDB中的集合 文档,由字段组成, 一个字段可以出现多次. 字段,其 ...

  6. Drools 规则学习

    Drools 规则学习 在 Drools 当中,一个标准的规则文件就是一个以“.drl”结尾的文本文件,由于它是一个标准的文本文件,所以可以通过一些记事本工具对其进行打开.查看和编辑.规则是放在规则文 ...

  7. Elasticsearch 教程--数据

    在Elasticsearch中,每一个文档都有一个版本号码.每当文档产生变化时(包括删除),_version就会增大.在<版本控制>中,我们将会详细讲解如何使用_version的数字来确认 ...

  8. #研发解决方案介绍#基于ES的搜索+筛选+排序解决方案

    郑昀 基于胡耀华和王超的设计文档 最后更新于2014/12/3 关键词:ElasticSearch.Lucene.solr.搜索.facet.高可用.可伸缩.mongodb.SearchHub.商品中 ...

  9. 生成yyMMddHHmmssSS时间戳代码作为唯一主键值

    import java.sql.Time; import java.text.DateFormat; import java.text.ParseException; import java.text ...

随机推荐

  1. js一点代码备用

    加载次序 .1等页面加载完毕 <script type="text/javascript"> jQuery(window).load(function(){ ... } ...

  2. Swift中UIView类方法(animateWithDuration)的使用

    需求:利用Swift语言实现OC语言中UIView类方法 [UIView animateWithDuration:0.5 animations:^{ bgView.alpha= 1; }]; 在Swi ...

  3. 0049 MyBatis关联映射--一对一关系

    世上的事务总不是孤立存在的,表现在Java类里面,则是类与类之间的关系,比如继承is-a.依赖use-a.关联has-a,反映在数据库中,则是表与表之间的关系,比如外键 关联关系存在着以下几种类型:一 ...

  4. struts-tiles学习笔记

    网上搜了一些,稀里糊涂的,要么是代码不全,要么是版本不对,还是去struts官网大概学习了一下 http://struts.apache.org/development/1.x/struts-tile ...

  5. Entity简单使用

    urlEntity: //定义 package com.example.cc.ecustapp.Model; /** * Created by weijiawang on 2016/3/8. */pu ...

  6. EMQ进行HttpApi登录问题

    今天进行EMQ http api调用的时候遇到一个问题,一直弹出登录验证框 在官网资料中也找不到相关的接口,如下图: 以前也经常看到这种登录,不过我这里没有用程序去调用过这样类似的接口. 后来我想到经 ...

  7. AJAX防止多次请求

    ajax诠释 ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式. ajax所包含 ...

  8. java 关键字static

    在Java中static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,当然也可以修饰代码块. Java把内存分为栈内存和堆内存, 栈内存用来存放一些基本类型的变量.数组和对象的引用, 堆 ...

  9. java常用 api

    java -cp .:/app/jenkins/ojdbc6-11.2.0.1.0.jar  QueryInterTestImpl onclick="add_win.after(initVa ...

  10. DevExpress 控件使用技巧

    DevExpress是非常主流的.NET控件,眼下全世界和中国都用非常多用户使用,只是因为是英文版,初次接触的同学可能会认为困难.这里就总结DevExpress常见的10个使用技巧. 1.TextEd ...