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 2017/10/08 12:02:48 UTC
[05/13] camel git commit: Camel java dsl parser prototype
Camel java dsl parser prototype
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/cb55f5a1
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/cb55f5a1
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/cb55f5a1
Branch: refs/heads/parser2
Commit: cb55f5a11fbe0656de2e981a4504a43ed707fa7d
Parents: 84cb66b
Author: Claus Ibsen <da...@apache.org>
Authored: Sat Oct 7 18:16:56 2017 +0200
Committer: Claus Ibsen <da...@apache.org>
Committed: Sat Oct 7 18:16:56 2017 +0200
----------------------------------------------------------------------
.../parser/AdvancedRouteBuilderParser.java | 57 ---
.../parser/RouteBuilderNodeDetailsParser.java | 24 +
.../apache/camel/parser/RouteBuilderParser.java | 36 ++
.../helper/AdvancedCamelJavaParserHelper.java | 440 ------------------
.../helper/CamelJavaTreeParserHelper.java | 443 +++++++++++++++++++
.../camel/parser/java/RoasterJavaDslTest.java | 17 +-
6 files changed, 517 insertions(+), 500 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/cb55f5a1/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/AdvancedRouteBuilderParser.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/AdvancedRouteBuilderParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/AdvancedRouteBuilderParser.java
deleted file mode 100644
index 9159203..0000000
--- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/AdvancedRouteBuilderParser.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * 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
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * 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.parser;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.camel.parser.helper.AdvancedCamelJavaParserHelper;
-import org.apache.camel.parser.helper.CamelJavaParserHelper;
-import org.apache.camel.parser.model.CamelNodeDetails;
-import org.jboss.forge.roaster.model.source.JavaClassSource;
-import org.jboss.forge.roaster.model.source.MethodSource;
-
-/**
- * TODO: Merge this to {@link RouteBuilderParser}
- */
-public class AdvancedRouteBuilderParser {
-
- // TODO: list of details, on per route
- public static CamelNodeDetails parseRouteBuilder(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
- boolean includeInlinedRouteBuilders) {
- AdvancedCamelJavaParserHelper parser = new AdvancedCamelJavaParserHelper();
-
- List<MethodSource<JavaClassSource>> methods = new ArrayList<>();
- MethodSource<JavaClassSource> method = CamelJavaParserHelper.findConfigureMethod(clazz);
- if (method != null) {
- methods.add(method);
- }
- if (includeInlinedRouteBuilders) {
- List<MethodSource<JavaClassSource>> inlinedMethods = CamelJavaParserHelper.findInlinedConfigureMethods(clazz);
- if (!inlinedMethods.isEmpty()) {
- methods.addAll(inlinedMethods);
- }
- }
-
- for (MethodSource<JavaClassSource> configureMethod : methods) {
- CamelNodeDetails details = parser.parseCamelRoute(clazz, baseDir, fullyQualifiedFileName, configureMethod);
- return details;
- }
-
- return null;
- }
-}
http://git-wip-us.apache.org/repos/asf/camel/blob/cb55f5a1/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderNodeDetailsParser.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderNodeDetailsParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderNodeDetailsParser.java
new file mode 100644
index 0000000..cb8591b
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderNodeDetailsParser.java
@@ -0,0 +1,24 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.parser;
+
+/**
+ * TODO: Merge this to {@link RouteBuilderParser}
+ */
+public class RouteBuilderNodeDetailsParser {
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/cb55f5a1/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
index d38e1d6..aa54cb3 100644
--- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
@@ -22,8 +22,10 @@ import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
+import org.apache.camel.parser.helper.CamelJavaTreeParserHelper;
import org.apache.camel.parser.helper.CamelJavaParserHelper;
import org.apache.camel.parser.model.CamelEndpointDetails;
+import org.apache.camel.parser.model.CamelNodeDetails;
import org.apache.camel.parser.model.CamelRouteDetails;
import org.apache.camel.parser.model.CamelSimpleExpressionDetails;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
@@ -48,6 +50,40 @@ public final class RouteBuilderParser {
}
/**
+ * Parses the java source class and build a route model (tree) of the discovered routes in the java source class.
+ *
+ * @param clazz the java source class
+ * @param baseDir the base of the source code
+ * @param fullyQualifiedFileName the fully qualified source code file name
+ * @return a list of route model (tree) of each discovered route
+ */
+ public static List<CamelNodeDetails> parseRouteBuilderTree(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+ boolean includeInlinedRouteBuilders) {
+
+ List<MethodSource<JavaClassSource>> methods = new ArrayList<>();
+ MethodSource<JavaClassSource> method = CamelJavaParserHelper.findConfigureMethod(clazz);
+ if (method != null) {
+ methods.add(method);
+ }
+ if (includeInlinedRouteBuilders) {
+ List<MethodSource<JavaClassSource>> inlinedMethods = CamelJavaParserHelper.findInlinedConfigureMethods(clazz);
+ if (!inlinedMethods.isEmpty()) {
+ methods.addAll(inlinedMethods);
+ }
+ }
+
+ CamelJavaTreeParserHelper parser = new CamelJavaTreeParserHelper();
+ List<CamelNodeDetails> list = new ArrayList<>();
+ for (MethodSource<JavaClassSource> configureMethod : methods) {
+ // TODO: list of details, on per route
+ CamelNodeDetails details = parser.parseCamelRoute(clazz, baseDir, fullyQualifiedFileName, configureMethod);
+ list.add(details);
+ }
+
+ return list;
+ }
+
+ /**
* Parses the java source class to discover Camel endpoints.
*
* @param clazz the java source class
http://git-wip-us.apache.org/repos/asf/camel/blob/cb55f5a1/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/AdvancedCamelJavaParserHelper.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/AdvancedCamelJavaParserHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/AdvancedCamelJavaParserHelper.java
deleted file mode 100644
index e57a09b..0000000
--- a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/AdvancedCamelJavaParserHelper.java
+++ /dev/null
@@ -1,440 +0,0 @@
-/**
- * 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.parser.helper;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
-import org.apache.camel.catalog.JSonSchemaHelper;
-import org.apache.camel.parser.RouteBuilderParser;
-import org.apache.camel.parser.model.CamelNodeDetails;
-import org.apache.camel.parser.model.CamelNodeDetailsFactory;
-import org.apache.camel.parser.roaster.StatementFieldSource;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Block;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.BooleanLiteral;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ExpressionStatement;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.FieldDeclaration;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.InfixExpression;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MemberValuePair;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodInvocation;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NormalAnnotation;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NumberLiteral;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ParenthesizedExpression;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.QualifiedName;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SimpleName;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SingleMemberAnnotation;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.StringLiteral;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Type;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationFragment;
-import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationStatement;
-import org.jboss.forge.roaster.model.Annotation;
-import org.jboss.forge.roaster.model.source.FieldSource;
-import org.jboss.forge.roaster.model.source.JavaClassSource;
-import org.jboss.forge.roaster.model.source.MethodSource;
-
-/**
- * A Camel Java parser that only depends on the Roaster API.
- * <p/>
- * This implementation is lower level details. For a higher level parser see {@link RouteBuilderParser}.
- */
-// TODO: rename this class
-public final class AdvancedCamelJavaParserHelper {
-
- private CamelCatalog camelCatalog = new DefaultCamelCatalog(true);
-
- public CamelNodeDetails parseCamelRoute(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
- MethodSource<JavaClassSource> method) {
-
- // find any from which is the start of the route
- CamelNodeDetailsFactory nodeFactory = CamelNodeDetailsFactory.newInstance();
-
- CamelNodeDetails route = nodeFactory.newNode(null, "route");
-
- if (method != null) {
- MethodDeclaration md = (MethodDeclaration) method.getInternal();
- Block block = md.getBody();
- if (block != null) {
- for (Object statement : md.getBody().statements()) {
- // must be a method call expression
- if (statement instanceof ExpressionStatement) {
- ExpressionStatement es = (ExpressionStatement) statement;
- Expression exp = es.getExpression();
- parseExpression(nodeFactory, fullyQualifiedFileName, clazz, block, exp, route);
- }
- }
- }
- }
-
- // now parse the route node and build the correct model/tree structure of the EIPs
-
- // re-create factory as we rebuild the tree
- nodeFactory = CamelNodeDetailsFactory.newInstance();
-
- CamelNodeDetails from = route.getOutputs().get(0);
- CamelNodeDetails answer = nodeFactory.copyNode(null, "from", from);
- answer.setFileName(fullyQualifiedFileName);
-
- CamelNodeDetails parent = answer;
- for (int i = 1; i < route.getOutputs().size(); i++) {
-
- CamelNodeDetails node = route.getOutputs().get(i);
- String name = node.getName();
-
- // TODO: use camel catalog to know about these types and when to do what
- // special for some EIPs
- if ("choice".equals(name)) {
- CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
- parent.addOutput(output);
- parent = output;
- } else if ("when".equals(name)) {
- parent = grandParent(parent, "choice");
- CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
- parent.addOutput(output);
- parent = output;
- } else if ("otherwise".equals(name)) {
- parent = grandParent(parent, "choice");
- CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
- parent.addOutput(output);
- parent = output;
- } else if ("end".equals(name) || "endChoice".equals(name) || "endDoTry".equals(name)) {
- // special for ending otherwise, as we end it automatic in Java DSL so do a double end then
- if ("otherwise".equals(parent.getName())) {
- parent = parent.getParent();
- }
- // parent should be grand parent
- parent = parent.getParent();
- } else {
- boolean hasOutput = hasOutput(name);
- if (hasOutput) {
- // has output so add as new child node
- CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
- parent.addOutput(output);
- parent = output;
- } else {
- // add straight to itself
- CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
- parent.addOutput(output);
- }
- }
- }
-
- return answer;
- }
-
- private boolean hasOutput(String name) {
- String json = camelCatalog.modelJSonSchema(name);
- List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("model", json, false);
- return isModelOutput(rows);
- }
-
- private static boolean isModelOutput(List<Map<String, String>> rows) {
- for (Map<String, String> row : rows) {
- if (row.containsKey("output")) {
- return "true".equals(row.get("output"));
- }
- }
- return false;
- }
-
- private static CamelNodeDetails grandParent(CamelNodeDetails node, String parentName) {
- if (node == null) {
- return null;
- }
- if (parentName.equals(node.getName())) {
- return node;
- } else {
- return grandParent(node.getParent(), parentName);
- }
- }
-
- private void parseExpression(CamelNodeDetailsFactory nodeFactory, String fullyQualifiedFileName,
- JavaClassSource clazz, Block block, Expression exp, CamelNodeDetails node) {
- if (exp == null) {
- return;
- }
- if (exp instanceof MethodInvocation) {
- MethodInvocation mi = (MethodInvocation) exp;
- node = doParseCamelModels(nodeFactory, fullyQualifiedFileName, clazz, block, mi, node);
- // if the method was called on another method, then recursive
- exp = mi.getExpression();
- parseExpression(nodeFactory, fullyQualifiedFileName, clazz, block, exp, node);
- }
- }
-
- private CamelNodeDetails doParseCamelModels(CamelNodeDetailsFactory nodeFactory, String fullyQualifiedFileName,
- JavaClassSource clazz, Block block, MethodInvocation mi, CamelNodeDetails node) {
- String name = mi.getName().getIdentifier();
-
- // special for Java DSL having some endXXX
- boolean isEnd = "end".equals(name) || "endChoice".equals(name) || "endDoTry".equals(name);
-
- // only include if its a known Camel model
- if (isEnd || camelCatalog.findModelNames().contains(name)) {
- CamelNodeDetails newNode = nodeFactory.newNode(node, name);
-
- // include source code details
- int pos = mi.getName().getStartPosition();
- int line = findLineNumber(fullyQualifiedFileName, pos);
- if (line > -1) {
- newNode.setLineNumber("" + line);
- }
- newNode.setFileName(fullyQualifiedFileName);
-
- node.addPreliminaryOutput(newNode);
- return node;
- }
-
- return node;
- }
-
- @SuppressWarnings("unchecked")
- private static FieldSource<JavaClassSource> getField(JavaClassSource clazz, Block block, SimpleName ref) {
- String fieldName = ref.getIdentifier();
- if (fieldName != null) {
- // find field in class
- FieldSource field = clazz != null ? clazz.getField(fieldName) : null;
- if (field == null) {
- field = findFieldInBlock(clazz, block, fieldName);
- }
- return field;
- }
- return null;
- }
-
- @SuppressWarnings("unchecked")
- private static FieldSource<JavaClassSource> findFieldInBlock(JavaClassSource clazz, Block block, String fieldName) {
- for (Object statement : block.statements()) {
- // try local statements first in the block
- if (statement instanceof VariableDeclarationStatement) {
- final Type type = ((VariableDeclarationStatement) statement).getType();
- for (Object obj : ((VariableDeclarationStatement) statement).fragments()) {
- if (obj instanceof VariableDeclarationFragment) {
- VariableDeclarationFragment fragment = (VariableDeclarationFragment) obj;
- SimpleName name = fragment.getName();
- if (name != null && fieldName.equals(name.getIdentifier())) {
- return new StatementFieldSource(clazz, fragment, type);
- }
- }
- }
- }
-
- // okay the field may be burried inside an anonymous inner class as a field declaration
- // outside the configure method, so lets go back to the parent and see what we can find
- ASTNode node = block.getParent();
- if (node instanceof MethodDeclaration) {
- node = node.getParent();
- }
- if (node instanceof AnonymousClassDeclaration) {
- List declarations = ((AnonymousClassDeclaration) node).bodyDeclarations();
- for (Object dec : declarations) {
- if (dec instanceof FieldDeclaration) {
- FieldDeclaration fd = (FieldDeclaration) dec;
- final Type type = fd.getType();
- for (Object obj : fd.fragments()) {
- if (obj instanceof VariableDeclarationFragment) {
- VariableDeclarationFragment fragment = (VariableDeclarationFragment) obj;
- SimpleName name = fragment.getName();
- if (name != null && fieldName.equals(name.getIdentifier())) {
- return new StatementFieldSource(clazz, fragment, type);
- }
- }
- }
- }
- }
- }
- }
- return null;
- }
-
- /**
- * @deprecated currently not in use
- */
- @Deprecated
- public static String getLiteralValue(JavaClassSource clazz, Block block, Expression expression) {
- // unwrap parenthesis
- if (expression instanceof ParenthesizedExpression) {
- expression = ((ParenthesizedExpression) expression).getExpression();
- }
-
- if (expression instanceof StringLiteral) {
- return ((StringLiteral) expression).getLiteralValue();
- } else if (expression instanceof BooleanLiteral) {
- return "" + ((BooleanLiteral) expression).booleanValue();
- } else if (expression instanceof NumberLiteral) {
- return ((NumberLiteral) expression).getToken();
- }
-
- // if it a method invocation then add a dummy value assuming the method invocation will return a valid response
- if (expression instanceof MethodInvocation) {
- String name = ((MethodInvocation) expression).getName().getIdentifier();
- return "{{" + name + "}}";
- }
-
- // if its a qualified name (usually a constant field in another class)
- // then add a dummy value as we cannot find the field value in other classes and maybe even outside the
- // source code we have access to
- if (expression instanceof QualifiedName) {
- QualifiedName qn = (QualifiedName) expression;
- String name = qn.getFullyQualifiedName();
- return "{{" + name + "}}";
- }
-
- if (expression instanceof SimpleName) {
- FieldSource<JavaClassSource> field = getField(clazz, block, (SimpleName) expression);
- if (field != null) {
- // is the field annotated with a Camel endpoint
- if (field.getAnnotations() != null) {
- for (Annotation ann : field.getAnnotations()) {
- boolean valid = "org.apache.camel.EndpointInject".equals(ann.getQualifiedName()) || "org.apache.camel.cdi.Uri".equals(ann.getQualifiedName());
- if (valid) {
- Expression exp = (Expression) ann.getInternal();
- if (exp instanceof SingleMemberAnnotation) {
- exp = ((SingleMemberAnnotation) exp).getValue();
- } else if (exp instanceof NormalAnnotation) {
- List values = ((NormalAnnotation) exp).values();
- for (Object value : values) {
- MemberValuePair pair = (MemberValuePair) value;
- if ("uri".equals(pair.getName().toString())) {
- exp = pair.getValue();
- break;
- }
- }
- }
- if (exp != null) {
- return getLiteralValue(clazz, block, exp);
- }
- }
- }
- }
- // is the field an org.apache.camel.Endpoint type?
- if ("Endpoint".equals(field.getType().getSimpleName())) {
- // then grab the uri from the first argument
- VariableDeclarationFragment vdf = (VariableDeclarationFragment) field.getInternal();
- expression = vdf.getInitializer();
- if (expression instanceof MethodInvocation) {
- MethodInvocation mi = (MethodInvocation) expression;
- List args = mi.arguments();
- if (args != null && args.size() > 0) {
- // the first argument has the endpoint uri
- expression = (Expression) args.get(0);
- return getLiteralValue(clazz, block, expression);
- }
- }
- } else {
- // no annotations so try its initializer
- VariableDeclarationFragment vdf = (VariableDeclarationFragment) field.getInternal();
- expression = vdf.getInitializer();
- if (expression == null) {
- // its a field which has no initializer, then add a dummy value assuming the field will be initialized at runtime
- return "{{" + field.getName() + "}}";
- } else {
- return getLiteralValue(clazz, block, expression);
- }
- }
- } else {
- // we could not find the field in this class/method, so its maybe from some other super class, so insert a dummy value
- final String fieldName = ((SimpleName) expression).getIdentifier();
- return "{{" + fieldName + "}}";
- }
- } else if (expression instanceof InfixExpression) {
- String answer = null;
- // is it a string that is concat together?
- InfixExpression ie = (InfixExpression) expression;
- if (InfixExpression.Operator.PLUS.equals(ie.getOperator())) {
-
- String val1 = getLiteralValue(clazz, block, ie.getLeftOperand());
- String val2 = getLiteralValue(clazz, block, ie.getRightOperand());
-
- // if numeric then we plus the values, otherwise we string concat
- boolean numeric = isNumericOperator(clazz, block, ie.getLeftOperand()) && isNumericOperator(clazz, block, ie.getRightOperand());
- if (numeric) {
- Long num1 = val1 != null ? Long.valueOf(val1) : 0;
- Long num2 = val2 != null ? Long.valueOf(val2) : 0;
- answer = "" + (num1 + num2);
- } else {
- answer = (val1 != null ? val1 : "") + (val2 != null ? val2 : "");
- }
-
- if (!answer.isEmpty()) {
- // include extended when we concat on 2 or more lines
- List extended = ie.extendedOperands();
- if (extended != null) {
- for (Object ext : extended) {
- String val3 = getLiteralValue(clazz, block, (Expression) ext);
- if (numeric) {
- Long num3 = val3 != null ? Long.valueOf(val3) : 0;
- Long num = Long.valueOf(answer);
- answer = "" + (num + num3);
- } else {
- answer += val3 != null ? val3 : "";
- }
- }
- }
- }
- }
- return answer;
- }
-
- return null;
- }
-
- private static boolean isNumericOperator(JavaClassSource clazz, Block block, Expression expression) {
- if (expression instanceof NumberLiteral) {
- return true;
- } else if (expression instanceof SimpleName) {
- FieldSource field = getField(clazz, block, (SimpleName) expression);
- if (field != null) {
- return field.getType().isType("int") || field.getType().isType("long")
- || field.getType().isType("Integer") || field.getType().isType("Long");
- }
- }
- return false;
- }
-
- private static int findLineNumber(String fullyQualifiedFileName, int position) {
- int lines = 0;
-
- try {
- int current = 0;
- try (BufferedReader br = new BufferedReader(new FileReader(new File(fullyQualifiedFileName)))) {
- String line;
- while ((line = br.readLine()) != null) {
- lines++;
- current += line.length() + 1; // add 1 for line feed
- if (current >= position) {
- return lines;
- }
- }
- }
- } catch (Exception e) {
- // ignore
- return -1;
- }
-
- return lines;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/camel/blob/cb55f5a1/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
new file mode 100644
index 0000000..af19acd
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
@@ -0,0 +1,443 @@
+/**
+ * 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.parser.helper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.catalog.JSonSchemaHelper;
+import org.apache.camel.parser.RouteBuilderParser;
+import org.apache.camel.parser.model.CamelNodeDetails;
+import org.apache.camel.parser.model.CamelNodeDetailsFactory;
+import org.apache.camel.parser.roaster.StatementFieldSource;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Block;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.InfixExpression;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MemberValuePair;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodInvocation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NumberLiteral;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.QualifiedName;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SimpleName;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.StringLiteral;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Type;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.jboss.forge.roaster.model.Annotation;
+import org.jboss.forge.roaster.model.source.FieldSource;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.MethodSource;
+
+/**
+ * A Camel Java tree parser that only depends on the Roaster API.
+ * <p/>
+ * This implement is used for parsing the Camel routes and build a tree structure of the EIP nodes.
+ *
+ * @see CamelJavaParserHelper for parser that can discover endpoints and simple expressions
+ */
+public final class CamelJavaTreeParserHelper {
+
+ private CamelCatalog camelCatalog = new DefaultCamelCatalog(true);
+
+ // TODO: add support for multiple routes
+ // TODO: rename this method
+ public CamelNodeDetails parseCamelRoute(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+ MethodSource<JavaClassSource> method) {
+
+ // find any from which is the start of the route
+ CamelNodeDetailsFactory nodeFactory = CamelNodeDetailsFactory.newInstance();
+
+ CamelNodeDetails route = nodeFactory.newNode(null, "route");
+
+ if (method != null) {
+ MethodDeclaration md = (MethodDeclaration) method.getInternal();
+ Block block = md.getBody();
+ if (block != null) {
+ for (Object statement : md.getBody().statements()) {
+ // must be a method call expression
+ if (statement instanceof ExpressionStatement) {
+ ExpressionStatement es = (ExpressionStatement) statement;
+ Expression exp = es.getExpression();
+ parseExpression(nodeFactory, fullyQualifiedFileName, clazz, block, exp, route);
+ }
+ }
+ }
+ }
+
+ // now parse the route node and build the correct model/tree structure of the EIPs
+
+ // re-create factory as we rebuild the tree
+ nodeFactory = CamelNodeDetailsFactory.newInstance();
+
+ CamelNodeDetails from = route.getOutputs().get(0);
+ CamelNodeDetails answer = nodeFactory.copyNode(null, "from", from);
+ answer.setFileName(fullyQualifiedFileName);
+
+ CamelNodeDetails parent = answer;
+ for (int i = 1; i < route.getOutputs().size(); i++) {
+
+ CamelNodeDetails node = route.getOutputs().get(i);
+ String name = node.getName();
+
+ // TODO: use camel catalog to know about these types and when to do what
+ // special for some EIPs
+ if ("choice".equals(name)) {
+ CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
+ parent.addOutput(output);
+ parent = output;
+ } else if ("when".equals(name)) {
+ parent = grandParent(parent, "choice");
+ CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
+ parent.addOutput(output);
+ parent = output;
+ } else if ("otherwise".equals(name)) {
+ parent = grandParent(parent, "choice");
+ CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
+ parent.addOutput(output);
+ parent = output;
+ } else if ("end".equals(name) || "endChoice".equals(name) || "endDoTry".equals(name)) {
+ // special for ending otherwise, as we end it automatic in Java DSL so do a double end then
+ if ("otherwise".equals(parent.getName())) {
+ parent = parent.getParent();
+ }
+ // parent should be grand parent
+ parent = parent.getParent();
+ } else {
+ boolean hasOutput = hasOutput(name);
+ if (hasOutput) {
+ // has output so add as new child node
+ CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
+ parent.addOutput(output);
+ parent = output;
+ } else {
+ // add straight to itself
+ CamelNodeDetails output = nodeFactory.copyNode(parent, name, node);
+ parent.addOutput(output);
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ private boolean hasOutput(String name) {
+ String json = camelCatalog.modelJSonSchema(name);
+ List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("model", json, false);
+ return isModelOutput(rows);
+ }
+
+ private static boolean isModelOutput(List<Map<String, String>> rows) {
+ for (Map<String, String> row : rows) {
+ if (row.containsKey("output")) {
+ return "true".equals(row.get("output"));
+ }
+ }
+ return false;
+ }
+
+ private static CamelNodeDetails grandParent(CamelNodeDetails node, String parentName) {
+ if (node == null) {
+ return null;
+ }
+ if (parentName.equals(node.getName())) {
+ return node;
+ } else {
+ return grandParent(node.getParent(), parentName);
+ }
+ }
+
+ private void parseExpression(CamelNodeDetailsFactory nodeFactory, String fullyQualifiedFileName,
+ JavaClassSource clazz, Block block, Expression exp, CamelNodeDetails node) {
+ if (exp == null) {
+ return;
+ }
+ if (exp instanceof MethodInvocation) {
+ MethodInvocation mi = (MethodInvocation) exp;
+ node = doParseCamelModels(nodeFactory, fullyQualifiedFileName, clazz, block, mi, node);
+ // if the method was called on another method, then recursive
+ exp = mi.getExpression();
+ parseExpression(nodeFactory, fullyQualifiedFileName, clazz, block, exp, node);
+ }
+ }
+
+ private CamelNodeDetails doParseCamelModels(CamelNodeDetailsFactory nodeFactory, String fullyQualifiedFileName,
+ JavaClassSource clazz, Block block, MethodInvocation mi, CamelNodeDetails node) {
+ String name = mi.getName().getIdentifier();
+
+ // special for Java DSL having some endXXX
+ boolean isEnd = "end".equals(name) || "endChoice".equals(name) || "endDoTry".equals(name);
+
+ // only include if its a known Camel model
+ if (isEnd || camelCatalog.findModelNames().contains(name)) {
+ CamelNodeDetails newNode = nodeFactory.newNode(node, name);
+
+ // include source code details
+ int pos = mi.getName().getStartPosition();
+ int line = findLineNumber(fullyQualifiedFileName, pos);
+ if (line > -1) {
+ newNode.setLineNumber("" + line);
+ }
+ newNode.setFileName(fullyQualifiedFileName);
+
+ node.addPreliminaryOutput(newNode);
+ return node;
+ }
+
+ return node;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static FieldSource<JavaClassSource> getField(JavaClassSource clazz, Block block, SimpleName ref) {
+ String fieldName = ref.getIdentifier();
+ if (fieldName != null) {
+ // find field in class
+ FieldSource field = clazz != null ? clazz.getField(fieldName) : null;
+ if (field == null) {
+ field = findFieldInBlock(clazz, block, fieldName);
+ }
+ return field;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static FieldSource<JavaClassSource> findFieldInBlock(JavaClassSource clazz, Block block, String fieldName) {
+ for (Object statement : block.statements()) {
+ // try local statements first in the block
+ if (statement instanceof VariableDeclarationStatement) {
+ final Type type = ((VariableDeclarationStatement) statement).getType();
+ for (Object obj : ((VariableDeclarationStatement) statement).fragments()) {
+ if (obj instanceof VariableDeclarationFragment) {
+ VariableDeclarationFragment fragment = (VariableDeclarationFragment) obj;
+ SimpleName name = fragment.getName();
+ if (name != null && fieldName.equals(name.getIdentifier())) {
+ return new StatementFieldSource(clazz, fragment, type);
+ }
+ }
+ }
+ }
+
+ // okay the field may be burried inside an anonymous inner class as a field declaration
+ // outside the configure method, so lets go back to the parent and see what we can find
+ ASTNode node = block.getParent();
+ if (node instanceof MethodDeclaration) {
+ node = node.getParent();
+ }
+ if (node instanceof AnonymousClassDeclaration) {
+ List declarations = ((AnonymousClassDeclaration) node).bodyDeclarations();
+ for (Object dec : declarations) {
+ if (dec instanceof FieldDeclaration) {
+ FieldDeclaration fd = (FieldDeclaration) dec;
+ final Type type = fd.getType();
+ for (Object obj : fd.fragments()) {
+ if (obj instanceof VariableDeclarationFragment) {
+ VariableDeclarationFragment fragment = (VariableDeclarationFragment) obj;
+ SimpleName name = fragment.getName();
+ if (name != null && fieldName.equals(name.getIdentifier())) {
+ return new StatementFieldSource(clazz, fragment, type);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated currently not in use
+ */
+ @Deprecated
+ public static String getLiteralValue(JavaClassSource clazz, Block block, Expression expression) {
+ // unwrap parenthesis
+ if (expression instanceof ParenthesizedExpression) {
+ expression = ((ParenthesizedExpression) expression).getExpression();
+ }
+
+ if (expression instanceof StringLiteral) {
+ return ((StringLiteral) expression).getLiteralValue();
+ } else if (expression instanceof BooleanLiteral) {
+ return "" + ((BooleanLiteral) expression).booleanValue();
+ } else if (expression instanceof NumberLiteral) {
+ return ((NumberLiteral) expression).getToken();
+ }
+
+ // if it a method invocation then add a dummy value assuming the method invocation will return a valid response
+ if (expression instanceof MethodInvocation) {
+ String name = ((MethodInvocation) expression).getName().getIdentifier();
+ return "{{" + name + "}}";
+ }
+
+ // if its a qualified name (usually a constant field in another class)
+ // then add a dummy value as we cannot find the field value in other classes and maybe even outside the
+ // source code we have access to
+ if (expression instanceof QualifiedName) {
+ QualifiedName qn = (QualifiedName) expression;
+ String name = qn.getFullyQualifiedName();
+ return "{{" + name + "}}";
+ }
+
+ if (expression instanceof SimpleName) {
+ FieldSource<JavaClassSource> field = getField(clazz, block, (SimpleName) expression);
+ if (field != null) {
+ // is the field annotated with a Camel endpoint
+ if (field.getAnnotations() != null) {
+ for (Annotation ann : field.getAnnotations()) {
+ boolean valid = "org.apache.camel.EndpointInject".equals(ann.getQualifiedName()) || "org.apache.camel.cdi.Uri".equals(ann.getQualifiedName());
+ if (valid) {
+ Expression exp = (Expression) ann.getInternal();
+ if (exp instanceof SingleMemberAnnotation) {
+ exp = ((SingleMemberAnnotation) exp).getValue();
+ } else if (exp instanceof NormalAnnotation) {
+ List values = ((NormalAnnotation) exp).values();
+ for (Object value : values) {
+ MemberValuePair pair = (MemberValuePair) value;
+ if ("uri".equals(pair.getName().toString())) {
+ exp = pair.getValue();
+ break;
+ }
+ }
+ }
+ if (exp != null) {
+ return getLiteralValue(clazz, block, exp);
+ }
+ }
+ }
+ }
+ // is the field an org.apache.camel.Endpoint type?
+ if ("Endpoint".equals(field.getType().getSimpleName())) {
+ // then grab the uri from the first argument
+ VariableDeclarationFragment vdf = (VariableDeclarationFragment) field.getInternal();
+ expression = vdf.getInitializer();
+ if (expression instanceof MethodInvocation) {
+ MethodInvocation mi = (MethodInvocation) expression;
+ List args = mi.arguments();
+ if (args != null && args.size() > 0) {
+ // the first argument has the endpoint uri
+ expression = (Expression) args.get(0);
+ return getLiteralValue(clazz, block, expression);
+ }
+ }
+ } else {
+ // no annotations so try its initializer
+ VariableDeclarationFragment vdf = (VariableDeclarationFragment) field.getInternal();
+ expression = vdf.getInitializer();
+ if (expression == null) {
+ // its a field which has no initializer, then add a dummy value assuming the field will be initialized at runtime
+ return "{{" + field.getName() + "}}";
+ } else {
+ return getLiteralValue(clazz, block, expression);
+ }
+ }
+ } else {
+ // we could not find the field in this class/method, so its maybe from some other super class, so insert a dummy value
+ final String fieldName = ((SimpleName) expression).getIdentifier();
+ return "{{" + fieldName + "}}";
+ }
+ } else if (expression instanceof InfixExpression) {
+ String answer = null;
+ // is it a string that is concat together?
+ InfixExpression ie = (InfixExpression) expression;
+ if (InfixExpression.Operator.PLUS.equals(ie.getOperator())) {
+
+ String val1 = getLiteralValue(clazz, block, ie.getLeftOperand());
+ String val2 = getLiteralValue(clazz, block, ie.getRightOperand());
+
+ // if numeric then we plus the values, otherwise we string concat
+ boolean numeric = isNumericOperator(clazz, block, ie.getLeftOperand()) && isNumericOperator(clazz, block, ie.getRightOperand());
+ if (numeric) {
+ Long num1 = val1 != null ? Long.valueOf(val1) : 0;
+ Long num2 = val2 != null ? Long.valueOf(val2) : 0;
+ answer = "" + (num1 + num2);
+ } else {
+ answer = (val1 != null ? val1 : "") + (val2 != null ? val2 : "");
+ }
+
+ if (!answer.isEmpty()) {
+ // include extended when we concat on 2 or more lines
+ List extended = ie.extendedOperands();
+ if (extended != null) {
+ for (Object ext : extended) {
+ String val3 = getLiteralValue(clazz, block, (Expression) ext);
+ if (numeric) {
+ Long num3 = val3 != null ? Long.valueOf(val3) : 0;
+ Long num = Long.valueOf(answer);
+ answer = "" + (num + num3);
+ } else {
+ answer += val3 != null ? val3 : "";
+ }
+ }
+ }
+ }
+ }
+ return answer;
+ }
+
+ return null;
+ }
+
+ private static boolean isNumericOperator(JavaClassSource clazz, Block block, Expression expression) {
+ if (expression instanceof NumberLiteral) {
+ return true;
+ } else if (expression instanceof SimpleName) {
+ FieldSource field = getField(clazz, block, (SimpleName) expression);
+ if (field != null) {
+ return field.getType().isType("int") || field.getType().isType("long")
+ || field.getType().isType("Integer") || field.getType().isType("Long");
+ }
+ }
+ return false;
+ }
+
+ private static int findLineNumber(String fullyQualifiedFileName, int position) {
+ int lines = 0;
+
+ try {
+ int current = 0;
+ try (BufferedReader br = new BufferedReader(new FileReader(new File(fullyQualifiedFileName)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines++;
+ current += line.length() + 1; // add 1 for line feed
+ if (current >= position) {
+ return lines;
+ }
+ }
+ }
+ } catch (Exception e) {
+ // ignore
+ return -1;
+ }
+
+ return lines;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/cb55f5a1/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
index 928ac87..9041e2f 100644
--- a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
+++ b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
@@ -17,8 +17,9 @@
package org.apache.camel.parser.java;
import java.io.File;
+import java.util.List;
-import org.apache.camel.parser.AdvancedRouteBuilderParser;
+import org.apache.camel.parser.RouteBuilderParser;
import org.apache.camel.parser.model.CamelNodeDetails;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.jboss.forge.roaster.Roaster;
@@ -37,14 +38,24 @@ public class RoasterJavaDslTest extends CamelTestSupport {
}
@Test
- public void parse() throws Exception {
+ public void parseTree() throws Exception {
JavaClassSource clazz = (JavaClassSource) Roaster.parse(new File("src/test/java/org/apache/camel/parser/java/MyJavaDslRouteBuilder.java"));
- CamelNodeDetails details = AdvancedRouteBuilderParser.parseRouteBuilder(clazz, ".",
+ List<CamelNodeDetails> list = RouteBuilderParser.parseRouteBuilderTree(clazz, ".",
"src/test/java/org/apache/camel/parser/java/MyJavaDslRouteBuilder.java",true);
+ assertEquals(1, list.size());
+ CamelNodeDetails details = list.get(0);
+ assertEquals("src/test/java/org/apache/camel/parser/java/MyJavaDslRouteBuilder.java", details.getFileName());
+
String tree = details.dump(0);
LOG.info("\n" + tree);
+ assertTrue(tree.contains("25\tfrom"));
+ assertTrue(tree.contains("27\t setHeader"));
+ assertTrue(tree.contains("30\t toD"));
+ assertTrue(tree.contains("33\t otherwise"));
+ assertTrue(tree.contains("36\t to"));
+
String name = details.getFileName();
System.out.println(name);
System.out.println(tree);