Skip to content

Commit 4c0096f

Browse files
committed
camel-core - Add safeQuote function to simple language
1 parent 3373154 commit 4c0096f

10 files changed

Lines changed: 282 additions & 55 deletions

File tree

catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/simple.json

Lines changed: 27 additions & 26 deletions
Large diffs are not rendered by default.

components/camel-csimple-joor/src/test/java/org/apache/camel/language/csimple/joor/OriginalSimpleTest.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2635,6 +2635,53 @@ public void testQuote() {
26352635
assertEquals("\"Hi\"", s);
26362636
}
26372637

2638+
@Test
2639+
public void testSafeQuote() {
2640+
exchange.getMessage().setBody("Hello World");
2641+
2642+
Expression expression = context.resolveLanguage("csimple").createExpression("${safeQuote()}");
2643+
String s = expression.evaluate(exchange, String.class);
2644+
assertEquals("\"Hello World\"", s);
2645+
2646+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote(${body})}");
2647+
s = expression.evaluate(exchange, String.class);
2648+
assertEquals("\"Hello World\"", s);
2649+
2650+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote('Hi')}");
2651+
s = expression.evaluate(exchange, String.class);
2652+
assertEquals("\"Hi\"", s);
2653+
2654+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote(''Hi'')}");
2655+
s = expression.evaluate(exchange, String.class);
2656+
assertEquals("\"Hi\"", s);
2657+
2658+
exchange.getMessage().setBody(123);
2659+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote()}");
2660+
s = expression.evaluate(exchange, String.class);
2661+
assertEquals("123", s);
2662+
2663+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote(${body})}");
2664+
s = expression.evaluate(exchange, String.class);
2665+
assertEquals("123", s);
2666+
2667+
exchange.getMessage().setBody(true);
2668+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote()}");
2669+
s = expression.evaluate(exchange, String.class);
2670+
assertEquals("true", s);
2671+
2672+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote(${body})}");
2673+
s = expression.evaluate(exchange, String.class);
2674+
assertEquals("true", s);
2675+
2676+
Map<String, String> m = new LinkedHashMap<>();
2677+
m.put("A", "1");
2678+
m.put("B", "2");
2679+
exchange.getMessage().setBody(m);
2680+
expression = context.resolveLanguage("csimple").createExpression("${safeQuote()}");
2681+
s = expression.evaluate(exchange, String.class);
2682+
assertEquals("\"{A=1, B=2}\"", s);
2683+
}
2684+
26382685
@Test
26392686
public void testTrim() {
26402687
exchange.getMessage().setBody(" Hello World ");
@@ -2879,7 +2926,7 @@ public void testAbs() {
28792926
Integer i = expression.evaluate(exchange, Integer.class);
28802927
assertEquals(987, i);
28812928

2882-
expression = context.resolveLanguage("simple").createExpression("${abs(-5)}");
2929+
expression = context.resolveLanguage("csimple").createExpression("${abs(-5)}");
28832930
i = expression.evaluate(exchange, Integer.class);
28842931
assertEquals(5, i);
28852932

components/camel-jsonpath/src/test/java/org/apache/camel/jsonpath/JsonPathSimpleInitBlockFunctionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ public class JsonPathSimpleInitBlockFunctionTest extends CamelTestSupport {
3535
"id": "$id",
3636
"type": "$type",
3737
"amount": $price,
38-
"oldStatus": $price ~> $level() ~> ${quote()}
39-
"status": ${newStatus($price)} ~> $level() ~> ${quote()}
38+
"oldStatus": $price ~> $level() ~> ${safeQuote()}
39+
"status": ${newStatus($price)} ~> $level() ~> ${safeQuote()}
4040
}
4141
""";
4242

core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json

Lines changed: 27 additions & 26 deletions
Large diffs are not rendered by default.

core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ The generator supports different kinds `default`, `classic`, `short`, `simple` a
460460
|`pad(exp,width,separator)` | `String` | Pads the expression with extra padding if necessary, according the total width The separator is by default a space. If the width is negative then padding to the right, otherwise to the left.
461461
|`replace(from,to)` | `String` | Replace all the string values in the message body. To make it easier to replace single and double quotes, then you can use XML escaped values `\&quot;` as double quote, `\&apos;` as single quote, and `\&empty;` as empty value.
462462
|`replace(from,to,exp)` | `String` | Replace all the string values in the given expression. To make it easier to replace single and double quotes, then you can use XML escaped values `\&quot;` as double quote, `\&apos;` as single quote, and `\&empty;` as empty value.
463+
|`safeQuote() | `String` | Returns the message body safely quoted if needed
464+
|`safeQuote(exp) | `String` | Returns the expression safely quoted if needed
463465
|`substring(num1)` | `String` | Returns a substring of the message body. If the number is positive, then the returned string is clipped from the beginning. If the number is negative, then the returned string is clipped from the ending.
464466
|`substring(num1,num2)` | `String` | Returns a substring of the message body. If the number is positive, then the returned string is clipped from the beginning. If the number is negative, then the returned string is clipped from the ending.
465467
|`substring(num1,num2,exp)` | `String` | Returns a substring of the given expression. If the number is positive, then the returned string is clipped from the beginning. If the number is negative, then the returned string is clipped from the ending.
@@ -532,6 +534,22 @@ If the value is currently in single quote, then this will be converted to double
532534
If the message body contains `Hello World` then `${quote()}` returns `"Hello World"`.
533535
And using `${quote('Hi from me')}` then `"Hi from me"` is returned.
534536

537+
The `safeQuote` function is quoting the value depending on the value type
538+
(uses the same logic as `kindOfType` function). In essence values that are null, boolean or numbers are not quoted,
539+
while everything else is. The `safeQuote` function is useful when working with JSon data.
540+
541+
For example when doing JSon to JSon mapping you can extract values form the source document,
542+
to be included in the output, but the values may or may not need to be quoted. Then you can use the `~>` chain operator
543+
to call the `safeQuote` function as shown below:
544+
545+
[source,text]
546+
----
547+
{
548+
"id": ${jsonpath($.id)} ~> ${safeQuote()}
549+
"total": ${jsonpath($.amount)} ~> ${safeQuote()}
550+
}
551+
----
552+
535553

536554
=== XML & JSon Functions
537555

core/camel-core-languages/src/main/java/org/apache/camel/language/csimple/CSimpleHelper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,19 @@ public static String quote(Exchange exchange, Object value) {
10621062
return body;
10631063
}
10641064

1065+
public static Object safeQuote(Exchange exchange, Object value) {
1066+
if (value == null) {
1067+
return null;
1068+
}
1069+
String type = kindOfType(exchange, value);
1070+
if ("string".equals(type) || "array".equals(type) || "object".equals(type)) {
1071+
String body = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value);
1072+
body = StringHelper.removeLeadingAndEndingQuotes(body);
1073+
value = StringQuoteHelper.doubleQuote(body);
1074+
}
1075+
return value;
1076+
}
1077+
10651078
public static String trim(Exchange exchange, Object value) {
10661079
String body;
10671080
if (value != null) {

core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ public final class SimpleConstants {
337337
label = "function")
338338
public static final String ROUTE_ID = "routeId";
339339

340+
@Metadata(description = "Returns the message body (or expression) safely quoted if needed",
341+
label = "function", javaType = "String")
342+
public static final String SAFE_QUOTE = "safeQuote(exp)";
343+
340344
@Metadata(description = "Sets an attachment with payload from the message body/expression.",
341345
label = "function", javaType = "Object")
342346
public static final String SET_ATTACHMENT = "setVariable(key,exp)";

core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,68 @@ public String toString() {
643643
};
644644
}
645645

646+
/**
647+
* Safe quotes the given expressions (uses message body if expression is null) if necessary.
648+
*/
649+
public static Expression safeQuoteExpression(final String expression) {
650+
return new ExpressionAdapter() {
651+
private Expression exp;
652+
653+
@Override
654+
public void init(CamelContext context) {
655+
if (expression != null) {
656+
exp = context.resolveLanguage("simple").createExpression(expression);
657+
exp.init(context);
658+
}
659+
}
660+
661+
@Override
662+
public Object evaluate(Exchange exchange) {
663+
Object value;
664+
if (exp != null) {
665+
value = exp.evaluate(exchange, Object.class);
666+
} else {
667+
value = exchange.getMessage().getBody(Object.class);
668+
}
669+
if (value != null) {
670+
String type = kindOfType(value);
671+
if ("string".equals(type) || "array".equals(type) || "object".equals(type)) {
672+
String body = exchange.getContext().getTypeConverter().tryConvertTo(String.class, exchange, value);
673+
body = StringHelper.removeLeadingAndEndingQuotes(body);
674+
value = StringQuoteHelper.doubleQuote(body);
675+
}
676+
}
677+
return value;
678+
}
679+
680+
private String kindOfType(Object value) {
681+
Class<?> type = value.getClass();
682+
if (ObjectHelper.isNumericType(type)) {
683+
return "number";
684+
} else if (boolean.class == type || Boolean.class == type) {
685+
return "boolean";
686+
} else if (value instanceof CharSequence) {
687+
return "string";
688+
} else if (ObjectHelper.isPrimitiveArrayType(type) || value instanceof Collection
689+
|| value instanceof Map<?, ?>) {
690+
return "array";
691+
} else {
692+
return "object";
693+
}
694+
}
695+
696+
697+
@Override
698+
public String toString() {
699+
if (expression != null) {
700+
return "safeQuote(" + expression + ")";
701+
} else {
702+
return "safeQuote()";
703+
}
704+
}
705+
};
706+
}
707+
646708
/**
647709
* Double quotes the given expressions (uses message body if expression is null)
648710
*/

core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,16 @@ private Expression createSimpleExpressionMisc(String function) {
12041204
}
12051205
return SimpleExpressionBuilder.quoteExpression(exp);
12061206
}
1207+
// safeQuote function
1208+
remainder = ifStartsWithReturnRemainder("safeQuote(", function);
1209+
if (remainder != null) {
1210+
String exp = null;
1211+
String value = StringHelper.beforeLast(remainder, ")");
1212+
if (ObjectHelper.isNotEmpty(value)) {
1213+
exp = StringHelper.removeQuotes(value);
1214+
}
1215+
return SimpleExpressionBuilder.safeQuoteExpression(exp);
1216+
}
12071217

12081218
// trim function
12091219
remainder = ifStartsWithReturnRemainder("trim(", function);
@@ -2977,6 +2987,30 @@ private String createCodeExpressionMisc(CamelContext camelContext, String functi
29772987
}
29782988
return "Object o = " + exp + ";\n return quote(exchange, o);";
29792989
}
2990+
// safaeQuote function
2991+
remainder = ifStartsWithReturnRemainder("safeQuote(", function);
2992+
if (remainder != null) {
2993+
String exp = null;
2994+
String values = StringHelper.beforeLast(remainder, ")");
2995+
if (ObjectHelper.isNotEmpty(values)) {
2996+
String[] tokens = codeSplitSafe(values, ',', true, true);
2997+
if (tokens.length != 1) {
2998+
throw new SimpleParserException(
2999+
"Valid syntax: ${safeQuote(exp)} was: " + function, token.getIndex());
3000+
}
3001+
// single quotes should be double quotes
3002+
String s = tokens[0];
3003+
if (StringHelper.isSingleQuoted(s)) {
3004+
s = StringHelper.removeLeadingAndEndingQuotes(s);
3005+
s = StringQuoteHelper.doubleQuote(s);
3006+
}
3007+
exp = s;
3008+
}
3009+
if (ObjectHelper.isEmpty(exp)) {
3010+
exp = "body";
3011+
}
3012+
return "Object o = " + exp + ";\n return safeQuote(exchange, o);";
3013+
}
29803014

29813015
// trim function
29823016
remainder = ifStartsWithReturnRemainder("trim(", function);

core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2960,6 +2960,53 @@ public void testQuote() {
29602960
assertEquals("\"Hi\"", s);
29612961
}
29622962

2963+
@Test
2964+
public void testSafeQuote() {
2965+
exchange.getMessage().setBody("Hello World");
2966+
2967+
Expression expression = context.resolveLanguage("simple").createExpression("${safeQuote()}");
2968+
String s = expression.evaluate(exchange, String.class);
2969+
assertEquals("\"Hello World\"", s);
2970+
2971+
expression = context.resolveLanguage("simple").createExpression("${safeQuote(${body})}");
2972+
s = expression.evaluate(exchange, String.class);
2973+
assertEquals("\"Hello World\"", s);
2974+
2975+
expression = context.resolveLanguage("simple").createExpression("${safeQuote('Hi')}");
2976+
s = expression.evaluate(exchange, String.class);
2977+
assertEquals("\"Hi\"", s);
2978+
2979+
expression = context.resolveLanguage("simple").createExpression("${safeQuote(''Hi'')}");
2980+
s = expression.evaluate(exchange, String.class);
2981+
assertEquals("\"Hi\"", s);
2982+
2983+
exchange.getMessage().setBody(123);
2984+
expression = context.resolveLanguage("simple").createExpression("${safeQuote()}");
2985+
s = expression.evaluate(exchange, String.class);
2986+
assertEquals("123", s);
2987+
2988+
expression = context.resolveLanguage("simple").createExpression("${safeQuote(${body})}");
2989+
s = expression.evaluate(exchange, String.class);
2990+
assertEquals("123", s);
2991+
2992+
exchange.getMessage().setBody(true);
2993+
expression = context.resolveLanguage("simple").createExpression("${safeQuote()}");
2994+
s = expression.evaluate(exchange, String.class);
2995+
assertEquals("true", s);
2996+
2997+
expression = context.resolveLanguage("simple").createExpression("${safeQuote(${body})}");
2998+
s = expression.evaluate(exchange, String.class);
2999+
assertEquals("true", s);
3000+
3001+
Map<String, String> m = new LinkedHashMap<>();
3002+
m.put("A", "1");
3003+
m.put("B", "2");
3004+
exchange.getMessage().setBody(m);
3005+
expression = context.resolveLanguage("simple").createExpression("${safeQuote()}");
3006+
s = expression.evaluate(exchange, String.class);
3007+
assertEquals("\"{A=1, B=2}\"", s);
3008+
}
3009+
29633010
@Test
29643011
public void testTrim() {
29653012
exchange.getMessage().setBody(" Hello World ");

0 commit comments

Comments
 (0)