Package org.jbpm.designer.expressioneditor.parser

Source Code of org.jbpm.designer.expressioneditor.parser.ExpressionParser

/**
* Copyright 2012 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jbpm.designer.expressioneditor.parser;

import org.jbpm.designer.expressioneditor.model.Condition;
import org.jbpm.designer.expressioneditor.model.ConditionExpression;

import java.text.MessageFormat;
import java.text.ParseException;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExpressionParser {

    private static final String VARIABLE_NAME_PARAM_REGEX = "[$_a-zA-Z][$_a-zA-Z0-9]*";

    public static final String KIE_FUNCTIONS = "KieFunctions.";

    private static Map<String, FunctionDef> functionsRegistry = new TreeMap<String, FunctionDef>();

    private int parseIndex = 0;

    private String expression;

    private String functionName = null;

    public static final String FUNCTION_NAME_NOT_RECOGNIZED_ERROR = "The function name \"{0}\" is not recognized by system.";

    public static final String FUNCTION_CALL_NOT_FOUND_ERROR = "Function call was not found, a token like \"" + KIE_FUNCTIONS + "functionName(variable, params)\" is expected.";

    public static final String VALID_FUNCTION_CALL_NOT_FOUND_ERROR = "The \"" + KIE_FUNCTIONS + "\" keyword must be followed by one of the following function names: \"{0}\"";

    public static final String FUNCTION_CALL_NOT_CLOSED_PROPERLY_ERROR = "Function call \"{0}\" is not closed properly, character \")\" is expected.";

    public static final String SENTENCE_NOT_CLOSED_PROPERLY_ERROR = "Script not closed properly, character \";\" is expected.";

    public static final String VARIABLE_NAME_EXPECTED_ERROR = "Variable name not found, a valid process variable name is expected.";

    public static final String PARAMETER_DELIMITER_EXPECTED_ERROR = "Parameter delimiter \",\" is expected.";

    public static final String STRING_PARAMETER_EXPECTED_ERROR = "String parameter value like \"some value\" is expected.";

    public static final String RETURN_SENTENCE_EXPECTED_ERROR = "Sentence \"{0}\" is expected.";

    public static final String BLANK_AFTER_RETURN_EXPECTED_ERROR = "Sentence \"{0}\" must be followed by a blank space or a line break.";

    private static String functionNames = null;

    static {

        //Operators for all types:

        FunctionDef isNull = new FunctionDef("isNull");
        isNull.addParam("param1", Object.class);
        functionsRegistry.put(isNull.getName(), isNull);

        //Global operators:

        FunctionDef equalsTo = new FunctionDef("equalsTo");
        equalsTo.addParam("param1", Object.class);
        equalsTo.addParam("param2", String.class);
        functionsRegistry.put(equalsTo.getName(), equalsTo);

        //Operators for String type:

        FunctionDef isEmpty = new FunctionDef("isEmpty");
        isEmpty.addParam("param1", Object.class);
        functionsRegistry.put(isEmpty.getName(), isEmpty);

        FunctionDef contains = new FunctionDef("contains");
        contains.addParam("param1", Object.class);
        contains.addParam("param2", String.class);
        functionsRegistry.put(contains.getName(), contains);

        FunctionDef startsWith = new FunctionDef("startsWith");
        startsWith.addParam("param1", Object.class);
        startsWith.addParam("param2", String.class);
        functionsRegistry.put(startsWith.getName(), startsWith);

        FunctionDef endsWith = new FunctionDef("endsWith");
        endsWith.addParam("param1", Object.class);
        endsWith.addParam("param2", String.class);
        functionsRegistry.put(endsWith.getName(), endsWith);

        // Operators for Numeric types:

        FunctionDef greaterThan = new FunctionDef("greaterThan");
        greaterThan.addParam("param1", Object.class);
        greaterThan.addParam("param2", String.class);
        functionsRegistry.put(greaterThan.getName(), greaterThan);

        FunctionDef greaterOrEqualThan = new FunctionDef("greaterOrEqualThan");
        greaterOrEqualThan.addParam("param1", Object.class);
        greaterOrEqualThan.addParam("param2", String.class);
        functionsRegistry.put(greaterOrEqualThan.getName(), greaterOrEqualThan);

        FunctionDef lessThan = new FunctionDef("lessThan");
        lessThan.addParam("param1", Object.class);
        lessThan.addParam("param2", String.class);
        functionsRegistry.put(lessThan.getName(), lessThan);

        FunctionDef lessOrEqualThan = new FunctionDef("lessOrEqualThan");
        lessOrEqualThan.addParam("param1", Object.class);
        lessOrEqualThan.addParam("param2", String.class);
        functionsRegistry.put(lessOrEqualThan.getName(), lessOrEqualThan);

        FunctionDef between = new FunctionDef("between");
        between.addParam("param1", Object.class);
        between.addParam("param2", String.class);
        between.addParam("param3", String.class);
        functionsRegistry.put(between.getName(), between);

        // Operators for Boolean type:

        FunctionDef isTrue = new FunctionDef("isTrue");
        isTrue.addParam("param1", Object.class);
        functionsRegistry.put(isTrue.getName(), isTrue);


        FunctionDef isFalse = new FunctionDef("isFalse");
        isFalse.addParam("param1", Object.class);
        functionsRegistry.put(isFalse.getName(), isFalse);

        StringBuilder functionNamesBuilder = new StringBuilder();
        functionNamesBuilder.append("{");
        boolean first = true;
        for (String functionName : functionsRegistry.keySet()) {
            if (!first) functionNamesBuilder.append(", ");
            functionNamesBuilder.append(functionName);
            first = false;
        }
        functionNamesBuilder.append("}");
        functionNames = functionNamesBuilder.toString();
    }

    public ExpressionParser(String expression) {
        this.expression = expression;
        this.parseIndex = expression != null ? 0 : -1;
    }

    public ConditionExpression parse() throws ParseException {

        ConditionExpression conditionExpression = new ConditionExpression();
        Condition condition = null;
        FunctionDef functionDef = null;

        parseReturnSentence();

        functionName = parseFunctionName();
        functionName = functionName.substring(KIE_FUNCTIONS.length(), functionName.length());
        functionDef = functionsRegistry.get(functionName);

        if (functionDef == null) throw new ParseException(errorMessage(FUNCTION_NAME_NOT_RECOGNIZED_ERROR, functionName), parseIndex);

        conditionExpression.setOperator(ConditionExpression.AND_OPERATOR);
        condition = new Condition(functionName);
        conditionExpression.getConditions().add(condition);

        String param = null;
        boolean first = true;

        for (ParamDef paramDef : functionDef.getParams()) {
            if (first) {
                first = false;
            } else {
                parseParamDelimiter();
            }

            if (Object.class.getName().equals(paramDef.getType().getName())) {
                param = parseVariableName();
            } else {
                param = parseStringParameter();
            }
            condition.addParam(param);
        }

        //all parameters were consumed
        parseFunctionClose();
        parseSentenceClose();


       return conditionExpression;
    }

    private String parseReturnSentence() throws ParseException {

        int index = nextNonBlank();
        if (index < 0) throw new ParseException(errorMessage(RETURN_SENTENCE_EXPECTED_ERROR, "return"), parseIndex);

        if (!expression.startsWith("return", index)) {
            //the expression does not start with return.
            throw new ParseException(errorMessage(RETURN_SENTENCE_EXPECTED_ERROR, "return"), parseIndex);
        }

        parseIndex = index + "return".length();

        //next character after return must be a \n or a " "
        if (!isBlank(expression.charAt(parseIndex))) throw new ParseException(errorMessage(BLANK_AFTER_RETURN_EXPECTED_ERROR, "return"), parseIndex);

        return "return";
    }

    private String parseFunctionName() throws ParseException {

        int index = nextNonBlank();
        if (index < 0) throw new ParseException(errorMessage(FUNCTION_CALL_NOT_FOUND_ERROR), parseIndex);
        String functionName = null;

        if (!expression.startsWith(KIE_FUNCTIONS, index)) throw new ParseException(errorMessage(FUNCTION_CALL_NOT_FOUND_ERROR), parseIndex);

        for(FunctionDef functionDef : functionsRegistry.values()) {
            if (expression.startsWith(KIE_FUNCTIONS+functionDef.getName()+"(", index)) {
                functionName = KIE_FUNCTIONS+functionDef.getName();
                break;
            }
        }

        if (functionName == null) throw new ParseException(errorMessage(VALID_FUNCTION_CALL_NOT_FOUND_ERROR, functionNames()), parseIndex);

        parseIndex = index + functionName.length() +1;

        return functionName;
    }

    private String parseFunctionClose() throws ParseException {
        int index = nextNonBlank();
        if (index < 0) throw new ParseException(errorMessage(FUNCTION_CALL_NOT_CLOSED_PROPERLY_ERROR, functionName), parseIndex);

        if (expression.charAt(index) != ')') throw new ParseException(errorMessage(FUNCTION_CALL_NOT_CLOSED_PROPERLY_ERROR, functionName), parseIndex);

        parseIndex = index +1;
        return ")";
    }

    private String parseSentenceClose() throws ParseException {
        int index = nextNonBlank();
        if (index < 0) throw new ParseException(errorMessage(SENTENCE_NOT_CLOSED_PROPERLY_ERROR), parseIndex);

        if (expression.charAt(index) != ';') throw new ParseException(errorMessage(SENTENCE_NOT_CLOSED_PROPERLY_ERROR), parseIndex);

        parseIndex = index +1;
        while (parseIndex < expression.length()) {
            if (!isBlank(expression.charAt(parseIndex))) throw new ParseException(errorMessage(SENTENCE_NOT_CLOSED_PROPERLY_ERROR), parseIndex);
            parseIndex++;
        }

        return ";";
    }

    private String parseVariableName() throws ParseException {
        int index = nextNonBlank();
        if (index < 0) throw new ParseException(errorMessage(VARIABLE_NAME_EXPECTED_ERROR), parseIndex);

        Pattern variableNameParam = Pattern.compile(VARIABLE_NAME_PARAM_REGEX);
        Matcher variableMatcher = variableNameParam.matcher(expression.substring(index, expression.length()));

        if (!Pattern.matches(VARIABLE_NAME_PARAM_REGEX, String.valueOf(expression.charAt(index)))) throw new ParseException(errorMessage(VARIABLE_NAME_EXPECTED_ERROR), parseIndex);

        String variableName = null;
        if (variableMatcher.find()) {
            variableName = variableMatcher.group();
        } else {
            throw new ParseException(errorMessage(VARIABLE_NAME_EXPECTED_ERROR), parseIndex);
        }

        parseIndex = index + variableName.length();

        return variableName;
    }

    private String parseParamDelimiter() throws ParseException {
        int index = nextNonBlank();
        if (index < 0) throw new ParseException(errorMessage(PARAMETER_DELIMITER_EXPECTED_ERROR), parseIndex);

        if (expression.charAt(index) != ',') {
            throw new ParseException(errorMessage(PARAMETER_DELIMITER_EXPECTED_ERROR), parseIndex);
        }

        parseIndex = index + 1;
        return ",";
    }

    private String parseStringParameter() throws ParseException {
        int index = nextNonBlank();
        if (index < 0) throw new ParseException(STRING_PARAMETER_EXPECTED_ERROR, parseIndex);

        if (expression.charAt(index) != '"') {
            throw new ParseException(STRING_PARAMETER_EXPECTED_ERROR, parseIndex);
        }

        int shift = 1;
        Character scapeChar = Character.valueOf('\\');
        Character last = null;
        boolean strReaded = false;
        StringBuilder param = new StringBuilder();
        for (int i = index+1; i < expression.length(); i++) {
            if (expression.charAt(i) == '\\') {
                if (scapeChar.equals(last)) {
                    shift += 2;
                    param.append('\\');
                    last = null;
                } else {
                    last = expression.charAt(i);
                }
            } else if (expression.charAt(i) == '"') {
                if (scapeChar.equals(last)) {
                    shift += 2;
                    param.append('"');
                    last = null;
                } else {
                    shift++;
                    strReaded = true;
                    break;
                }
            } else if (expression.charAt(i) == 'n') {
                if (scapeChar.equals(last)) {
                    shift += 2;
                    param.append('\n');
                } else {
                    shift += 1;
                    param.append(expression.charAt(i));
                }
                last = null;
            } else {
                if (last != null) {
                    shift++;
                    param.append(last);
                }
                last = null;
                shift++;
                param.append(expression.charAt(i));
            }

        }

        if (!strReaded) throw new ParseException(STRING_PARAMETER_EXPECTED_ERROR, parseIndex);

        parseIndex = index + shift;
        return param.toString();
    }

    private int nextNonBlank() {
        if (parseIndex < 0) return -1;

        for (int i = parseIndex; i < expression.length(); i++) {
            if (!isBlank(expression.charAt(i))) {
                return i;
            }
        }

        return -1;
    }

    private int nextBlank() {
        if (parseIndex < 0) return -1;

        for (int i = parseIndex; i < expression.length(); i++) {
            if (isBlank(expression.charAt(i))) {
                return i;
            }
        }

        return -1;
    }

    private boolean isBlank(Character character) {
        return character != null && (character.equals('\n') || character.equals(' '));
    }

    private String errorMessage(String message, Object ... params) {
        return MessageFormat.format(message, params);
    }

    private String functionNames() {
        return functionNames;
    }
}
TOP

Related Classes of org.jbpm.designer.expressioneditor.parser.ExpressionParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.