diff --git a/modules/core/src/main/java/org/apache/synapse/SynapseConstants.java b/modules/core/src/main/java/org/apache/synapse/SynapseConstants.java index 01a99133e4..02e5a89b51 100644 --- a/modules/core/src/main/java/org/apache/synapse/SynapseConstants.java +++ b/modules/core/src/main/java/org/apache/synapse/SynapseConstants.java @@ -640,7 +640,7 @@ public enum ENDPOINT_TIMEOUT_TYPE { ENDPOINT_TIMEOUT, GLOBAL_TIMEOUT, HTTP_CONNE public static final String TRIM = "trim"; public static final String REPLACE = "replace"; public static final String SPLIT = "split"; - public static final String NOW = "now"; + public static final String INDEX_OF = "indexOf"; public static final String ABS = "abs"; public static final String CEIL = "ceil"; public static final String FLOOR = "floor"; @@ -661,6 +661,9 @@ public enum ENDPOINT_TIMEOUT_TYPE { ENDPOINT_TIMEOUT, GLOBAL_TIMEOUT, HTTP_CONNE public static final String EXISTS = "exists"; public static final String XPATH = "xpath"; public static final String SECRET = "secret"; + public static final String NOW = "now"; + public static final String FORMAT_DATE_TIME = "formatDateTime"; + public static final String CHAR_AT = "charAt"; public static final String ROUND = "round"; public static final String INTEGER = "integer"; diff --git a/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/ExpressionResult.java b/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/ExpressionResult.java index 8dc0a60b55..7cfe21e323 100644 --- a/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/ExpressionResult.java +++ b/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/ExpressionResult.java @@ -118,6 +118,16 @@ public double asDouble() { throw new EvaluationException("Value : " + value + " cannot be converted to double"); } + // Method to get value as Long + public long asLong() { + if (value instanceof Number) { + return ((Number) value).longValue(); + } else if (value instanceof JsonPrimitive && ((JsonPrimitive) value).isNumber()) { + return ((JsonPrimitive) value).getAsLong(); + } + throw new EvaluationException("Value : " + value + " cannot be converted to double"); + } + // Method to get value as boolean public boolean asBoolean() { if (value instanceof Boolean) { diff --git a/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/PredefinedFunctionNode.java b/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/PredefinedFunctionNode.java index bb89d3236b..767af34eb2 100644 --- a/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/PredefinedFunctionNode.java +++ b/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/ast/PredefinedFunctionNode.java @@ -26,8 +26,16 @@ import org.jaxen.JaxenException; import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.UnsupportedCharsetException; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Base64; import java.util.List; @@ -160,6 +168,12 @@ private ExpressionResult handleDoubleArgumentFunctions(EvaluationContext context return handleUrlEncodeFunction(source, argument1); case SynapseConstants.REGISTRY: return handleRegistryAccess(context, source, argument1); + case SynapseConstants.INDEX_OF: + return handleIndexOfFunction(source, argument1); + case SynapseConstants.FORMAT_DATE_TIME: + return handleFormatCurrentDateTimeFunction(source, argument1); + case SynapseConstants.CHAR_AT: + return handleCharAtFunction(source, argument1); default: throw new EvaluationException("Invalid function: " + functionName + " with two arguments"); } @@ -177,6 +191,10 @@ private ExpressionResult handleTripleArgumentFunctions(EvaluationContext context return handleSubstringFunction(source, argument1, argument2); case SynapseConstants.REPLACE: return handleReplaceFunction(source, argument1, argument2); + case SynapseConstants.INDEX_OF: + return handleIndexOfFunction(source, argument1, argument2); + case SynapseConstants.FORMAT_DATE_TIME: + return handleFormatDateTimeFunctions(source, argument1, argument2); default: throw new EvaluationException("Invalid function: " + functionName + " with three arguments"); } @@ -295,7 +313,7 @@ private ExpressionResult handleUrlEncodeFunction(ExpressionResult result) { private ExpressionResult handleUrlDecodeFunction(ExpressionResult result) { if (result.isString()) { try { - return new ExpressionResult(java.net.URLDecoder.decode(result.asString(), "UTF-8")); + return new ExpressionResult(URLDecoder.decode(result.asString(), "UTF-8")); } catch (UnsupportedEncodingException e) { throw new EvaluationException("unsupported encoding provided for urlDecode function"); } @@ -385,6 +403,88 @@ private ExpressionResult handleSplitFunction(ExpressionResult source, Expression + ", argument1: " + argument1.asString()); } + private ExpressionResult handleIndexOfFunction(ExpressionResult source, ExpressionResult argument1) { + if (source.isString() && argument1.isString()) { + return new ExpressionResult(source.asString().indexOf(argument1.asString())); + } + throw new EvaluationException("Invalid argument provided for indexOf function. source: " + source.asString() + + ", argument1: " + argument1.asString()); + } + + private ExpressionResult handleIndexOfFunction(ExpressionResult source, ExpressionResult argument1, ExpressionResult argument2) { + if (source.isString() && argument1.isString() && argument2.isInteger()) { + return new ExpressionResult(source.asString().indexOf(argument1.asString(), argument2.asInt())); + } + throw new EvaluationException("Invalid argument provided for indexOf function. source: " + source.asString() + + ", argument1: " + argument1.asString() + ", argument2: " + argument2.asString()); + } + + private ExpressionResult handleFormatCurrentDateTimeFunction(ExpressionResult source, ExpressionResult argument1) { + if (argument1.isString() && source.isNumeric()) { + try { + DateTimeFormatter formatObj = DateTimeFormatter.ofPattern(argument1.asString()); + LocalDateTime dateObj = LocalDateTime.ofInstant(Instant.ofEpochMilli(source.asLong()), + ZoneId.systemDefault()); + return new ExpressionResult(dateObj.format(formatObj)); + } catch (DateTimeException e) { + throw new EvaluationException("Invalid date format provided for formatDateTime function. Format: " + + argument1.asString()); + } + } + throw new EvaluationException("Invalid argument provided for formatDateTime function. source: " + source.asString() + + ", argument1: " + argument1.asString()); + } + + private ExpressionResult handleFormatDateTimeFunctions(ExpressionResult source, ExpressionResult oldFormat, + ExpressionResult newFormat) { + if (source.isString() && oldFormat.isString() && newFormat.isString()) { + DateTimeFormatter oldFormatObj = null; + try { + oldFormatObj = DateTimeFormatter.ofPattern(oldFormat.asString()); + LocalDateTime dateObj = LocalDateTime.parse(source.asString(), oldFormatObj); + DateTimeFormatter newFormatObj = DateTimeFormatter.ofPattern(newFormat.asString()); + return new ExpressionResult(dateObj.format(newFormatObj)); + } catch (DateTimeException | IllegalArgumentException e) { + // try with date only + if (oldFormatObj != null) { + try { + LocalDate dateObj = LocalDate.parse(source.asString(), oldFormatObj); + DateTimeFormatter newFormatObj = DateTimeFormatter.ofPattern(newFormat.asString()); + return new ExpressionResult(dateObj.format(newFormatObj)); + } catch (DateTimeException | IllegalArgumentException ex) { + // try with time only + try { + LocalTime dateObj = LocalTime.parse(source.asString(), oldFormatObj); + DateTimeFormatter newFormatObj = DateTimeFormatter.ofPattern(newFormat.asString()); + return new ExpressionResult(dateObj.format(newFormatObj)); + } catch (DateTimeException | IllegalArgumentException exc) { + throw new EvaluationException("Invalid date format provided for formatDateTime function. Format: " + + oldFormat.asString()); + } + } + } + throw new EvaluationException("Invalid date format provided for formatDateTime function. Format: " + + oldFormat.asString()); + } + } + throw new EvaluationException("Invalid argument provided for formatDateTime function. source: " + source.asString() + + ", oldFormat: " + oldFormat.asString() + ", newFormat: " + newFormat.asString()); + } + + private ExpressionResult handleCharAtFunction(ExpressionResult source, ExpressionResult argument1) { + if (source.isString() && argument1.isInteger()) { + try { + + return new ExpressionResult(String.valueOf(source.asString().charAt(argument1.asInt()))); + } catch (StringIndexOutOfBoundsException ex) { + throw new EvaluationException("Invalid index provided for charAt function. source: " + source.asString() + + ", index: " + argument1.asInt()); + } + } + throw new EvaluationException("Invalid argument provided for charAt function. source: " + source.asString() + + ", argument1: " + argument1.asString()); + } + private ExpressionResult handlePowFunction(ExpressionResult source, ExpressionResult argument1) { if ((source.isDouble() || source.isInteger()) && (argument1.isDouble() || argument1.isInteger())) { return new ExpressionResult(Math.pow(source.asDouble(), argument1.asDouble())); diff --git a/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/visitor/ExpressionVisitor.java b/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/visitor/ExpressionVisitor.java index 8bdeec5a47..b79c523002 100644 --- a/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/visitor/ExpressionVisitor.java +++ b/modules/core/src/main/java/org/apache/synapse/util/synapse/expression/visitor/ExpressionVisitor.java @@ -36,7 +36,6 @@ public class ExpressionVisitor extends ExpressionParserBaseVisitor 6) ? true : false")); Assert.assertEquals(SynapseConstants.UNKNOWN, TestUtils.evaluateExpression("not(123)")); } + + @Test + public void testIndexOf() { + Assert.assertEquals("6", TestUtils.evaluateExpression("indexOf(\"Hello World\", \"World\")")); + Assert.assertEquals("-1", TestUtils.evaluateExpression("indexOf(\"Hello World\", \"World2\")")); + Assert.assertEquals("8", TestUtils.evaluateExpression("indexOf(\"Hello World\", \"r\")")); + Assert.assertEquals("9", TestUtils.evaluateExpression("indexOf(\"Hello World\", \"l\",5)")); + Assert.assertEquals("-1", TestUtils.evaluateExpression("indexOf(\"Hello World\", \"l\",50)")); + Assert.assertEquals("7", TestUtils.evaluateExpressionWithPayload("indexOf($.string, \"World\")", 1)); + } + + @Test + public void testCharAt() { + Assert.assertEquals("W", TestUtils.evaluateExpression("charAt(\"Hello World\", 6)")); + Assert.assertEquals(SynapseConstants.UNKNOWN, TestUtils.evaluateExpression("charAt(\"Hello World\", -1)")); + Assert.assertEquals(SynapseConstants.UNKNOWN, TestUtils.evaluateExpression("charAt(\"Hello World\", 100)")); + Assert.assertEquals(" ", TestUtils.evaluateExpressionWithPayload("charAt($.string, 0)", 1)); + } + + @Test + public void testFormatDateTime() { + Assert.assertEquals(LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm")), + TestUtils.evaluateExpression("formatDateTime(now(), \"dd-MM-yyyy HH:mm\")")); + Assert.assertEquals(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + TestUtils.evaluateExpression("formatDateTime(now(), \"yyyy-MM-dd\")")); + Assert.assertEquals("1988-09-29", + TestUtils.evaluateExpression("formatDateTime(\"29/09/1988\",\"dd/MM/yyyy\", \"yyyy-MM-dd\")")); + Assert.assertEquals("1988 Sep 29", + TestUtils.evaluateExpression("formatDateTime(\"29-09-1988\",\"dd-MM-yyyy\", \"yyyy MMM dd\")")); + Assert.assertEquals("11 22 33", + TestUtils.evaluateExpression("formatDateTime(\"11-22-33\",\"HH-mm-ss\", \"HH mm ss\")")); + Assert.assertEquals(SynapseConstants.UNKNOWN, + TestUtils.evaluateExpression("formatDateTime(\"50-22-33\",\"HH-mm-ss\", \"HH mm ss\")")); + } }