kenh.expl.Environment.java Source code

Java tutorial

Introduction

Here is the source code for kenh.expl.Environment.java

Source

/*
 * ExpL (Expression Language)
 * Copyright 2014 and beyond, Kenneth Huang
 * 
 * This file is part of ExpL.
 * 
 * ExpL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 * 
 * ExpL is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with ExpL.  If not, see <http://www.gnu.org/licenses/>. 
 */

package kenh.expl;

import java.util.*;

import kenh.expl.impl.ExpLParser;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Environment for ExpL. 
 * 
 * @author Kenneth
 * @since 1.0
 *
 */
public class Environment implements Callback {

    /**
     * Map for variable
     */
    private Map<String, Object> variables = Collections.synchronizedMap(new LinkedHashMap());

    /**
     * Map for function package, let Environment know where to load function.
     */
    private Map<String, String> functionPackages = Collections.synchronizedMap(new LinkedHashMap());

    /**
     * Function package loaded by system properties should use this prefix 
     */
    private static final String FUNCTION_PATH_PREFIX = "kenh.expl.function.packages";

    /**
     * The parser
     */
    private Parser parser = null;

    /**
     * Logger
     */
    protected static final Log logger = LogFactory.getLog(Environment.class.getName());

    /**
     * Constructor
     */
    public Environment() {
        this(null);
    }

    /**
     * Constructor
     * @param parser  The parser
     */
    public Environment(Parser parser) {
        if (parser == null)
            parser = new ExpLParser();

        setParser(parser);
        setFunctionPackage("expl", "kenh.expl.functions"); // default function package

        loadFunctionPackages_SystemProperties();
        loadFunctionPackages_Extension();
        //setVariable("@system", System.getProperties());
    }

    /**
     * Set the parser, and the parser's environment will be set.
     * @param p   The parser
     * @see Parser#setEnvironment(Environment)
     */
    public void setParser(Parser parser) {
        this.parser = parser;
        parser.setEnvironment(this);
    }

    /**
     * Parse the express. If the parser is null, then return null.
     * @param express  The expression
     * @return   
     */
    public Object parse(String express) throws UnsupportedExpressionException {
        if (parser == null)
            return null;
        return parser.parse(express);
    }

    /**
     * Parse the express. If the parser is null, then return null.
     * @param express  The expression
     * @return   
     */
    public Object[] parse(String express, boolean split) throws UnsupportedExpressionException {
        if (parser == null)
            return null;
        return parser.parse(express, split);
    }

    /**
     * Load function packages through system properties
     */
    private void loadFunctionPackages_SystemProperties() {
        Properties p = System.getProperties();
        Set keys = p.keySet();
        for (Object key_ : keys) {
            if (key_ instanceof String) {
                String key = (String) key_;
                if (StringUtils.startsWith(key, FUNCTION_PATH_PREFIX + ".")) {
                    String name = StringUtils.substringAfter(key, FUNCTION_PATH_PREFIX + ".");
                    String funcPackage = p.getProperty(key);
                    setFunctionPackage(name, funcPackage);
                }
            }
        }
    }

    /**
     * Load function package through <code>Extension</code>.
     * @see Extension
     */
    private void loadFunctionPackages_Extension() {
        ServiceLoader<Extension> es = ServiceLoader.load(Extension.class);
        for (Extension e : es) {
            if (e != null) {
                Map<String, String> p = e.getFunctionPackages();
                if (p != null && p.size() > 0) {
                    Set<String> keys = p.keySet();
                    for (String key : keys) {
                        String funcPackage = p.get(key);
                        setFunctionPackage(key, funcPackage);
                    }
                }
            }
        }
    }

    /**
     * Load <code>Function</code>. 
     * 
     * @param funcPackage  The function package.
     * @param funcName     The function name.
     * @return      Use <code>funcPackage + funcName</code> to load class, if not find, return null.
     */
    private Function getFunction_(String funcPackage, String funcName) {
        if (StringUtils.isBlank(funcPackage))
            return null;
        if (StringUtils.isBlank(funcName))
            return null;

        funcPackage = StringUtils.trimToEmpty(funcPackage);
        funcName = StringUtils.trimToEmpty(funcName);

        try {
            if (StringUtils.isNotBlank(funcPackage)) {
                Function function = (Function) Class.forName(funcPackage + "." + StringUtils.capitalize(funcName))
                        .newInstance();
                return function;
            }
        } catch (Throwable e) {
        }
        return null;
    }

    /**
     * Loop all function packages, find the first class match the function name.
     * @param funcName   The function name.
     * @return
     */
    public Function getFunction(String funcName) {
        if (StringUtils.isBlank(funcName))
            return null;

        funcName = StringUtils.trimToEmpty(funcName);

        Iterator<String> iterator = functionPackages.values().iterator();
        while (iterator.hasNext()) {
            String funcPackage = iterator.next();
            Function function = getFunction_(funcPackage, funcName);
            if (function != null)
                return function;
        }
        return null;
    }

    /**
     * Load <code>Function</code> through name space.
     * @param key       The key of function package 
     * @param funcName  The function name
     * @return
     */
    public Function getFunction(String key, String funcName) {
        if (StringUtils.isBlank(funcName))
            return null;

        key = StringUtils.trimToEmpty(key);
        funcName = StringUtils.trimToEmpty(funcName);

        if (StringUtils.isNotBlank(key)) {
            if (functionPackages.containsKey(key)) {
                return getFunction_(functionPackages.get(key), funcName);
            }
            return null;
        } else {
            return getFunction(funcName);
        }

    }

    /**
     * Add a function package
     * @param key     The key of function package
     * @param funcPackage
     * @return   If the key already exist, but function package is not the same, then return false.
     * @see Map#put(Object, Object)
     */
    public boolean setFunctionPackage(String key, String funcPackage) {
        if (StringUtils.isBlank(key))
            return false;
        if (StringUtils.isBlank(funcPackage))
            return false;

        key = StringUtils.trimToEmpty(key);
        funcPackage = StringUtils.trimToEmpty(funcPackage);

        if (functionPackages.containsKey(key)) {
            String p = functionPackages.get(key);
            if (p.equals(funcPackage))
                return true;
            else
                return false;

        } else {
            functionPackages.put(key, funcPackage);
            return true;
        }
    }

    /**
     * Remove a function package.
     * @param key    The key of function package
     * @param funcPackage
     * @return   true, if both key and function package are matching.
     */
    public boolean removeFunctionPackage(String key, String funcPackage) {
        if (StringUtils.isBlank(key))
            return false;
        if (StringUtils.isBlank(funcPackage))
            return false;

        key = StringUtils.trimToEmpty(key);
        funcPackage = StringUtils.trimToEmpty(funcPackage);

        if (functionPackages.containsKey(key)) {
            String p = functionPackages.get(key);
            if (p.equals(funcPackage)) {
                functionPackages.remove(key);
                return true;

            } else
                return false;

        } else {
            return true;
        }
    }

    /**
     * Get all variables.
     * @return
     */
    public Map<String, Object> getVariables() {
        return variables;
    }

    /**
     * Get the specified variable.
     * @param key  The key of variable.
     * @return
     */
    public Object getVariable(String key) {
        if (StringUtils.isBlank(key))
            return null;
        return variables.get(key);
    }

    /**
     * Get the variable with specify class type.
     * 
     * @param key
     * @param o
     * @return
     */
    public <T> T getVariable(String key, T o) {

        if (!this.containsVariable(key))
            throw new NullPointerException("Can't find the variable. [" + key + "]");

        Object obj = this.getVariable(key);

        if (obj == null)
            throw new NullPointerException("Reference is null. [" + key + "]");

        Class class1 = obj.getClass();
        Class class2 = o.getClass();

        if (class2.isAssignableFrom(class1) || class2 == Object.class) {
            return (T) obj;

        } else {
            throw new NullPointerException("Can't find the variable with specify class type. [" + key + "]");
        }

    }

    /**
     * Get the variable with specify class type.
     * 
     * @param key
     * @param o
     * @return
     */
    public <T> T getVariable(String key, Class<T> c) {

        if (!this.containsVariable(key))
            throw new NullPointerException("Can't find the variable. [" + key + "]");

        Object obj = this.getVariable(key);

        if (obj == null)
            throw new NullPointerException("Reference is null. [" + key + "]");

        Class class1 = obj.getClass();
        Class class2 = c;

        if (class2.isAssignableFrom(class1) || class2 == Object.class) {
            return (T) obj;

        } else {
            throw new NullPointerException("Can't find the variable with specify class type. [" + key + "]");
        }

    }

    /**
     * Add a variable, if the key already exist, remove the old variable.
     * @param key
     * @param obj
     */
    public void setVariable(String key, Object obj) {
        if (StringUtils.isBlank(key))
            return;

        if (variables.containsKey(key)) {
            Object oldObj = variables.remove(key);
            callback(oldObj);
            variables.put(key, obj);
            logger.debug("Replace var: " + key + ", " + ((obj == null) ? "<null>" : obj.toString()));
        } else {
            variables.put(key, obj);
            logger.debug("Add var: " + key + ", " + ((obj == null) ? "<null>" : obj.toString()));
        }
    }

    /**
     * Remove the variable, if variable implement <code>Callback</code>, call back method will be called.
     * @param key  The key of variable.
     * @return
     */
    public Object removeVariable(String key) {
        return removeVariable(key, true);
    }

    /**
     * Remove the variable.
     * @param key
     * @param callback  if true and variable implement <code>Callback</code>, call back method will be called.
     * @return
     */
    public Object removeVariable(String key, boolean callback) {
        if (StringUtils.isBlank(key))
            return null;
        if (!variables.containsKey(key))
            return null;

        Object oldObj = variables.remove(key);
        boolean c = false;
        if (callback) {
            callback(oldObj);
            c = true;
        }
        if (c) {
            logger.debug("Remove var(C): " + key);
        } else {
            logger.debug("Remove var: " + key);
        }
        return oldObj;
    }

    /**
     * Check if contains the variable with specified key.
     * @param key  The key of variable.
     * @return
     */
    public boolean containsVariable(String key) {
        return variables.containsKey(key);
    }

    /**
     * Check if contains the specified object.
     * @param value   The object.
     * @return
     */
    public boolean containsValue(Object value) {
        return variables.containsValue(value);
    }

    /**
     * Invoke the call back method.
     * @param obj    The object implement <code>Callback</code>.
     * @see Callback
     */
    protected void callback(Object obj) {
        if (obj instanceof Callback) {
            ((Callback) obj).callback();
        }
    }

    @Override
    public void callback() {
        Iterator<String> iterator = variables.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object obj = variables.get(key);
            callback(obj);
        }
        variables.clear();
    }

    /**
     * Convert string to specified type. Only support primitive type.
     * @param s
     * @param c
     * @return  if s is null, return null.
     */
    public static Object convert(String s, Class c) {
        if (s == null)
            return null;
        if (c == String.class)
            return s;

        if (c == int.class || c == Integer.class) {
            return Integer.parseInt(s);
        }
        if (c == boolean.class || c == Boolean.class) {
            return Boolean.parseBoolean(s);
        }
        if (c == float.class || c == Float.class) {
            return Float.parseFloat(s);
        }
        if (c == double.class || c == Double.class) {
            return Double.parseDouble(s);
        }
        return null;
    }

    // Demo method
    public static void main(String[] args) {
        Environment env = new Environment();

        // 1) normal operation
        try {
            System.out.println(env.parse("7 % 4 = {7 mod 4}"));
            System.out.println(env.parse("1 + 1 = {1 + 1}"));
            System.out.println(env.parse("1 + 1 + 1 = {1 + {1 + 1}}"));
        } catch (UnsupportedExpressionException e) {
            e.printExpressTrace();
        }

        System.out.println("---------------");

        // 2) using variable;
        env.setVariable("var1", "2");
        env.setVariable("var2", "3");
        env.setVariable("var3", "4");
        env.setVariable("var4", new Date());
        env.setVariable("var5", "{1+5}");
        env.setVariable("var6", "{var5}");
        try {
            System.out.println(env.parse("$var2 = {$var{$var1}}"));
            System.out.println(env.parse("$var3 = {$var{$var{$var1}}}"));
            System.out.println(env.parse("{$var{$var{$var{$var1}}}}"));
            System.out.println(env.parse("$var5 = {$var5}"));
            System.out.println(env.parse("$var6 = {$var{{$var5}}}"));
        } catch (UnsupportedExpressionException e) {
            e.printExpressTrace();
        }

        System.out.println("---------------");

        // 3) using function
        try {
            System.out.println(env.parse("#contains = {#contains(123456789,{$var1})}"));
            System.out.println(env.parse("{#expl:split({'abc,def,ghi'},{','})}"));
        } catch (UnsupportedExpressionException e) {
            e.printExpressTrace();
            e.printStackTrace();
        }

        System.out.println("---------------");

        // 4) using JEXL
        try {
            System.out.println(env.parse("JEXT >> {var4.getDate()}"));
            System.out.println(env.parse("JEXT >> {'abc'}"));
            System.out.println(env.parse("{jextvar=['a', 'b', 'c']}"));
            System.out.println(env.parse("{jextvar}"));
        } catch (UnsupportedExpressionException e) {
            e.printExpressTrace();
            e.printStackTrace();
        }

        env.callback();

    }

}