You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2011/09/12 16:11:41 UTC

svn commit: r1169753 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/language/simple/ main/java/org/apache/camel/language/simple/ast/ test/java/org/apache/camel/language/simple/

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;
+    }
+
+}