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 2016/12/05 13:12:38 UTC
[05/25] camel git commit: CAMEL-10559: route parser for java and xml
to parse source code. Donated from fabric8 project.
CAMEL-10559: route parser for java and xml to parse source code. Donated from fabric8 project.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/42e4d973
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/42e4d973
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/42e4d973
Branch: refs/heads/master
Commit: 42e4d9733fab7082b90b5d16f90d4e8b4b4a3a85
Parents: 1d23b62
Author: Claus Ibsen <da...@apache.org>
Authored: Mon Dec 5 11:41:11 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Mon Dec 5 14:04:48 2016 +0100
----------------------------------------------------------------------
tooling/pom.xml | 1 +
tooling/route-parser/pom.xml | 43 ++
.../camel/parser/AnonymousMethodSource.java | 426 +++++++++++++
.../camel/parser/CamelJavaParserHelper.java | 630 +++++++++++++++++++
.../org/apache/camel/parser/ParserResult.java | 68 ++
.../apache/camel/parser/RouteBuilderParser.java | 281 +++++++++
.../camel/parser/StatementFieldSource.java | 278 ++++++++
.../parser/model/CamelEndpointDetails.java | 157 +++++
.../camel/parser/model/CamelSimpleDetails.java | 98 +++
.../src/main/resources/META-INF/LICENSE.txt | 203 ++++++
.../src/main/resources/META-INF/NOTICE.txt | 11 +
11 files changed, 2196 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/pom.xml b/tooling/pom.xml
index b205a45..10e460d 100644
--- a/tooling/pom.xml
+++ b/tooling/pom.xml
@@ -39,6 +39,7 @@
<module>parent</module>
<module>spi-annotations</module>
<module>apt</module>
+ <module>route-parser</module>
<module>maven</module>
<module>archetypes</module>
<module>camel-manual</module>
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/route-parser/pom.xml b/tooling/route-parser/pom.xml
new file mode 100644
index 0000000..b6f51a0
--- /dev/null
+++ b/tooling/route-parser/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>tooling</artifactId>
+ <version>2.19.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>route-parser</artifactId>
+ <name>Camel :: Tooling :: Route Parser</name>
+ <description>Java and XML source code parser for Camel routes</description>
+
+ <properties>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.forge.roaster</groupId>
+ <artifactId>roaster-jdt</artifactId>
+ <version>${roaster-version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/AnonymousMethodSource.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/AnonymousMethodSource.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/AnonymousMethodSource.java
new file mode 100644
index 0000000..777d34e
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/AnonymousMethodSource.java
@@ -0,0 +1,426 @@
+/**
+ * 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;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+import org.jboss.forge.roaster.model.JavaType;
+import org.jboss.forge.roaster.model.Type;
+import org.jboss.forge.roaster.model.TypeVariable;
+import org.jboss.forge.roaster.model.Visibility;
+import org.jboss.forge.roaster.model.source.AnnotationSource;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.JavaDocSource;
+import org.jboss.forge.roaster.model.source.MethodSource;
+import org.jboss.forge.roaster.model.source.ParameterSource;
+import org.jboss.forge.roaster.model.source.TypeVariableSource;
+
+/**
+ * In use when we have discovered a RouteBuilder being as anonymous inner class
+ */
+public class AnonymousMethodSource implements MethodSource<JavaClassSource> {
+
+ // this implementation should only implement the needed logic to support the parser
+
+ private final JavaClassSource origin;
+ private final Object internal;
+
+ public AnonymousMethodSource(JavaClassSource origin, Object internal) {
+ this.origin = origin;
+ this.internal = internal;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setDefault(boolean b) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setSynchronized(boolean b) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setNative(boolean b) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setReturnType(Class<?> aClass) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setReturnType(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setReturnType(JavaType<?> javaType) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setReturnTypeVoid() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setBody(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setConstructor(boolean b) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setParameters(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> addThrows(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> addThrows(Class<? extends Exception> aClass) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeThrows(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeThrows(Class<? extends Exception> aClass) {
+ return null;
+ }
+
+ @Override
+ public boolean isSynchronized() {
+ return false;
+ }
+
+ @Override
+ public boolean isNative() {
+ return false;
+ }
+
+ @Override
+ public String getBody() {
+ return null;
+ }
+
+ @Override
+ public boolean isConstructor() {
+ return false;
+ }
+
+ @Override
+ public Type<JavaClassSource> getReturnType() {
+ return null;
+ }
+
+ @Override
+ public boolean isReturnTypeVoid() {
+ return false;
+ }
+
+ @Override
+ public List<ParameterSource<JavaClassSource>> getParameters() {
+ return null;
+ }
+
+ @Override
+ public String toSignature() {
+ return null;
+ }
+
+ @Override
+ public List<String> getThrownExceptions() {
+ return null;
+ }
+
+ @Override
+ public boolean isDefault() {
+ return false;
+ }
+
+ @Override
+ public ParameterSource<JavaClassSource> addParameter(Class<?> aClass, String s) {
+ return null;
+ }
+
+ @Override
+ public ParameterSource<JavaClassSource> addParameter(String s, String s1) {
+ return null;
+ }
+
+ @Override
+ public ParameterSource<JavaClassSource> addParameter(JavaType<?> javaType, String s) {
+ return null;
+ }
+
+ @Override
+ public void removeAllAnnotations() {
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeParameter(ParameterSource<JavaClassSource> parameterSource) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeParameter(Class<?> aClass, String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeParameter(String s, String s1) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeParameter(JavaType<?> javaType, String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setAbstract(boolean b) {
+ return null;
+ }
+
+ @Override
+ public List<AnnotationSource<JavaClassSource>> getAnnotations() {
+ return null;
+ }
+
+ @Override
+ public boolean hasAnnotation(Class<? extends Annotation> aClass) {
+ return false;
+ }
+
+ @Override
+ public boolean hasAnnotation(String s) {
+ return false;
+ }
+
+ @Override
+ public AnnotationSource<JavaClassSource> getAnnotation(Class<? extends Annotation> aClass) {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource<JavaClassSource> getAnnotation(String s) {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource<JavaClassSource> addAnnotation() {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource<JavaClassSource> addAnnotation(Class<? extends Annotation> aClass) {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource<JavaClassSource> addAnnotation(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeAnnotation(org.jboss.forge.roaster.model.Annotation<JavaClassSource> annotation) {
+ return null;
+ }
+
+ @Override
+ public List<TypeVariableSource<JavaClassSource>> getTypeVariables() {
+ return null;
+ }
+
+ @Override
+ public TypeVariableSource<JavaClassSource> getTypeVariable(String s) {
+ return null;
+ }
+
+ @Override
+ public TypeVariableSource<JavaClassSource> addTypeVariable() {
+ return null;
+ }
+
+ @Override
+ public TypeVariableSource<JavaClassSource> addTypeVariable(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeTypeVariable(String s) {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeTypeVariable(TypeVariable<?> typeVariable) {
+ return null;
+ }
+
+ @Override
+ public boolean hasJavaDoc() {
+ return false;
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return false;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setFinal(boolean b) {
+ return null;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public Object getInternal() {
+ return internal;
+ }
+
+ @Override
+ public JavaDocSource<MethodSource<JavaClassSource>> getJavaDoc() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> removeJavaDoc() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setName(String s) {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public JavaClassSource getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setStatic(boolean b) {
+ return null;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setPackagePrivate() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setPublic() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setPrivate() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setProtected() {
+ return null;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setVisibility(Visibility visibility) {
+ return null;
+ }
+
+ @Override
+ public boolean isPackagePrivate() {
+ return false;
+ }
+
+ @Override
+ public boolean isPublic() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrivate() {
+ return false;
+ }
+
+ @Override
+ public boolean isProtected() {
+ return false;
+ }
+
+ @Override
+ public Visibility getVisibility() {
+ return null;
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return 0;
+ }
+
+ @Override
+ public int getStartPosition() {
+ return 0;
+ }
+
+ @Override
+ public int getEndPosition() {
+ return 0;
+ }
+
+ @Override
+ public int getLineNumber() {
+ return 0;
+ }
+
+ @Override
+ public boolean hasTypeVariable(String arg0) {
+ return false;
+ }
+
+ @Override
+ public MethodSource<JavaClassSource> setReturnType(Type<?> arg0) {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/CamelJavaParserHelper.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/CamelJavaParserHelper.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/CamelJavaParserHelper.java
new file mode 100644
index 0000000..71d442d
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/CamelJavaParserHelper.java
@@ -0,0 +1,630 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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.ClassInstanceCreation;
+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.ReturnStatement;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SimpleName;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SimpleType;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Statement;
+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.VariableDeclaration;
+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.AnnotationSource;
+import org.jboss.forge.roaster.model.source.FieldSource;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.MethodSource;
+import org.jboss.forge.roaster.model.util.Strings;
+
+/**
+ * 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}.
+ */
+public class CamelJavaParserHelper {
+
+ public static MethodSource<JavaClassSource> findConfigureMethod(JavaClassSource clazz) {
+ MethodSource<JavaClassSource> method = clazz.getMethod("configure");
+ // must be public void configure()
+ if (method != null && method.isPublic() && method.getParameters().isEmpty() && method.getReturnType().isType("void")) {
+ return method;
+ }
+
+ // maybe the route builder is from unit testing with camel-test as an anonymous inner class
+ // there is a bit of code to dig out this using the eclipse jdt api
+ method = findCreateRouteBuilderMethod(clazz);
+ if (method != null) {
+ return findConfigureMethodInCreateRouteBuilder(clazz, method);
+ }
+
+ return null;
+ }
+
+ public static List<MethodSource<JavaClassSource>> findInlinedConfigureMethods(JavaClassSource clazz) {
+ List<MethodSource<JavaClassSource>> answer = new ArrayList<>();
+
+ List<MethodSource<JavaClassSource>> methods = clazz.getMethods();
+ if (methods != null) {
+ for (MethodSource<JavaClassSource> method : methods) {
+ if (method.isPublic()
+ && (method.getParameters() == null || method.getParameters().isEmpty())
+ && (method.getReturnType() == null || method.getReturnType().isType("void"))) {
+ // maybe the method contains an inlined createRouteBuilder usually from an unit test method
+ MethodSource<JavaClassSource> builder = findConfigureMethodInCreateRouteBuilder(clazz, method);
+ if (builder != null) {
+ answer.add(builder);
+ }
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ private static MethodSource<JavaClassSource> findCreateRouteBuilderMethod(JavaClassSource clazz) {
+ MethodSource method = clazz.getMethod("createRouteBuilder");
+ if (method != null && (method.isPublic() || method.isProtected()) && method.getParameters().isEmpty()) {
+ return method;
+ }
+ return null;
+ }
+
+ private static MethodSource<JavaClassSource> findConfigureMethodInCreateRouteBuilder(JavaClassSource clazz, MethodSource<JavaClassSource> method) {
+ // find configure inside the code
+ MethodDeclaration md = (MethodDeclaration) method.getInternal();
+ Block block = md.getBody();
+ if (block != null) {
+ List statements = block.statements();
+ for (int i = 0; i < statements.size(); i++) {
+ Statement stmt = (Statement) statements.get(i);
+ Expression exp = null;
+ if (stmt instanceof ReturnStatement) {
+ ReturnStatement rs = (ReturnStatement) stmt;
+ exp = rs.getExpression();
+ } else if (stmt instanceof ExpressionStatement) {
+ ExpressionStatement es = (ExpressionStatement) stmt;
+ exp = es.getExpression();
+ if (exp instanceof MethodInvocation) {
+ MethodInvocation mi = (MethodInvocation) exp;
+ for (Object arg : mi.arguments()) {
+ if (arg instanceof ClassInstanceCreation) {
+ exp = (Expression) arg;
+ break;
+ }
+ }
+ }
+ }
+ if (exp != null && exp instanceof ClassInstanceCreation) {
+ ClassInstanceCreation cic = (ClassInstanceCreation) exp;
+ boolean isRouteBuilder = false;
+ if (cic.getType() instanceof SimpleType) {
+ SimpleType st = (SimpleType) cic.getType();
+ isRouteBuilder = "RouteBuilder".equals(st.getName().toString());
+ }
+ if (isRouteBuilder && cic.getAnonymousClassDeclaration() != null) {
+ List body = cic.getAnonymousClassDeclaration().bodyDeclarations();
+ for (int j = 0; j < body.size(); j++) {
+ Object line = body.get(j);
+ if (line instanceof MethodDeclaration) {
+ MethodDeclaration amd = (MethodDeclaration) line;
+ if ("configure".equals(amd.getName().toString())) {
+ return new AnonymousMethodSource(clazz, amd);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public static List<ParserResult> parseCamelConsumerUris(MethodSource<JavaClassSource> method, boolean strings, boolean fields) {
+ return doParseCamelUris(method, true, false, strings, fields);
+ }
+
+ public static List<ParserResult> parseCamelProducerUris(MethodSource<JavaClassSource> method, boolean strings, boolean fields) {
+ return doParseCamelUris(method, false, true, strings, fields);
+ }
+
+ private static List<ParserResult> doParseCamelUris(MethodSource<JavaClassSource> method, boolean consumers, boolean producers,
+ boolean strings, boolean fields) {
+
+ List<ParserResult> answer = new ArrayList<ParserResult>();
+
+ 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();
+
+ List<ParserResult> uris = new ArrayList<ParserResult>();
+ parseExpression(method.getOrigin(), block, exp, uris, consumers, producers, strings, fields);
+ if (!uris.isEmpty()) {
+ // reverse the order as we will grab them from last->first
+ Collections.reverse(uris);
+ answer.addAll(uris);
+ }
+ }
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ private static void parseExpression(JavaClassSource clazz, Block block, Expression exp, List<ParserResult> uris,
+ boolean consumers, boolean producers, boolean strings, boolean fields) {
+ if (exp == null) {
+ return;
+ }
+ if (exp instanceof MethodInvocation) {
+ MethodInvocation mi = (MethodInvocation) exp;
+ doParseCamelUris(clazz, block, mi, uris, consumers, producers, strings, fields);
+ // if the method was called on another method, then recursive
+ exp = mi.getExpression();
+ parseExpression(clazz, block, exp, uris, consumers, producers, strings, fields);
+ }
+ }
+
+ private static void doParseCamelUris(JavaClassSource clazz, Block block, MethodInvocation mi, List<ParserResult> uris,
+ boolean consumers, boolean producers, boolean strings, boolean fields) {
+ String name = mi.getName().getIdentifier();
+
+ if (consumers) {
+ if ("from".equals(name)) {
+ List args = mi.arguments();
+ if (args != null) {
+ for (Object arg : args) {
+ if (isValidArgument(name, arg)) {
+ extractEndpointUriFromArgument(name, clazz, block, uris, arg, strings, fields);
+ }
+ }
+ }
+ }
+ if ("fromF".equals(name)) {
+ List args = mi.arguments();
+ // the first argument is where the uri is
+ if (args != null && args.size() >= 1) {
+ Object arg = args.get(0);
+ if (isValidArgument(name, arg)) {
+ extractEndpointUriFromArgument(name, clazz, block, uris, arg, strings, fields);
+ }
+ }
+ }
+ if ("pollEnrich".equals(name)) {
+ List args = mi.arguments();
+ // the first argument is where the uri is
+ if (args != null && args.size() >= 1) {
+ Object arg = args.get(0);
+ if (isValidArgument(name, arg)) {
+ extractEndpointUriFromArgument(name, clazz, block, uris, arg, strings, fields);
+ }
+ }
+ }
+ }
+
+ if (producers) {
+ if ("to".equals(name) || "toD".equals(name)) {
+ List args = mi.arguments();
+ if (args != null) {
+ for (Object arg : args) {
+ // skip if the arg is a boolean, ExchangePattern or Iterateable, type
+ if (isValidArgument(name, arg)) {
+ extractEndpointUriFromArgument(name, clazz, block, uris, arg, strings, fields);
+ }
+ }
+ }
+ }
+ if ("toF".equals(name)) {
+ List args = mi.arguments();
+ // the first argument is where the uri is
+ if (args != null && args.size() >= 1) {
+ Object arg = args.get(0);
+ if (isValidArgument(name, arg)) {
+ extractEndpointUriFromArgument(name, clazz, block, uris, arg, strings, fields);
+ }
+ }
+ }
+ if ("enrich".equals(name) || "wireTap".equals(name)) {
+ List args = mi.arguments();
+ // the first argument is where the uri is
+ if (args != null && args.size() >= 1) {
+ Object arg = args.get(0);
+ if (isValidArgument(name, arg)) {
+ extractEndpointUriFromArgument(name, clazz, block, uris, arg, strings, fields);
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean isValidArgument(String node, Object arg) {
+ // skip boolean argument, as toD can accept a boolean value
+ if (arg instanceof BooleanLiteral) {
+ return false;
+ }
+ // skip ExchangePattern argument
+ if (arg instanceof QualifiedName) {
+ QualifiedName qn = (QualifiedName) arg;
+ String name = qn.getFullyQualifiedName();
+ if (name.startsWith("ExchangePattern")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void extractEndpointUriFromArgument(String node, JavaClassSource clazz, Block block, List<ParserResult> uris, Object arg, boolean strings, boolean fields) {
+ if (strings) {
+ String uri = getLiteralValue(clazz, block, (Expression) arg);
+ if (!Strings.isBlank(uri)) {
+ int position = ((Expression) arg).getStartPosition();
+
+ // if the node is fromF or toF, then replace all %s with {{%s}} as we cannot parse that value
+ if ("fromF".equals(node) || "toF".equals(node)) {
+ uri = uri.replaceAll("\\%s", "\\{\\{\\%s\\}\\}");
+ }
+
+ uris.add(new ParserResult(node, position, uri));
+ return;
+ }
+ }
+ if (fields && arg instanceof SimpleName) {
+ FieldSource field = getField(clazz, block, (SimpleName) arg);
+ if (field != null) {
+ // find the endpoint uri from the annotation
+ AnnotationSource annotation = field.getAnnotation("org.apache.camel.cdi.Uri");
+ if (annotation == null) {
+ annotation = field.getAnnotation("org.apache.camel.EndpointInject");
+ }
+ if (annotation != null) {
+ Expression exp = (Expression) annotation.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;
+ }
+ }
+ }
+ String uri = CamelJavaParserHelper.getLiteralValue(clazz, block, exp);
+ if (!Strings.isBlank(uri)) {
+ int position = ((SimpleName) arg).getStartPosition();
+ uris.add(new ParserResult(node, position, uri));
+ }
+ } else {
+ // the field may be initialized using variables, so we need to evaluate those expressions
+ Object fi = field.getInternal();
+ if (fi instanceof VariableDeclaration) {
+ Expression exp = ((VariableDeclaration) fi).getInitializer();
+ String uri = CamelJavaParserHelper.getLiteralValue(clazz, block, exp);
+ if (!Strings.isBlank(uri)) {
+ // we want the position of the field, and not in the route
+ int position = ((VariableDeclaration) fi).getStartPosition();
+ uris.add(new ParserResult(node, position, uri));
+ }
+ }
+ }
+ }
+ }
+
+ // cannot parse it so add a failure
+ uris.add(new ParserResult(node, -1, arg.toString(), false));
+ }
+
+ public static List<ParserResult> parseCamelSimpleExpressions(MethodSource<JavaClassSource> method) {
+ List<ParserResult> answer = new ArrayList<ParserResult>();
+
+ MethodDeclaration md = (MethodDeclaration) method.getInternal();
+ Block block = md.getBody();
+ if (block != null) {
+ for (Object statement : block.statements()) {
+ // must be a method call expression
+ if (statement instanceof ExpressionStatement) {
+ ExpressionStatement es = (ExpressionStatement) statement;
+ Expression exp = es.getExpression();
+
+ List<ParserResult> expressions = new ArrayList<ParserResult>();
+ parseExpression(null, method.getOrigin(), block, exp, expressions);
+ if (!expressions.isEmpty()) {
+ // reverse the order as we will grab them from last->first
+ Collections.reverse(expressions);
+ answer.addAll(expressions);
+ }
+ }
+ }
+ }
+
+ return answer;
+ }
+
+ private static void parseExpression(String node, JavaClassSource clazz, Block block, Expression exp, List<ParserResult> expressions) {
+ if (exp == null) {
+ return;
+ }
+ if (exp instanceof MethodInvocation) {
+ MethodInvocation mi = (MethodInvocation) exp;
+ doParseCamelSimple(node, clazz, block, mi, expressions);
+ // if the method was called on another method, then recursive
+ exp = mi.getExpression();
+ parseExpression(node, clazz, block, exp, expressions);
+ }
+ }
+
+ private static void doParseCamelSimple(String node, JavaClassSource clazz, Block block, MethodInvocation mi, List<ParserResult> expressions) {
+ String name = mi.getName().getIdentifier();
+
+ if ("simple".equals(name)) {
+ List args = mi.arguments();
+ // the first argument is a string parameter for the simple expression
+ if (args != null && args.size() >= 1) {
+ // it is a String type
+ Object arg = args.get(0);
+ String simple = getLiteralValue(clazz, block, (Expression) arg);
+ if (!Strings.isBlank(simple)) {
+ int position = ((Expression) arg).getStartPosition();
+ expressions.add(new ParserResult(node, position, simple));
+ }
+ }
+ }
+
+ // simple maybe be passed in as an argument
+ List args = mi.arguments();
+ if (args != null) {
+ for (Object arg : args) {
+ if (arg instanceof MethodInvocation) {
+ MethodInvocation ami = (MethodInvocation) arg;
+ doParseCamelSimple(node, clazz, block, ami, expressions);
+ }
+ }
+ }
+ }
+
+ @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;
+ }
+
+ 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;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/ParserResult.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/ParserResult.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/ParserResult.java
new file mode 100644
index 0000000..445147d
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/ParserResult.java
@@ -0,0 +1,68 @@
+/**
+ * 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;
+
+public class ParserResult {
+
+ private final String node;
+ private boolean parsed;
+ private int position;
+ private String element;
+
+ public ParserResult(String node, int position, String element) {
+ this(node, position, element, true);
+ }
+
+ public ParserResult(String node, int position, String element, boolean parsed) {
+ this.node = node;
+ this.position = position;
+ this.element = element;
+ this.parsed = parsed;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ public String getElement() {
+ return element;
+ }
+
+ public void setElement(String element) {
+ this.element = element;
+ }
+
+ public boolean isParsed() {
+ return parsed;
+ }
+
+ public void setParsed(boolean parsed) {
+ this.parsed = parsed;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public String toString() {
+ return element;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
new file mode 100644
index 0000000..2327ae1
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
@@ -0,0 +1,281 @@
+/**
+ * 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.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.parser.model.CamelEndpointDetails;
+import org.apache.camel.parser.model.CamelSimpleDetails;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MemberValuePair;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+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;
+import org.jboss.forge.roaster.model.util.Strings;
+
+/**
+ * A Camel RouteBuilder parser that parses Camel Java routes source code.
+ * <p/>
+ * This implementation is higher level details, and uses the lower level parser {@link CamelJavaParserHelper}.
+ */
+public class RouteBuilderParser {
+
+ public static void parseRouteBuilderEndpoints(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+ List<CamelEndpointDetails> endpoints) {
+ parseRouteBuilderEndpoints(clazz, baseDir, fullyQualifiedFileName, endpoints, null, false);
+ }
+
+ public static void parseRouteBuilderEndpoints(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+ List<CamelEndpointDetails> endpoints, List<String> unparsable, boolean includeInlinedRouteBuilders) {
+
+ // look for fields which are not used in the route
+ for (FieldSource<JavaClassSource> field : clazz.getFields()) {
+
+ // is the field annotated with a Camel endpoint
+ String uri = null;
+ Expression exp = 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) {
+ 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;
+ }
+ }
+ }
+ uri = CamelJavaParserHelper.getLiteralValue(clazz, null, exp);
+ }
+ }
+
+ // we only want to add fields which are not used in the route
+ if (!Strings.isBlank(uri) && findEndpointByUri(endpoints, uri) == null) {
+
+ // we only want the relative dir name from the
+ String fileName = fullyQualifiedFileName;
+ if (fileName.startsWith(baseDir)) {
+ fileName = fileName.substring(baseDir.length() + 1);
+ }
+ String id = field.getName();
+
+ CamelEndpointDetails detail = new CamelEndpointDetails();
+ detail.setFileName(fileName);
+ detail.setClassName(clazz.getQualifiedName());
+ detail.setEndpointInstance(id);
+ detail.setEndpointUri(uri);
+ detail.setEndpointComponentName(endpointComponentName(uri));
+
+ // favor the position of the expression which had the actual uri
+ Object internal = exp != null ? exp : field.getInternal();
+
+ // find position of field/expression
+ if (internal instanceof ASTNode) {
+ int pos = ((ASTNode) internal).getStartPosition();
+ int line = findLineNumber(fullyQualifiedFileName, pos);
+ if (line > -1) {
+ detail.setLineNumber("" + line);
+ }
+ }
+ // we do not know if this field is used as consumer or producer only, but we try
+ // to find out by scanning the route in the configure method below
+ endpoints.add(detail);
+ }
+ }
+
+ // find all the configure methods
+ 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);
+ }
+ }
+
+ // look if any of these fields are used in the route only as consumer or producer, as then we can
+ // determine this to ensure when we edit the endpoint we should only the options accordingly
+ for (MethodSource<JavaClassSource> configureMethod : methods) {
+ // consumers only
+ List<ParserResult> uris = CamelJavaParserHelper.parseCamelConsumerUris(configureMethod, true, true);
+ for (ParserResult result : uris) {
+ if (!result.isParsed()) {
+ if (unparsable != null) {
+ unparsable.add(result.getElement());
+ }
+ } else {
+ CamelEndpointDetails detail = findEndpointByUri(endpoints, result.getElement());
+ if (detail != null) {
+ // its a consumer only
+ detail.setConsumerOnly(true);
+ } else {
+ String fileName = fullyQualifiedFileName;
+ if (fileName.startsWith(baseDir)) {
+ fileName = fileName.substring(baseDir.length() + 1);
+ }
+
+ detail = new CamelEndpointDetails();
+ detail.setFileName(fileName);
+ detail.setClassName(clazz.getQualifiedName());
+ detail.setMethodName(configureMethod.getName());
+ detail.setEndpointInstance(null);
+ detail.setEndpointUri(result.getElement());
+ int line = findLineNumber(fullyQualifiedFileName, result.getPosition());
+ if (line > -1) {
+ detail.setLineNumber("" + line);
+ }
+ detail.setEndpointComponentName(endpointComponentName(result.getElement()));
+ detail.setConsumerOnly(true);
+ detail.setProducerOnly(false);
+ endpoints.add(detail);
+ }
+ }
+ }
+ // producer only
+ uris = CamelJavaParserHelper.parseCamelProducerUris(configureMethod, true, true);
+ for (ParserResult result : uris) {
+ if (!result.isParsed()) {
+ if (unparsable != null) {
+ unparsable.add(result.getElement());
+ }
+ } else {
+ CamelEndpointDetails detail = findEndpointByUri(endpoints, result.getElement());
+ if (detail != null) {
+ if (detail.isConsumerOnly()) {
+ // its both a consumer and producer
+ detail.setConsumerOnly(false);
+ detail.setProducerOnly(false);
+ } else {
+ // its a producer only
+ detail.setProducerOnly(true);
+ }
+ }
+ // the same endpoint uri may be used in multiple places in the same route
+ // so we should maybe add all of them
+ String fileName = fullyQualifiedFileName;
+ if (fileName.startsWith(baseDir)) {
+ fileName = fileName.substring(baseDir.length() + 1);
+ }
+
+ detail = new CamelEndpointDetails();
+ detail.setFileName(fileName);
+ detail.setClassName(clazz.getQualifiedName());
+ detail.setMethodName(configureMethod.getName());
+ detail.setEndpointInstance(null);
+ detail.setEndpointUri(result.getElement());
+ int line = findLineNumber(fullyQualifiedFileName, result.getPosition());
+ if (line > -1) {
+ detail.setLineNumber("" + line);
+ }
+ detail.setEndpointComponentName(endpointComponentName(result.getElement()));
+ detail.setConsumerOnly(false);
+ detail.setProducerOnly(true);
+ endpoints.add(detail);
+ }
+ }
+ }
+ }
+
+ private static CamelEndpointDetails findEndpointByUri(List<CamelEndpointDetails> endpoints, String uri) {
+ for (CamelEndpointDetails detail : endpoints) {
+ if (uri.equals(detail.getEndpointUri())) {
+ return detail;
+ }
+ }
+ return null;
+ }
+
+ public static void parseRouteBuilderSimpleExpressions(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+ List<CamelSimpleDetails> simpleExpressions) {
+
+ MethodSource<JavaClassSource> method = CamelJavaParserHelper.findConfigureMethod(clazz);
+ if (method != null) {
+ List<ParserResult> expressions = CamelJavaParserHelper.parseCamelSimpleExpressions(method);
+ for (ParserResult result : expressions) {
+ if (result.isParsed()) {
+ String fileName = fullyQualifiedFileName;
+ if (fileName.startsWith(baseDir)) {
+ fileName = fileName.substring(baseDir.length() + 1);
+ }
+
+ CamelSimpleDetails details = new CamelSimpleDetails();
+ details.setFileName(fileName);
+ details.setClassName(clazz.getQualifiedName());
+ details.setMethodName("configure");
+ int line = findLineNumber(fullyQualifiedFileName, result.getPosition());
+ if (line > -1) {
+ details.setLineNumber("" + line);
+ }
+ details.setSimple(result.getElement());
+
+ simpleExpressions.add(details);
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+ private static String endpointComponentName(String uri) {
+ if (uri != null) {
+ int idx = uri.indexOf(":");
+ if (idx > 0) {
+ return uri.substring(0, idx);
+ }
+ }
+ return null;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/StatementFieldSource.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/StatementFieldSource.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/StatementFieldSource.java
new file mode 100644
index 0000000..1daa85d
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/StatementFieldSource.java
@@ -0,0 +1,278 @@
+/**
+ * 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;
+
+import java.util.List;
+
+import org.jboss.forge.roaster.model.Annotation;
+import org.jboss.forge.roaster.model.JavaType;
+import org.jboss.forge.roaster.model.Type;
+import org.jboss.forge.roaster.model.Visibility;
+import org.jboss.forge.roaster.model.impl.TypeImpl;
+import org.jboss.forge.roaster.model.source.AnnotationSource;
+import org.jboss.forge.roaster.model.source.FieldSource;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.JavaDocSource;
+
+public class StatementFieldSource implements FieldSource {
+
+ // this implementation should only implement the needed logic to support the parser
+
+ private final JavaClassSource origin;
+ private final Object internal;
+ private final Type type;
+
+ public StatementFieldSource(JavaClassSource origin, Object internal, Object typeInternal) {
+ this.origin = origin;
+ this.internal = internal;
+ this.type = new TypeImpl(origin, typeInternal);
+ }
+
+ @Override
+ public FieldSource setType(Class clazz) {
+ return null;
+ }
+
+ @Override
+ public FieldSource setType(String type) {
+ return null;
+ }
+
+ @Override
+ public FieldSource setLiteralInitializer(String value) {
+ return null;
+ }
+
+ @Override
+ public FieldSource setStringInitializer(String value) {
+ return null;
+ }
+
+ @Override
+ public FieldSource setTransient(boolean value) {
+ return null;
+ }
+
+ @Override
+ public FieldSource setVolatile(boolean value) {
+ return null;
+ }
+
+ @Override
+ public FieldSource setType(JavaType entity) {
+ return null;
+ }
+
+ @Override
+ public List<AnnotationSource> getAnnotations() {
+ return null;
+ }
+
+ @Override
+ public boolean hasAnnotation(String type) {
+ return false;
+ }
+
+ @Override
+ public boolean hasAnnotation(Class type) {
+ return false;
+ }
+
+ @Override
+ public AnnotationSource getAnnotation(String type) {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource addAnnotation() {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource addAnnotation(String className) {
+ return null;
+ }
+
+ @Override
+ public void removeAllAnnotations() {
+ }
+
+ @Override
+ public Object removeAnnotation(Annotation annotation) {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource addAnnotation(Class type) {
+ return null;
+ }
+
+ @Override
+ public AnnotationSource getAnnotation(Class type) {
+ return null;
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public String getStringInitializer() {
+ return null;
+ }
+
+ @Override
+ public String getLiteralInitializer() {
+ return null;
+ }
+
+ @Override
+ public boolean isTransient() {
+ return false;
+ }
+
+ @Override
+ public boolean isVolatile() {
+ return false;
+ }
+
+ @Override
+ public Object setFinal(boolean finl) {
+ return null;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public Object getInternal() {
+ return internal;
+ }
+
+ @Override
+ public JavaDocSource getJavaDoc() {
+ return null;
+ }
+
+ @Override
+ public boolean hasJavaDoc() {
+ return false;
+ }
+
+ @Override
+ public Object removeJavaDoc() {
+ return null;
+ }
+
+ @Override
+ public Object setName(String name) {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public Object getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Object setStatic(boolean value) {
+ return null;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public Object setPackagePrivate() {
+ return null;
+ }
+
+ @Override
+ public Object setPublic() {
+ return null;
+ }
+
+ @Override
+ public Object setPrivate() {
+ return null;
+ }
+
+ @Override
+ public Object setProtected() {
+ return null;
+ }
+
+ @Override
+ public Object setVisibility(Visibility scope) {
+ return null;
+ }
+
+ @Override
+ public boolean isPackagePrivate() {
+ return false;
+ }
+
+ @Override
+ public boolean isPublic() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrivate() {
+ return false;
+ }
+
+ @Override
+ public boolean isProtected() {
+ return false;
+ }
+
+ @Override
+ public Visibility getVisibility() {
+ return null;
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return 0;
+ }
+
+ @Override
+ public int getStartPosition() {
+ return 0;
+ }
+
+ @Override
+ public int getEndPosition() {
+ return 0;
+ }
+
+ @Override
+ public int getLineNumber() {
+ return 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java
new file mode 100644
index 0000000..2255c1d
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelEndpointDetails.java
@@ -0,0 +1,157 @@
+/**
+ * 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.model;
+
+public class CamelEndpointDetails {
+
+ private String fileName;
+ private String lineNumber;
+ private String lineNumberEnd;
+ private String className;
+ private String methodName;
+ private String endpointComponentName;
+ private String endpointInstance;
+ private String endpointUri;
+ private boolean consumerOnly;
+ private boolean producerOnly;
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getLineNumber() {
+ return lineNumber;
+ }
+
+ public void setLineNumber(String lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public String getLineNumberEnd() {
+ return lineNumberEnd;
+ }
+
+ public void setLineNumberEnd(String lineNumberEnd) {
+ this.lineNumberEnd = lineNumberEnd;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public String getEndpointComponentName() {
+ return endpointComponentName;
+ }
+
+ public void setEndpointComponentName(String endpointComponentName) {
+ this.endpointComponentName = endpointComponentName;
+ }
+
+ public String getEndpointInstance() {
+ return endpointInstance;
+ }
+
+ public void setEndpointInstance(String endpointInstance) {
+ this.endpointInstance = endpointInstance;
+ }
+
+ public String getEndpointUri() {
+ return endpointUri;
+ }
+
+ public void setEndpointUri(String endpointUri) {
+ this.endpointUri = endpointUri;
+ }
+
+ public boolean isConsumerOnly() {
+ return consumerOnly;
+ }
+
+ public void setConsumerOnly(boolean consumerOnly) {
+ this.consumerOnly = consumerOnly;
+ }
+
+ public boolean isProducerOnly() {
+ return producerOnly;
+ }
+
+ public void setProducerOnly(boolean producerOnly) {
+ this.producerOnly = producerOnly;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CamelEndpointDetails that = (CamelEndpointDetails) o;
+
+ if (!fileName.equals(that.fileName)) return false;
+ if (lineNumber != null ? !lineNumber.equals(that.lineNumber) : that.lineNumber != null) return false;
+ if (lineNumberEnd != null ? !lineNumberEnd.equals(that.lineNumberEnd) : that.lineNumberEnd != null) return false;
+ if (!className.equals(that.className)) return false;
+ if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) return false;
+ if (endpointInstance != null ? !endpointInstance.equals(that.endpointInstance) : that.endpointInstance != null)
+ return false;
+ return endpointUri.equals(that.endpointUri);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = fileName.hashCode();
+ result = 31 * result + (lineNumber != null ? lineNumber.hashCode() : 0);
+ result = 31 * result + (lineNumberEnd != null ? lineNumberEnd.hashCode() : 0);
+ result = 31 * result + className.hashCode();
+ result = 31 * result + (methodName != null ? methodName.hashCode() : 0);
+ result = 31 * result + (endpointInstance != null ? endpointInstance.hashCode() : 0);
+ result = 31 * result + endpointUri.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "CamelEndpointDetails[" +
+ "fileName='" + fileName + '\'' +
+ ", lineNumber='" + lineNumber + '\'' +
+ ", lineNumberEnd='" + lineNumberEnd + '\'' +
+ ", className='" + className + '\'' +
+ ", methodName='" + methodName + '\'' +
+ ", endpointComponentName='" + endpointComponentName + '\'' +
+ ", endpointInstance='" + endpointInstance + '\'' +
+ ", endpointUri='" + endpointUri + '\'' +
+ ", consumerOnly=" + consumerOnly +
+ ", producerOnly=" + producerOnly +
+ ']';
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/42e4d973/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelSimpleDetails.java
----------------------------------------------------------------------
diff --git a/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelSimpleDetails.java b/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelSimpleDetails.java
new file mode 100644
index 0000000..058111d
--- /dev/null
+++ b/tooling/route-parser/src/main/java/org/apache/camel/parser/model/CamelSimpleDetails.java
@@ -0,0 +1,98 @@
+/**
+ * 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.model;
+
+public class CamelSimpleDetails {
+
+ private String fileName;
+ private String lineNumber;
+ private String lineNumberEnd;
+ private String className;
+ private String methodName;
+ private String simple;
+ private boolean predicate;
+ private boolean expression;
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getLineNumber() {
+ return lineNumber;
+ }
+
+ public void setLineNumber(String lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public String getLineNumberEnd() {
+ return lineNumberEnd;
+ }
+
+ public void setLineNumberEnd(String lineNumberEnd) {
+ this.lineNumberEnd = lineNumberEnd;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public String getSimple() {
+ return simple;
+ }
+
+ public void setSimple(String simple) {
+ this.simple = simple;
+ }
+
+ public boolean isPredicate() {
+ return predicate;
+ }
+
+ public void setPredicate(boolean predicate) {
+ this.predicate = predicate;
+ }
+
+ public boolean isExpression() {
+ return expression;
+ }
+
+ public void setExpression(boolean expression) {
+ this.expression = expression;
+ }
+
+ @Override
+ public String toString() {
+ return simple;
+ }
+}