zoukankan      html  css  js  c++  java
  • mvel

    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
    Mvel logo.png
    Developer(s) Mike Brock and Various Contributors
    Stable release
    2.3.0 / June 15, 2016
    Repository Edit this at Wikidata
    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.
    •  - Newline
    •  - 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'  
    

    字符串转义字符

    • - 代表一个反斜杠。
    • - 换行符
    • - 回车符
    • 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

  • 相关阅读:
    MCV 和 MTV框架基本信息
    Django的一些操作与视图函数
    Django模板渲染
    python--jQuery
    MEF初体验之五:Lazy Exports
    MEF初体验之四:Imports声明
    MEF初体验之三:Exports声明
    MEF初体验之二:定义组合部件和契约
    MEF初体验之一:在应用程序宿主MEF
    WPF学习(12)动画
  • 原文地址:https://www.cnblogs.com/rsapaper/p/9848957.html
Copyright © 2011-2022 走看看