All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.microcks.util.el.ExpressionParser Maven / Gradle / Ivy

There is a newer version: 1.10.1
Show newest version
/*
 * Licensed to Laurent Broudoux (the "Author") under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. Author licenses this
 * file to you 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 io.github.microcks.util.el;

import io.github.microcks.util.el.function.ELFunction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Helper object for finding and parsing expressions present into a string template.
 * For example, following template: {@code Hello {{ request.body/name }} it's {{ now() }}} should be decomposed into
 * 4 expressions: 
    *
  • A LiteralExpression representing the "Hello " part
  • *
  • A VariableReferenceExpression representing the "request.body/name part
  • *
  • A LiteralExpression representing the " it's " part
  • *
  • A ELFunctionExpression representing the "now()" part
  • *
* @author laurent */ public class ExpressionParser { /** A simple logger for diagnostic messages. */ private static Logger log = LoggerFactory.getLogger(ExpressionParser.class); /** * Navigate the template for finding expressions that can be evaluated into this template. Expressions * are returned into an ordered array. * @param template The string to browse * @param context The EvaluationContext that may contains variable or function references * @param expressionPrefix The prefix starting new expression (ex: "{{") * @param expressionSuffix The suffix closing expression (ex: "}}") * @return The array of found expressions when browsing template from left to right */ public static Expression[] parseExpressions(String template, EvaluationContext context, String expressionPrefix, String expressionSuffix) throws ParseException { // Prepare an array for results. List expressions = new ArrayList<>(); int startIdx = 0; while (startIdx < template.length()) { int prefixIndex = template.indexOf(expressionPrefix, startIdx); if (prefixIndex >= startIdx) { // an inner expression was found - this is a composite if (prefixIndex > startIdx) { log.debug("Found a literal expression starting at " + startIdx); expressions.add(new LiteralExpression(template.substring(startIdx, prefixIndex))); } int afterPrefixIndex = prefixIndex + expressionPrefix.length(); int suffixIndex = skipToCorrectEndSuffix(expressionSuffix, template, afterPrefixIndex); if (suffixIndex == -1) { log.info("No ending suffix '" + expressionSuffix + "' for expression starting at character " + prefixIndex + ": " + template.substring(prefixIndex)); throw new ParseException(template, prefixIndex, "No ending suffix '" + expressionSuffix + "' for expression starting at character " + prefixIndex + ": " + template.substring(prefixIndex)); } if (suffixIndex == afterPrefixIndex) { log.info("No expression defined within delimiter '" + expressionPrefix + expressionSuffix + "' at character " + prefixIndex); throw new ParseException(template, prefixIndex, "No expression defined within delimiter '" + expressionPrefix + expressionSuffix + "' at character " + prefixIndex); } String expr = template.substring(prefixIndex + expressionPrefix.length(), suffixIndex); expr = expr.trim(); if (expr.isEmpty()) { log.info("No expression defined within delimiter '" + expressionPrefix + expressionSuffix + "' at character " + prefixIndex); throw new ParseException(template, prefixIndex, "No expression defined within delimiter '" + expressionPrefix + expressionSuffix + "' at character " + prefixIndex); } expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex + expressionSuffix.length(); log.debug("Expression accumulated. Pursuing with index " + startIdx + " on " + template.length()); } else { // no more expression. finalize with a literal. expressions.add(new LiteralExpression(template.substring(startIdx, template.length()))); break; } } return expressions.toArray(new Expression[0]); } /** Find for next suitable correct end suffix. Could be extended in future to manager recursivity... */ private static int skipToCorrectEndSuffix(String expressionSuffix, String template, int afterPrefixIndex) { int nextSuffix = template.indexOf(expressionSuffix, afterPrefixIndex); if (nextSuffix == -1) { return -1; // the suffix is missing } return nextSuffix; } /** Depending on expression string, try to guess if it's a Literal, a Function or a VariableReference expression. */ private static Expression doParseExpression(String expressionString, EvaluationContext context) { int argsStart = expressionString.indexOf('('); int argsEnd = expressionString.indexOf(')'); int variableStart = expressionString.indexOf('.'); boolean hasVariable = variableStart != -1; boolean hasArgs = (argsStart != 1 && argsEnd != -1 && argsStart < argsEnd); boolean isPostmanFunction = expressionString.startsWith("$"); boolean varBeforeArgs = (variableStart < argsStart) && !isPostmanFunction; log.debug("hasVariable:{} hasArgs:{} isPostmanFunction:{} varBeforeArgs:{}", hasVariable, hasArgs, isPostmanFunction, varBeforeArgs); // check if it's a VariableReferenceExpression. if (hasVariable && (!hasArgs || varBeforeArgs)) { log.debug("Found a variable reference expression " + expressionString); String variableName = expressionString.substring(0, expressionString.indexOf('.')); Object variable = context.lookupVariable(variableName); String pathExpression = expressionString.substring(expressionString.indexOf('.') + 1); if (variable != null) { return new VariableReferenceExpression(variable, pathExpression); } log.warn("Variable with name " + variableName + " cannot be found into EvaluationContext. " + "Returning empty literal expression"); return new LiteralExpression(""); } // Check if it's a ELFunctionExpression if ( hasArgs || isPostmanFunction) { log.debug("Found a function expression " + expressionString); String functionName = null; String[] args = new String[0]; // Checking for easier Postman compatibility notation first. if (expressionString.startsWith("$")) { functionName = expressionString.substring(1); } else { functionName = expressionString.substring(0, argsStart); String argsString = expressionString.substring(argsStart + 1, argsEnd); // Parse arguments if non empty string. if (argsString.length() > 0) { args = Arrays.stream(argsString.split(",")) .map(arg -> arg.trim()).toArray(String[]::new); } } Class functionClazz = context.lookupFunction(functionName); ELFunction function = null; try { function = functionClazz.newInstance(); } catch (Exception e) { log.error("Exception while instantiating the functionClazz " + functionClazz, e); return new LiteralExpression(""); } return new FunctionExpression(function, args); } log.info("No ELFunction or VariableReference expressions found... Returning empty literal expression"); return new LiteralExpression(""); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy