This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new fa12252 CAMEL-15704: camel-csimple - Compiled simple language.
fa12252 is described below
commit fa12252f98108f5ead734245256daade1fea3feb
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 30 11:06:18 2020 +0100
CAMEL-15704: camel-csimple - Compiled simple language.
---
.../language/csimple/joor/OriginalSimpleTest.java | 72 +++++---
.../camel/language/csimple/CSimpleHelper.java | 111 +++++++++----
.../simple/ast/SimpleFunctionExpression.java | 181 ++++++++++++++++-----
3 files changed, 267 insertions(+), 97 deletions(-)
diff --git
a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
index 8b29d79..4d586df 100644
---
a/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
+++
b/components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java
@@ -406,23 +406,56 @@ public class OriginalSimpleTest extends
LanguageTestSupport {
@Test
public void testOGNLBodyListAndMapAndMethod() throws Exception {
- Map<String, Object> map = new HashMap<>();
+ Map<String, OrderLine> map = new HashMap<>();
map.put("camel", new OrderLine(123, "Camel in Action"));
map.put("amq", new OrderLine(456, "ActiveMQ in Action"));
- List<Map<String, Object>> lines = new ArrayList<>();
+ List<Map<String, OrderLine>> lines = new ArrayList<>();
+ lines.add(map);
+
+ exchange.getIn().setBody(lines);
+
+ assertExpression("${bodyAsIndex(OrderLine, '[0][camel]').id}", 123);
+ assertExpression("${bodyAsIndex(OrderLine, '[0][camel]').name}",
"Camel in Action");
+ assertExpression("${bodyAsIndex(OrderLine, '[0][camel]').getId}", 123);
+ assertExpression("${bodyAsIndex(OrderLine, '[0][camel]').getName}",
"Camel in Action");
+
+ assertExpression("${bodyAs(OrderLine)[0][camel].id}", 123);
+ assertExpression("${bodyAs(OrderLine)[0][camel].name}", "Camel in
Action");
+ assertExpression("${bodyAs(OrderLine)[0][camel].getId}", 123);
+ assertExpression("${bodyAs(OrderLine)[0][camel].getName}", "Camel in
Action");
+ }
+
+ @Test
+ public void testOGNLMandatoryBodyListAndMapAndMethod() throws Exception {
+ Map<String, OrderLine> map = new HashMap<>();
+ map.put("camel", new OrderLine(123, "Camel in Action"));
+ map.put("amq", new OrderLine(456, "ActiveMQ in Action"));
+ map.put("noname", new OrderLine(789, null));
+
+ List<Map<String, OrderLine>> lines = new ArrayList<>();
lines.add(map);
exchange.getIn().setBody(lines);
- assertExpression("${in.body[0][camel].id}", 123);
- assertExpression("${in.body[0][camel].name}", "Camel in Action");
- assertExpression("${in.body[0][camel].getId}", 123);
- assertExpression("${in.body[0][camel].getName}", "Camel in Action");
- assertExpression("${body[0][camel].id}", 123);
- assertExpression("${body[0][camel].name}", "Camel in Action");
- assertExpression("${body[0][camel].getId}", 123);
- assertExpression("${body[0][camel].getName}", "Camel in Action");
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][camel]').id}", 123);
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][camel]').name}", "Camel in Action");
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][camel]').getId}", 123);
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][camel]').getName}", "Camel in Action");
+
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][noname]').getId}", 789);
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][noname]').getName}", null);
+ try {
+ assertExpression("${mandatoryBodyAsIndex(OrderLine,
'[0][doesnotexists]').getName}", null);
+ fail("Should throw exception");
+ } catch (Exception e) {
+ assertIsInstanceOf(InvalidPayloadException.class, e.getCause());
+ }
+
+ assertExpression("${mandatoryBodyAs(OrderLine)[0][camel].id}", 123);
+ assertExpression("${mandatoryBodyAs(OrderLine)[0][camel].name}",
"Camel in Action");
+ assertExpression("${mandatoryBodyAs(OrderLine)[0][camel].getId}", 123);
+ assertExpression("${mandatoryBodyAs(OrderLine)[0][camel].getName}",
"Camel in Action");
}
@Test
@@ -517,14 +550,7 @@ public class OriginalSimpleTest extends
LanguageTestSupport {
@Test
public void testOGNLPropertyMapNotMap() throws Exception {
- try {
- assertExpression("${exchangeProperty.foobar[bar]}", null);
- fail("Should have thrown an exception");
- } catch (RuntimeBeanExpressionException e) {
- IndexOutOfBoundsException cause =
assertIsInstanceOf(IndexOutOfBoundsException.class, e.getCause());
- assertEquals("Key: bar not found in bean: cba of type:
java.lang.String using OGNL path [[bar]]",
- cause.getMessage());
- }
+ assertExpression("${exchangeProperty.foobar[bar]}", null);
}
@Test
@@ -565,12 +591,12 @@ public class OriginalSimpleTest extends
LanguageTestSupport {
assertPredicate("${header.beer} == \" \"", true);
assertPredicate("${header.beer} == \" \"", true);
- assertPredicate("${header.beer.toString().trim()} == \"\"", true);
- assertPredicate("${header.beer.toString().trim()} == \"\"", true);
+ assertPredicate("${headerAs(beer, String).toString().trim()} == \"\"",
true);
+ assertPredicate("${headerAs(beer, String).toString().trim()} == \"\"",
true);
exchange.getIn().setHeader("beer", " ");
- assertPredicate("${header.beer.trim()} == \"\"", true);
- assertPredicate("${header.beer.trim()} == \"\"", true);
+ assertPredicate("${headerAs(beer, String).trim()} == \"\"", true);
+ assertPredicate("${headerAs(beer, String).trim()} == \"\"", true);
}
@Test
@@ -1073,7 +1099,7 @@ public class OriginalSimpleTest extends
LanguageTestSupport {
exchange.setProperty(Exchange.EXCEPTION_CAUGHT,
new CamelAuthorizationException("The camel authorization
exception", exchange));
- assertExpression("${exception.getPolicyId}", "myPolicy");
+
assertExpression("${exceptionAs(org.apache.camel.CamelAuthorizationException).getPolicyId}",
"myPolicy");
}
@Test
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
index 654eab8..427e390 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java
@@ -49,6 +49,7 @@ import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.InetAddressUtil;
import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.OgnlHelper;
import org.apache.camel.util.SkipIterator;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.TimeUtils;
@@ -82,37 +83,32 @@ public final class CSimpleHelper {
}
public static <T> T bodyAsIndex(Message message, Class<T> type, String
key) {
- // TODO: array
- // TODO: key can have multiple elements [0][foo] or [0][0]
- Object body = message.getBody();
- if (body instanceof Map) {
- Map map = (Map) body;
- body = map.get(key);
- } else if (body instanceof List) {
- List list = (List) body;
- Integer num;
- if (key.startsWith("last")) {
- num = list.size() - 1;
-
- // maybe its an expression to subtract a number after last
- String after = StringHelper.after(key, "-");
- if (after != null) {
- Integer redux
- =
message.getExchange().getContext().getTypeConverter().tryConvertTo(Integer.class,
after.trim());
- if (redux != null) {
- num -= redux;
- } else {
- throw new ExpressionIllegalSyntaxException(key);
- }
- }
- } else {
- num =
message.getExchange().getContext().getTypeConverter().tryConvertTo(Integer.class,
key);
- }
- if (num != null && num >= 0 && !list.isEmpty() && list.size() >
num - 1) {
- body = list.get(num);
+ Object obj = message.getBody();
+ // the key may contain multiple keys ([0][foo]) so we need to walk
these keys
+ List<String> keys = OgnlHelper.splitOgnl(key);
+ for (String k : keys) {
+ if (k.startsWith("[") && k.endsWith("]")) {
+ k = StringHelper.between(k, "[", "]");
}
+ obj = doObjectAsIndex(message.getExchange().getContext(), obj, k);
+ }
+ return type.cast(obj);
+ }
+
+ public static <T> T mandatoryBodyAsIndex(Message message, Class<T> type,
int key) throws InvalidPayloadException {
+ T out = bodyAsIndex(message, type, "" + key);
+ if (out == null) {
+ throw new InvalidPayloadException(message.getExchange(), type,
message);
}
- return type.cast(body);
+ return out;
+ }
+
+ public static <T> T mandatoryBodyAsIndex(Message message, Class<T> type,
String key) throws InvalidPayloadException {
+ T out = bodyAsIndex(message, type, key);
+ if (out == null) {
+ throw new InvalidPayloadException(message.getExchange(), type,
message);
+ }
+ return out;
}
public static Object header(Message message, String name) {
@@ -123,6 +119,19 @@ public final class CSimpleHelper {
return message.getHeader(name, type);
}
+ public static <T> T headerAsIndex(Message message, Class<T> type, String
name, String key) {
+ Object obj = message.getHeader(name);
+ // the key may contain multiple keys ([0][foo]) so we need to walk
these keys
+ List<String> keys = OgnlHelper.splitOgnl(key);
+ for (String k : keys) {
+ if (k.startsWith("[") && k.endsWith("]")) {
+ k = StringHelper.between(k, "[", "]");
+ }
+ obj = doObjectAsIndex(message.getExchange().getContext(), obj, k);
+ }
+ return type.cast(obj);
+ }
+
public static Object exchangeProperty(Exchange exchange, String name) {
return exchange.getProperty(name);
}
@@ -131,6 +140,19 @@ public final class CSimpleHelper {
return exchange.getProperty(name, type);
}
+ public static <T> T exchangePropertyAsIndex(Exchange exchange, Class<T>
type, String name, String key) {
+ Object obj = exchange.getProperty(name);
+ // the key may contain multiple keys ([0][foo]) so we need to walk
these keys
+ List<String> keys = OgnlHelper.splitOgnl(key);
+ for (String k : keys) {
+ if (k.startsWith("[") && k.endsWith("]")) {
+ k = StringHelper.between(k, "[", "]");
+ }
+ obj = doObjectAsIndex(exchange.getContext(), obj, k);
+ }
+ return type.cast(obj);
+ }
+
public static String bodyOneLine(Exchange exchange) {
String body = exchange.getIn().getBody(String.class);
if (body == null) {
@@ -632,4 +654,35 @@ public final class CSimpleHelper {
return type.isInstance(leftValue);
}
+ private static Object doObjectAsIndex(CamelContext context, Object obj,
String key) {
+ if (obj instanceof Map) {
+ Map map = (Map) obj;
+ obj = map.get(key);
+ } else if (obj instanceof List) {
+ List list = (List) obj;
+ Integer num;
+ if (key.startsWith("last")) {
+ num = list.size() - 1;
+
+ // maybe its an expression to subtract a number after last
+ String after = StringHelper.after(key, "-");
+ if (after != null) {
+ Integer redux
+ =
context.getTypeConverter().tryConvertTo(Integer.class, after.trim());
+ if (redux != null) {
+ num -= redux;
+ } else {
+ throw new ExpressionIllegalSyntaxException(key);
+ }
+ }
+ } else {
+ num = context.getTypeConverter().tryConvertTo(Integer.class,
key);
+ }
+ if (num != null && num >= 0 && !list.isEmpty() && list.size() >
num - 1) {
+ obj = list.get(num);
+ }
+ }
+ return obj;
+ }
+
}
diff --git
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index ba986e5..becf312 100644
---
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -17,6 +17,7 @@
package org.apache.camel.language.simple.ast;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -643,22 +644,35 @@ public class SimpleFunctionExpression extends
LiteralExpression {
if (remainder.startsWith("[") && remainder.endsWith("]")) {
remainder = remainder.substring(1, remainder.length() - 1);
}
+ // remove quotes from key
+ String key = StringHelper.removeLeadingAndEndingQuotes(remainder);
+ key = key.trim();
// validate syntax
- boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key);
if (invalid) {
- // must use exchangePropertyAs as we need to be typed
throw new SimpleParserException(
- "Valid syntax: ${exchangePropertyAs(key, type).OGNL}
was: " + function, token.getIndex());
+ "Valid syntax: ${exchangeProperty.name[key]} was: " +
function, token.getIndex());
}
- if (OgnlHelper.isValidOgnlExpression(remainder)) {
- // must use exchangePropertyAs as we need to be typed
+ // it is an index?
+ String index = null;
+ if (key.endsWith("]")) {
+ index = StringHelper.between(key, "[", "]");
+ if (index != null) {
+ key = StringHelper.before(key, "[");
+ }
+ }
+ if (index != null) {
+ index = StringHelper.removeLeadingAndEndingQuotes(index);
+ return "exchangePropertyAsIndex(exchange, Object.class, \"" +
key + "\", \"" + index + "\")";
+ } else if (OgnlHelper.isValidOgnlExpression(remainder)) {
+ // ognl based exchange property must be typed
throw new SimpleParserException(
- "Valid syntax: ${exchangePropertyAs(key, type).OGNL}
was: " + function, token.getIndex());
+ "Valid syntax: ${exchangePropertyAs(key, type)} was: "
+ function, token.getIndex());
} else {
// regular property
- return "exchangeProperty(exchange, \"" + remainder + "\")";
+ return "exchangeProperty(exchange, \"" + key + "\")";
}
}
@@ -849,7 +863,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
- private String createCodeBodyOrHeader(String function) {
+ private String createCodeBodyOrHeader(final String function) {
// bodyAsIndex
String remainder = ifStartsWithReturnRemainder("bodyAsIndex(",
function);
if (remainder != null) {
@@ -905,25 +919,61 @@ public class SimpleFunctionExpression extends
LiteralExpression {
if (invalid) {
throw new SimpleParserException("Valid syntax:
${bodyAs(type).OGNL} was: " + function, token.getIndex());
}
- // it is an index?
- String index = null;
- String after = null;
if (remainder.startsWith("[")) {
- // the OGNL starts with an index so lets use bodyAsIndex
- index = StringHelper.between(remainder, "[", "]");
- after = StringHelper.after(remainder, "]");
- }
- if (index != null) {
- index = StringHelper.removeLeadingAndEndingQuotes(index);
- return "bodyAsIndex(message, " + type + ", \"" + index +
"\")" + ognlCodeMethods(after, type);
- } else {
- return "bodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
+ // is there any index, then we should use bodyAsIndex
function instead
+ // (use splitOgnl which assembles multiple indexes into a
single part)
+ List<String> parts = splitOgnl(remainder);
+ if (!parts.isEmpty()) {
+ String func = "bodyAsIndex(" + type + ", \"" +
parts.remove(0) + "\")";
+ String last = String.join("", parts);
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ return createCodeBodyOrHeader(func);
+ }
}
+ return "bodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
} else {
return "bodyAs(message, " + type + ")";
}
}
+ // mandatoryBodyAsIndex
+ remainder = ifStartsWithReturnRemainder("mandatoryBodyAsIndex(",
function);
+ if (remainder != null) {
+ String typeAndIndex = StringHelper.before(remainder, ")");
+ if (typeAndIndex == null) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, token.getIndex());
+ }
+
+ String type = StringHelper.before(typeAndIndex, ",");
+ String index = StringHelper.after(typeAndIndex, ",");
+ remainder = StringHelper.after(remainder, ")");
+ if (ObjectHelper.isEmpty(type) || ObjectHelper.isEmpty(index)) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, token.getIndex());
+ }
+ type = StringHelper.removeQuotes(type);
+ type = type.trim();
+ if (!type.endsWith(".class")) {
+ type = type + ".class";
+ }
+ type = type.replace('$', '.');
+ index = StringHelper.removeQuotes(index);
+ index = index.trim();
+ if (ObjectHelper.isNotEmpty(remainder)) {
+ boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
+ if (invalid) {
+ throw new SimpleParserException(
+ "Valid syntax: ${mandatoryBodyAsIndex(type,
index).OGNL} was: " + function, token.getIndex());
+ }
+ return "mandatoryBodyAsIndex(message, " + type + ", \"" +
index + "\")" + ognlCodeMethods(remainder, type);
+ } else {
+ return "mandatoryBodyAsIndex(message, " + type + ", \"" +
index + "\")";
+ }
+ }
+
// mandatoryBodyAs
remainder = ifStartsWithReturnRemainder("mandatoryBodyAs(", function);
if (remainder != null) {
@@ -939,12 +989,24 @@ public class SimpleFunctionExpression extends
LiteralExpression {
type = type.trim();
remainder = StringHelper.after(remainder, ")");
if (ObjectHelper.isNotEmpty(remainder)) {
- // TODO: mandatoryBodyAsIndex
boolean invalid =
OgnlHelper.isInvalidValidOgnlExpression(remainder);
if (invalid) {
throw new SimpleParserException(
"Valid syntax: ${mandatoryBodyAs(type).OGNL} was:
" + function, token.getIndex());
}
+ if (remainder.startsWith("[")) {
+ // is there any index, then we should use
mandatoryBodyAsIndex function instead
+ // (use splitOgnl which assembles multiple indexes into a
single part)
+ List<String> parts = splitOgnl(remainder);
+ if (!parts.isEmpty()) {
+ String func = "mandatoryBodyAsIndex(" + type + ", \""
+ parts.remove(0) + "\")";
+ String last = String.join("", parts);
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ return createCodeBodyOrHeader(func);
+ }
+ }
return "mandatoryBodyAs(message, " + type + ")" +
ognlCodeMethods(remainder, type);
} else {
return "mandatoryBodyAs(message, " + type + ")";
@@ -963,12 +1025,20 @@ public class SimpleFunctionExpression extends
LiteralExpression {
if (invalid) {
throw new SimpleParserException("Valid syntax: ${body.OGNL}
was: " + function, token.getIndex());
}
- String answer = ognlCodeMethods(remainder, null);
- if (answer.startsWith("body")) {
- return answer;
- } else {
- return "body" + answer;
+ if (remainder.startsWith("[")) {
+ // is there any index, then we should use bodyAsIndex function
instead
+ // (use splitOgnl which assembles multiple indexes into a
single part)
+ List<String> parts = splitOgnl(remainder);
+ if (!parts.isEmpty()) {
+ String func = "bodyAsIndex(Object.class, \"" +
parts.remove(0) + "\")";
+ String last = String.join("", parts);
+ if (!last.isEmpty()) {
+ func += "." + last;
+ }
+ return createCodeBodyOrHeader(func);
+ }
}
+ return "body" + ognlCodeMethods(remainder, null);
}
// headerAs
@@ -1030,8 +1100,17 @@ public class SimpleFunctionExpression extends
LiteralExpression {
if (invalid) {
throw new SimpleParserException("Valid syntax:
${header.name[key]} was: " + function, token.getIndex());
}
-
- if (OgnlHelper.isValidOgnlExpression(key)) {
+ // it is an index?
+ String index = null;
+ if (key.endsWith("]")) {
+ index = StringHelper.between(key, "[", "]");
+ if (index != null) {
+ key = StringHelper.before(key, "[");
+ }
+ }
+ if (index != null) {
+ return "headerAsIndex(message, Object.class, \"" + key + "\",
\"" + index + "\")";
+ } else if (OgnlHelper.isValidOgnlExpression(key)) {
// ognl based header must be typed
throw new SimpleParserException("Valid syntax: ${headerAs(key,
type)} was: " + function, token.getIndex());
} else {
@@ -1139,11 +1218,37 @@ public class SimpleFunctionExpression extends
LiteralExpression {
return null;
}
+ private static List<String> splitOgnl(String remainder) {
+ List<String> methods = OgnlHelper.splitOgnl(remainder);
+ // if its a double index [foo][0] then we want them combined into a
single element
+ List<String> answer = new ArrayList<>();
+ for (String m : methods) {
+ if (m.startsWith(".")) {
+ m = m.substring(1);
+ }
+ boolean index = m.startsWith("[") && m.endsWith("]");
+ if (index) {
+ String last = answer.isEmpty() ? null :
answer.get(answer.size() - 1);
+ boolean lastIndex = last != null && last.startsWith("[") &&
last.endsWith("]");
+ if (lastIndex) {
+ String line = last + m;
+ answer.set(answer.size() - 1, line);
+ } else {
+ answer.add(m);
+ }
+ } else {
+ answer.add(m);
+ }
+ }
+
+ return answer;
+ }
+
private static String ognlCodeMethods(String remainder, String type) {
StringBuilder sb = new StringBuilder();
if (remainder != null) {
- List<String> methods = OgnlHelper.splitOgnl(remainder);
+ List<String> methods = splitOgnl(remainder);
for (int i = 0; i < methods.size(); i++) {
String m = methods.get(i);
if (m.startsWith("(")) {
@@ -1151,12 +1256,9 @@ public class SimpleFunctionExpression extends
LiteralExpression {
sb.append(m);
continue;
}
- if (m.startsWith(".")) {
- m = m.substring(1);
- }
// clip index
- String index = StringHelper.between(m, "[", "]");
+ String index = StringHelper.betweenOuterPair(m, '[', ']');
if (index != null) {
m = StringHelper.before(m, "[");
}
@@ -1182,18 +1284,7 @@ public class SimpleFunctionExpression extends
LiteralExpression {
// append index via a get method - eg get for a list, or get
for a map (array not supported)
if (index != null) {
- // if there was no method then its direct on the body, so
use bodyAsIndex instead of get
- if (m == null || m.isEmpty()) {
- sb.append("bodyAsIndex(message, ");
- if (type != null) {
- sb.append(type);
- } else {
- sb.append("Object.class");
- }
- sb.append(", ");
- } else {
- sb.append(".get(");
- }
+ sb.append(".get(");
try {
long lon = Long.parseLong(index);
sb.append(lon);