Author: davsclaus
Date: Mon Sep 12 14:11:40 2011
New Revision: 1169753
URL: http://svn.apache.org/viewvc?rev=1169753&view=rev
Log:
CAMEL-4445: Simple language now supports nested functions.
Added:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimplePropertiesNestedTest.java
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionInvalidTest.java
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionTest.java
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
(original)
+++
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/BaseSimpleParser.java
Mon Sep 12 14:11:40 2011
@@ -120,8 +120,18 @@ public abstract class BaseSimpleParser {
if (stack.isEmpty()) {
throw new
SimpleParserException(token.getToken().getType().getType() + " has no matching
start token", token.getToken().getIndex());
}
+
Block top = stack.pop();
- answer.add(top);
+ // if there is a block on the stack then it should accept the
child token
+ Block block = stack.isEmpty() ? null : stack.peek();
+ if (block != null) {
+ if (!block.acceptAndAddNode(top)) {
+ throw new
SimpleParserException(block.getToken().getType() + " cannot accept " +
token.getToken().getType(), token.getToken().getIndex());
+ }
+ } else {
+ // no block, so add to answer
+ answer.add(top);
+ }
} else {
// if there is a block on the stack then it should accept the
child token
Block block = stack.isEmpty() ? null : stack.peek();
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
(original)
+++
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java
Mon Sep 12 14:11:40 2011
@@ -167,10 +167,17 @@ public class SimpleExpressionParser exte
if (accept(TokenType.functionStart)) {
nextToken();
while (!token.getType().isFunctionEnd() &&
!token.getType().isEol()) {
- // we need to loop until we find the ending function quote, or
the eol
+ if (token.getType().isFunctionStart()) {
+ // embedded function
+ functionText();
+ }
+ // we need to loop until we find the ending function quote, an
embedded function, or the eol
nextToken();
}
- expect(TokenType.functionEnd);
+ // if its not an embedded function then we expect the end token
+ if (!token.getType().isFunctionStart()) {
+ expect(TokenType.functionEnd);
+ }
return true;
}
return false;
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
(original)
+++
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java
Mon Sep 12 14:11:40 2011
@@ -494,13 +494,19 @@ public class SimplePredicateParser exten
protected boolean functionText() {
if (accept(TokenType.functionStart)) {
- nextToken(TokenType.functionEnd, TokenType.eol);
nextToken();
while (!token.getType().isFunctionEnd() &&
!token.getType().isEol()) {
- // we need to loop until we find the ending function quote, or
the eol
- nextToken(TokenType.functionEnd, TokenType.eol);
+ if (token.getType().isFunctionStart()) {
+ // embedded function
+ functionText();
+ }
+ // we need to loop until we find the ending function quote, an
embedded function, or the eol
+ nextToken();
+ }
+ // if its not an embedded function then we expect the end token
+ if (!token.getType().isFunctionStart()) {
+ expect(TokenType.functionEnd);
}
- expect(TokenType.functionEnd);
return true;
}
return false;
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
(original)
+++
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/CompositeNodes.java
Mon Sep 12 14:11:40 2011
@@ -47,6 +47,10 @@ public class CompositeNodes extends Base
children.add(child);
}
+ public List<SimpleNode> getChildren() {
+ return children;
+ }
+
@Override
public Expression createExpression(String expression) {
if (children.isEmpty()) {
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
(original)
+++
camel/trunk/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java
Mon Sep 12 14:11:40 2011
@@ -16,7 +16,10 @@
*/
package org.apache.camel.language.simple.ast;
+import org.apache.camel.Exchange;
import org.apache.camel.Expression;
+import org.apache.camel.language.simple.SimpleIllegalSyntaxException;
+import org.apache.camel.language.simple.SimpleParserException;
import org.apache.camel.language.simple.SimpleToken;
/**
@@ -24,30 +27,88 @@ import org.apache.camel.language.simple.
*/
public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart {
- private LiteralNode literal;
+ private CompositeNodes block;
public SimpleFunctionStart(SimpleToken token) {
super(token);
+ this.block = new CompositeNodes(token);
}
@Override
public String toString() {
// output a nice toString so it makes debugging easier as we can see
the entire block
- return "${" + literal + "}";
+ return "${" + block + "}";
}
@Override
public Expression createExpression(String expression) {
+ // a function can either be a simple literal function, or contain
nested functions
+ if (block.getChildren().size() == 1 && block.getChildren().get(0)
instanceof LiteralNode) {
+ return doCreateLiteralExpression(expression);
+ } else {
+ return doCreateCompositeExpression(expression);
+ }
+ }
+
+ private Expression doCreateLiteralExpression(final String expression) {
SimpleFunctionExpression function = new
SimpleFunctionExpression(this.getToken());
+ LiteralNode literal = (LiteralNode) block.getChildren().get(0);
function.addText(literal.getText());
return function.createExpression(expression);
}
+ private Expression doCreateCompositeExpression(final String expression) {
+ final SimpleToken token = getToken();
+ return new Expression() {
+ @Override
+ public <T> T evaluate(Exchange exchange, Class<T> type) {
+ StringBuilder sb = new StringBuilder();
+
+ // we need to concat the block so we have the expression
+ for (SimpleNode child : block.getChildren()) {
+ if (child instanceof LiteralNode) {
+ String text = ((LiteralNode) child).getText();
+ sb.append(text);
+ } else if (child instanceof SimpleFunctionStart) {
+ try {
+ // pass in null when we evaluate the nested
expressions
+ Expression nested = child.createExpression(null);
+ String text = nested.evaluate(exchange,
String.class);
+ if (text != null) {
+ sb.append(text);
+ }
+ } catch (SimpleParserException e) {
+ // must rethrow parser exception as illegal syntax
with details about the location
+ throw new SimpleIllegalSyntaxException(expression,
e.getIndex(), e.getMessage(), e);
+ }
+ }
+ }
+
+ // we have now concat the block as a String which contains the
function expression
+ // which we then need to evaluate as a function
+ String exp = sb.toString();
+ SimpleFunctionExpression function = new
SimpleFunctionExpression(token);
+ function.addText(exp);
+ try {
+ return function.createExpression(exp).evaluate(exchange,
type);
+ } catch (SimpleParserException e) {
+ // must rethrow parser exception as illegal syntax with
details about the location
+ throw new SimpleIllegalSyntaxException(expression,
e.getIndex(), e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return expression;
+ }
+ };
+ }
+
@Override
public boolean acceptAndAddNode(SimpleNode node) {
- // only accept literals as it contains the text for the function
- if (node instanceof LiteralNode) {
- literal = (LiteralNode) node;
+ // only accept literals or embedded functions
+ if (node instanceof LiteralNode || node instanceof
SimpleFunctionStart) {
+ block.addChild(node);
return true;
} else {
return false;
Modified:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionInvalidTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionInvalidTest.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionInvalidTest.java
(original)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionInvalidTest.java
Mon Sep 12 14:11:40 2011
@@ -23,23 +23,23 @@ import org.apache.camel.ExchangeTestSupp
*/
public class SimpleParserExpressionInvalidTest extends ExchangeTestSupport {
- public void testSimpleFunctionInvalid() throws Exception {
- SimpleExpressionParser parser = new
SimpleExpressionParser("${body${foo}}");
+ public void testSimpleUnbalanceFunction() throws Exception {
+ SimpleExpressionParser parser = new SimpleExpressionParser("${body is
a nice day");
try {
parser.parseExpression();
fail("Should thrown exception");
} catch (SimpleIllegalSyntaxException e) {
- assertEquals(6, e.getIndex());
+ assertEquals(19, e.getIndex());
}
}
- public void testSimpleUnbalanceFunction() throws Exception {
- SimpleExpressionParser parser = new SimpleExpressionParser("${body is
a nice day");
+ public void testSimpleNestedUnbalanceFunction() throws Exception {
+ SimpleExpressionParser parser = new
SimpleExpressionParser("${body${foo}");
try {
parser.parseExpression();
fail("Should thrown exception");
} catch (SimpleIllegalSyntaxException e) {
- assertEquals(19, e.getIndex());
+ assertEquals(11, e.getIndex());
}
}
@@ -53,4 +53,15 @@ public class SimpleParserExpressionInval
}
}
+ public void testSimpleNestedUnknownFunction() throws Exception {
+ SimpleExpressionParser parser = new SimpleExpressionParser("Hello
${bodyAs(${foo})} how are you?");
+ try {
+ // nested functions can only be syntax evaluated when evaluating
an exchange at runtime
+ parser.parseExpression().evaluate(exchange, String.class);
+ fail("Should thrown exception");
+ } catch (SimpleIllegalSyntaxException e) {
+ assertEquals(15, e.getIndex());
+ }
+ }
+
}
Modified:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionTest.java?rev=1169753&r1=1169752&r2=1169753&view=diff
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionTest.java
(original)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimpleParserExpressionTest.java
Mon Sep 12 14:11:40 2011
@@ -115,4 +115,54 @@ public class SimpleParserExpressionTest
assertEquals(Integer.valueOf(121), exp.evaluate(exchange,
Integer.class));
}
+ public void testHeaderNestedFunction() throws Exception {
+ exchange.getIn().setBody("foo");
+ exchange.getIn().setHeader("foo", "abc");
+ SimpleExpressionParser parser = new
SimpleExpressionParser("${header.${body}}");
+ Expression exp = parser.parseExpression();
+
+ Object obj = exp.evaluate(exchange, Object.class);
+ assertNotNull(obj);
+ assertEquals("abc", obj);
+ }
+
+ public void testBodyAsNestedFunction() throws Exception {
+ exchange.getIn().setBody("123");
+ exchange.getIn().setHeader("foo", "Integer");
+ SimpleExpressionParser parser = new
SimpleExpressionParser("${bodyAs(${header.foo})}");
+ Expression exp = parser.parseExpression();
+
+ Object obj = exp.evaluate(exchange, Object.class);
+ assertNotNull(obj);
+ Integer num = assertIsInstanceOf(Integer.class, obj);
+ assertEquals(123, num.intValue());
+ }
+
+ public void testThreeNestedFunctions() throws Exception {
+ exchange.getIn().setBody("123");
+ exchange.getIn().setHeader("foo", "Int");
+ exchange.getIn().setHeader("bar", "e");
+ exchange.getIn().setHeader("baz", "ger");
+ SimpleExpressionParser parser = new
SimpleExpressionParser("${bodyAs(${header.foo}${header.bar}${header.baz})}");
+ Expression exp = parser.parseExpression();
+
+ Object obj = exp.evaluate(exchange, Object.class);
+ assertNotNull(obj);
+ Integer num = assertIsInstanceOf(Integer.class, obj);
+ assertEquals(123, num.intValue());
+ }
+
+ public void testNestedNestedFunctions() throws Exception {
+ exchange.getIn().setBody("123");
+ exchange.getIn().setHeader("foo", "Integer");
+ exchange.getIn().setHeader("bar", "foo");
+ SimpleExpressionParser parser = new
SimpleExpressionParser("${bodyAs(${header.${header.bar}})}");
+ Expression exp = parser.parseExpression();
+
+ Object obj = exp.evaluate(exchange, Object.class);
+ assertNotNull(obj);
+ Integer num = assertIsInstanceOf(Integer.class, obj);
+ assertEquals(123, num.intValue());
+ }
+
}
Added:
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimplePropertiesNestedTest.java
URL:
http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimplePropertiesNestedTest.java?rev=1169753&view=auto
==============================================================================
---
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimplePropertiesNestedTest.java
(added)
+++
camel/trunk/camel-core/src/test/java/org/apache/camel/language/simple/SimplePropertiesNestedTest.java
Mon Sep 12 14:11:40 2011
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.camel.language.simple;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.properties.PropertiesComponent;
+
+/**
+ *
+ */
+public class SimplePropertiesNestedTest extends ContextTestSupport {
+
+ public void testSimplePropertiesNested() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedMessageCount(1);
+ mock.expectedBodiesReceived("Hello World");
+ mock.expectedHeaderReceived("myHeader", "Beer taste good");
+
+ template.sendBodyAndHeader("direct:start", "Hello World", "beer",
"bar.quote");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+
.setHeader("myHeader").simple("${properties:${header.beer}}")
+ .to("mock:result");
+ }
+ };
+ }
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ CamelContext context = super.createCamelContext();
+
+ PropertiesComponent pc = new PropertiesComponent();
+ pc.setCamelContext(context);
+ pc.setLocations(new
String[]{"org/apache/camel/component/properties/bar.properties"});
+ context.addComponent("properties", pc);
+
+ return context;
+ }
+
+}