Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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("");
}
}