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:55 UTC

[22/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/88ba9063
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/88ba9063
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/88ba9063

Branch: refs/heads/master
Commit: 88ba9063c77e5e8625471c67d6c7cecaac58d4d6
Parents: 96e946e
Author: Claus Ibsen <da...@apache.org>
Authored: Mon Dec 5 13:28:49 2016 +0100
Committer: Claus Ibsen <da...@apache.org>
Committed: Mon Dec 5 14:04:48 2016 +0100

----------------------------------------------------------------------
 apache-camel/pom.xml                            |   4 +
 .../src/main/descriptors/common-bin.xml         |   1 +
 parent/pom.xml                                  |   5 +
 tooling/camel-route-parser/pom.xml              |  99 +++
 .../org/apache/camel/parser/ParserResult.java   |  72 +++
 .../apache/camel/parser/RouteBuilderParser.java | 310 +++++++++
 .../org/apache/camel/parser/XmlRouteParser.java | 169 +++++
 .../parser/helper/CamelJavaParserHelper.java    | 638 +++++++++++++++++++
 .../camel/parser/helper/CamelXmlHelper.java     | 268 ++++++++
 .../parser/helper/XmlLineNumberParser.java      | 197 ++++++
 .../parser/model/CamelEndpointDetails.java      | 175 +++++
 .../model/CamelSimpleExpressionDetails.java     | 101 +++
 .../parser/roaster/AnonymousMethodSource.java   | 426 +++++++++++++
 .../parser/roaster/StatementFieldSource.java    | 278 ++++++++
 .../src/main/resources/META-INF/LICENSE.txt     | 203 ++++++
 .../src/main/resources/META-INF/NOTICE.txt      |  11 +
 .../parser/java/MyBasePortRouteBuilder.java     |  27 +
 .../parser/java/MyCdiConcatRouteBuilder.java    |  49 ++
 .../camel/parser/java/MyCdiRouteBuilder.java    |  46 ++
 .../parser/java/MyConcatFieldRouteBuilder.java  |  31 +
 .../java/MyFieldMethodCallRouteBuilder.java     |  33 +
 .../camel/parser/java/MyFieldRouteBuilder.java  |  31 +
 .../parser/java/MyLocalAddRouteBuilderTest.java |  52 ++
 .../parser/java/MyMethodCallRouteBuilder.java   |  33 +
 .../apache/camel/parser/java/MyNettyTest.java   |  52 ++
 .../parser/java/MyNewLineConstRouteBuilder.java |  34 +
 .../parser/java/MyNewLineRouteBuilder.java      |  31 +
 .../camel/parser/java/MyRouteBuilder.java       |  31 +
 .../camel/parser/java/MyRouteEmptyUriTest.java  |  42 ++
 .../apache/camel/parser/java/MyRouteTest.java   |  40 ++
 .../camel/parser/java/MySimpleRouteBuilder.java |  34 +
 .../camel/parser/java/MySimpleToDRoute.java     |  34 +
 .../camel/parser/java/MySimpleToFRoute.java     |  28 +
 ...asterCdiConcatRouteBuilderConfigureTest.java |  55 ++
 .../RoasterCdiRouteBuilderConfigureTest.java    |  55 ++
 ...terConcatFieldRouteBuilderConfigureTest.java |  54 ++
 .../parser/java/RoasterEndpointInjectTest.java  |  73 +++
 .../RoasterFieldRouteBuilderConfigureTest.java  |  55 ++
 ...sterMethodCallRouteBuilderConfigureTest.java |  55 ++
 ...ieldMethodCallRouteBuilderConfigureTest.java |  57 ++
 .../java/RoasterMyLocalAddRouteBuilderTest.java |  57 ++
 .../camel/parser/java/RoasterMyNettyTest.java   |  57 ++
 ...erNewLineConstRouteBuilderConfigureTest.java |  55 ++
 ...RoasterNewLineRouteBuilderConfigureTest.java |  55 ++
 ...RoasterRouteBuilderCamelTestSupportTest.java |  54 ++
 .../java/RoasterRouteBuilderConfigureTest.java  |  56 ++
 .../java/RoasterRouteBuilderEmptyUriTest.java   |  55 ++
 .../parser/java/RoasterSimpleProcessorTest.java |  64 ++
 .../RoasterSimpleRouteBuilderConfigureTest.java |  71 +++
 .../camel/parser/java/RoasterSimpleToDTest.java |  73 +++
 .../camel/parser/java/RoasterSimpleToFTest.java |  67 ++
 .../parser/java/RoasterSplitTokenizeTest.java   |  75 +++
 .../camel/parser/java/SimpleProcessorTest.java  |  45 ++
 .../camel/parser/java/SplitTokenizeTest.java    | 126 ++++
 .../parser/xml/FindElementInRoutesTest.java     |  45 ++
 .../parser/xml/XmlOnExceptionRouteTest.java     |  55 ++
 .../apache/camel/parser/xml/XmlRouteTest.java   |  51 ++
 .../src/test/resources/log4j2.properties        |  30 +
 .../camel/parser/xml/mycamel-onexception.xml    |  33 +
 .../org/apache/camel/parser/xml/mycamel.xml     |  26 +
 .../org/apache/camel/parser/xml/myroutes.xml    |  19 +
 tooling/pom.xml                                 |   2 +-
 tooling/route-parser/pom.xml                    |  99 ---
 .../org/apache/camel/parser/ParserResult.java   |  72 ---
 .../apache/camel/parser/RouteBuilderParser.java | 310 ---------
 .../org/apache/camel/parser/XmlRouteParser.java | 169 -----
 .../parser/helper/CamelJavaParserHelper.java    | 638 -------------------
 .../camel/parser/helper/CamelXmlHelper.java     | 268 --------
 .../parser/helper/XmlLineNumberParser.java      | 197 ------
 .../parser/model/CamelEndpointDetails.java      | 175 -----
 .../model/CamelSimpleExpressionDetails.java     | 101 ---
 .../parser/roaster/AnonymousMethodSource.java   | 426 -------------
 .../parser/roaster/StatementFieldSource.java    | 278 --------
 .../src/main/resources/META-INF/LICENSE.txt     | 203 ------
 .../src/main/resources/META-INF/NOTICE.txt      |  11 -
 .../parser/java/MyBasePortRouteBuilder.java     |  27 -
 .../parser/java/MyCdiConcatRouteBuilder.java    |  49 --
 .../camel/parser/java/MyCdiRouteBuilder.java    |  46 --
 .../parser/java/MyConcatFieldRouteBuilder.java  |  31 -
 .../java/MyFieldMethodCallRouteBuilder.java     |  33 -
 .../camel/parser/java/MyFieldRouteBuilder.java  |  31 -
 .../parser/java/MyLocalAddRouteBuilderTest.java |  52 --
 .../parser/java/MyMethodCallRouteBuilder.java   |  33 -
 .../apache/camel/parser/java/MyNettyTest.java   |  52 --
 .../parser/java/MyNewLineConstRouteBuilder.java |  34 -
 .../parser/java/MyNewLineRouteBuilder.java      |  31 -
 .../camel/parser/java/MyRouteBuilder.java       |  31 -
 .../camel/parser/java/MyRouteEmptyUriTest.java  |  42 --
 .../apache/camel/parser/java/MyRouteTest.java   |  40 --
 .../camel/parser/java/MySimpleRouteBuilder.java |  34 -
 .../camel/parser/java/MySimpleToDRoute.java     |  34 -
 .../camel/parser/java/MySimpleToFRoute.java     |  28 -
 ...asterCdiConcatRouteBuilderConfigureTest.java |  55 --
 .../RoasterCdiRouteBuilderConfigureTest.java    |  55 --
 ...terConcatFieldRouteBuilderConfigureTest.java |  54 --
 .../parser/java/RoasterEndpointInjectTest.java  |  73 ---
 .../RoasterFieldRouteBuilderConfigureTest.java  |  55 --
 ...sterMethodCallRouteBuilderConfigureTest.java |  55 --
 ...ieldMethodCallRouteBuilderConfigureTest.java |  57 --
 .../java/RoasterMyLocalAddRouteBuilderTest.java |  57 --
 .../camel/parser/java/RoasterMyNettyTest.java   |  57 --
 ...erNewLineConstRouteBuilderConfigureTest.java |  55 --
 ...RoasterNewLineRouteBuilderConfigureTest.java |  55 --
 ...RoasterRouteBuilderCamelTestSupportTest.java |  54 --
 .../java/RoasterRouteBuilderConfigureTest.java  |  56 --
 .../java/RoasterRouteBuilderEmptyUriTest.java   |  55 --
 .../parser/java/RoasterSimpleProcessorTest.java |  64 --
 .../RoasterSimpleRouteBuilderConfigureTest.java |  71 ---
 .../camel/parser/java/RoasterSimpleToDTest.java |  73 ---
 .../camel/parser/java/RoasterSimpleToFTest.java |  67 --
 .../parser/java/RoasterSplitTokenizeTest.java   |  75 ---
 .../camel/parser/java/SimpleProcessorTest.java  |  45 --
 .../camel/parser/java/SplitTokenizeTest.java    | 126 ----
 .../parser/xml/FindElementInRoutesTest.java     |  45 --
 .../parser/xml/XmlOnExceptionRouteTest.java     |  55 --
 .../apache/camel/parser/xml/XmlRouteTest.java   |  51 --
 .../src/test/resources/log4j2.properties        |  30 -
 .../camel/parser/xml/mycamel-onexception.xml    |  33 -
 .../org/apache/camel/parser/xml/mycamel.xml     |  26 -
 .../org/apache/camel/parser/xml/myroutes.xml    |  19 -
 120 files changed, 5159 insertions(+), 5149 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/apache-camel/pom.xml
----------------------------------------------------------------------
diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml
index 839ba04..66d9c53 100644
--- a/apache-camel/pom.xml
+++ b/apache-camel/pom.xml
@@ -2166,6 +2166,10 @@
       <groupId>org.apache.camel</groupId>
       <artifactId>camel-commands-spring-boot</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-route-parser</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.apache.geronimo.specs</groupId>

http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/apache-camel/src/main/descriptors/common-bin.xml
----------------------------------------------------------------------
diff --git a/apache-camel/src/main/descriptors/common-bin.xml b/apache-camel/src/main/descriptors/common-bin.xml
index 3717e70..d5146b4 100644
--- a/apache-camel/src/main/descriptors/common-bin.xml
+++ b/apache-camel/src/main/descriptors/common-bin.xml
@@ -280,6 +280,7 @@
         <include>org.apache.camel:camel-commands-jolokia</include>
         <include>org.apache.camel:camel-commands-spring-boot</include>
         <include>org.apache.camel.karaf:camel-karaf-commands</include>
+        <include>org.apache.camel:camel-route-parser</include>
       </includes>
     </dependencySet>
 

http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/parent/pom.xml
----------------------------------------------------------------------
diff --git a/parent/pom.xml b/parent/pom.xml
index 0ea685f..aba4928 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -1990,6 +1990,11 @@
         <artifactId>camel-karaf-commands</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-route-parser</artifactId>
+        <version>${project.version}</version>
+      </dependency>
 
       <!-- camel misc -->
       <dependency>

http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/tooling/camel-route-parser/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/pom.xml b/tooling/camel-route-parser/pom.xml
new file mode 100644
index 0000000..f042fca
--- /dev/null
+++ b/tooling/camel-route-parser/pom.xml
@@ -0,0 +1,99 @@
+<?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>camel-route-parser</artifactId>
+  <name>Camel :: Tooling :: Camel Route Parser</name>
+  <description>Java and XML source code parser for Camel routes</description>
+
+  <dependencies>
+
+    <!-- the roaster-jdt is set as provided as different runtimes may have it out of the box -->
+    <dependency>
+      <groupId>org.jboss.forge.roaster</groupId>
+      <artifactId>roaster-jdt</artifactId>
+      <version>${roaster-version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- only test scopes for camel as we have no runtime dependency on camel -->
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-spring</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-cdi</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test-cdi</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/ParserResult.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/ParserResult.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/ParserResult.java
new file mode 100644
index 0000000..4ef2fc4
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/ParserResult.java
@@ -0,0 +1,72 @@
+/**
+ * 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;
+
+/**
+ * Result of parsing Camel RouteBuilder or XML routes from the source code.
+ */
+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;
+    }
+
+    /**
+     * Character based position in the source code (not line based).
+     */
+    public int getPosition() {
+        return position;
+    }
+
+    /**
+     * The element such as a Camel endpoint uri
+     */
+    public String getElement() {
+        return element;
+    }
+
+    /**
+     * Whether the element was successfully parsed. If the parser cannot parse
+     * the element for whatever reason this will return <tt>false</tt>.
+     */
+    public boolean isParsed() {
+        return parsed;
+    }
+
+    /**
+     * The node which is typically a Camel EIP name such as <tt>to</tt>, <tt>wireTap</tt> etc.
+     */
+    public String getNode() {
+        return node;
+    }
+
+    public String toString() {
+        return element;
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/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
new file mode 100644
index 0000000..36fc443
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RouteBuilderParser.java
@@ -0,0 +1,310 @@
+/**
+ * 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.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.parser.helper.CamelJavaParserHelper;
+import org.apache.camel.parser.model.CamelEndpointDetails;
+import org.apache.camel.parser.model.CamelSimpleExpressionDetails;
+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 final class RouteBuilderParser {
+
+    private RouteBuilderParser() {
+    }
+
+    /**
+     * Parses the java source class to discover Camel endpoints.
+     *
+     * @param clazz                   the java source class
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @param endpoints               list to add discovered and parsed endpoints
+     */
+    public static void parseRouteBuilderEndpoints(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+                                                  List<CamelEndpointDetails> endpoints) {
+        parseRouteBuilderEndpoints(clazz, baseDir, fullyQualifiedFileName, endpoints, null, false);
+    }
+
+    /**
+     * Parses the java source class to discover Camel endpoints.
+     *
+     * @param clazz                        the java source class
+     * @param baseDir                      the base of the source code
+     * @param fullyQualifiedFileName       the fully qualified source code file name
+     * @param endpoints                    list to add discovered and parsed endpoints
+     * @param unparsable                   list of unparsable nodes
+     * @param includeInlinedRouteBuilders  whether to include inlined route builders in the parsing
+     */
+    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);
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses the java source class to discover Camel simple expressions.
+     *
+     * @param clazz                   the java source class
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @param simpleExpressions       list to add discovered and parsed simple expressions
+     */
+    public static void parseRouteBuilderSimpleExpressions(JavaClassSource clazz, String baseDir, String fullyQualifiedFileName,
+                                                          List<CamelSimpleExpressionDetails> 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);
+                    }
+
+                    CamelSimpleExpressionDetails details = new CamelSimpleExpressionDetails();
+                    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 CamelEndpointDetails findEndpointByUri(List<CamelEndpointDetails> endpoints, String uri) {
+        for (CamelEndpointDetails detail : endpoints) {
+            if (uri.equals(detail.getEndpointUri())) {
+                return detail;
+            }
+        }
+        return null;
+    }
+
+    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/88ba9063/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
new file mode 100644
index 0000000..2cca9df
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRouteParser.java
@@ -0,0 +1,169 @@
+/**
+ * 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.io.InputStream;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.apache.camel.parser.helper.CamelJavaParserHelper;
+import org.apache.camel.parser.helper.CamelXmlHelper;
+import org.apache.camel.parser.helper.XmlLineNumberParser;
+
+import org.apache.camel.parser.model.CamelEndpointDetails;
+import org.apache.camel.parser.model.CamelSimpleExpressionDetails;
+import org.jboss.forge.roaster.model.util.Strings;
+
+import static org.apache.camel.parser.helper.CamelXmlHelper.getSafeAttribute;
+
+/**
+ * A Camel XML parser that parses Camel XML routes source code.
+ * <p/>
+ * This implementation is higher level details, and uses the lower level parser {@link CamelJavaParserHelper}.
+ */
+public final class XmlRouteParser {
+
+    private XmlRouteParser() {
+    }
+
+    /**
+     * Parses the XML source to discover Camel endpoints.
+     *
+     * @param xml                     the xml file as input stream
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @param endpoints               list to add discovered and parsed endpoints
+     */
+    public static void parseXmlRouteEndpoints(InputStream xml, String baseDir, String fullyQualifiedFileName,
+                                              List<CamelEndpointDetails> endpoints) throws Exception {
+
+        // find all the endpoints (currently only <endpoint> and within <route>)
+        // try parse it as dom
+        Document dom = null;
+        try {
+            dom = XmlLineNumberParser.parseXml(xml);
+        } catch (Exception e) {
+            // ignore as the xml file may not be valid at this point
+        }
+        if (dom != null) {
+            List<Node> nodes = CamelXmlHelper.findAllEndpoints(dom);
+            for (Node node : nodes) {
+                String uri = getSafeAttribute(node, "uri");
+                if (uri != null) {
+                    // trim and remove whitespace noise
+                    uri = trimEndpointUri(uri);
+                }
+                if (!Strings.isBlank(uri)) {
+                    String id = getSafeAttribute(node, "id");
+                    String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
+                    String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+
+                    // we only want the relative dir name from the resource directory, eg META-INF/spring/foo.xml
+                    String fileName = fullyQualifiedFileName;
+                    if (fileName.startsWith(baseDir)) {
+                        fileName = fileName.substring(baseDir.length() + 1);
+                    }
+
+                    boolean consumerOnly = false;
+                    boolean producerOnly = false;
+                    String nodeName = node.getNodeName();
+                    if ("from".equals(nodeName) || "pollEnrich".equals(nodeName)) {
+                        consumerOnly = true;
+                    } else if ("to".equals(nodeName) || "enrich".equals(nodeName) || "wireTap".equals(nodeName)) {
+                        producerOnly = true;
+                    }
+
+                    CamelEndpointDetails detail = new CamelEndpointDetails();
+                    detail.setFileName(fileName);
+                    detail.setLineNumber(lineNumber);
+                    detail.setLineNumberEnd(lineNumberEnd);
+                    detail.setEndpointInstance(id);
+                    detail.setEndpointUri(uri);
+                    detail.setEndpointComponentName(endpointComponentName(uri));
+                    detail.setConsumerOnly(consumerOnly);
+                    detail.setProducerOnly(producerOnly);
+                    endpoints.add(detail);
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses the XML source to discover Camel endpoints.
+     *
+     * @param xml                     the xml file as input stream
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @param simpleExpressions       list to add discovered and parsed simple expressions
+     */
+    public static void parseXmlRouteSimpleExpressions(InputStream xml, String baseDir, String fullyQualifiedFileName,
+                                                      List<CamelSimpleExpressionDetails> simpleExpressions) throws Exception {
+
+        // find all the simple expressions
+        // try parse it as dom
+        Document dom = null;
+        try {
+            dom = XmlLineNumberParser.parseXml(xml);
+        } catch (Exception e) {
+            // ignore as the xml file may not be valid at this point
+        }
+        if (dom != null) {
+            List<Node> nodes = CamelXmlHelper.findAllSimpleExpressions(dom);
+            for (Node node : nodes) {
+                String simple = node.getTextContent();
+                String lineNumber = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER);
+                String lineNumberEnd = (String) node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+
+                // we only want the relative dir name from the resource directory, eg META-INF/spring/foo.xml
+                String fileName = fullyQualifiedFileName;
+                if (fileName.startsWith(baseDir)) {
+                    fileName = fileName.substring(baseDir.length() + 1);
+                }
+
+                CamelSimpleExpressionDetails detail = new CamelSimpleExpressionDetails();
+                detail.setFileName(fileName);
+                detail.setLineNumber(lineNumber);
+                detail.setLineNumberEnd(lineNumberEnd);
+                detail.setSimple(simple);
+                simpleExpressions.add(detail);
+            }
+        }
+    }
+
+    private static String endpointComponentName(String uri) {
+        if (uri != null) {
+            int idx = uri.indexOf(":");
+            if (idx > 0) {
+                return uri.substring(0, idx);
+            }
+        }
+        return null;
+    }
+
+
+    private static String trimEndpointUri(String uri) {
+        uri = uri.trim();
+        // if the uri is using new-lines then remove whitespace noise before & and ? separator
+        uri = uri.replaceAll("(\\s+)(\\&)", "$2");
+        uri = uri.replaceAll("(\\&)(\\s+)", "$1");
+        uri = uri.replaceAll("(\\?)(\\s+)", "$1");
+        return uri;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/88ba9063/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaParserHelper.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaParserHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaParserHelper.java
new file mode 100644
index 0000000..6df709a
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaParserHelper.java
@@ -0,0 +1,638 @@
+/**
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.camel.parser.ParserResult;
+import org.apache.camel.parser.RouteBuilderParser;
+import org.apache.camel.parser.roaster.AnonymousMethodSource;
+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.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 final class CamelJavaParserHelper {
+
+    private CamelJavaParserHelper() {
+        // utility class
+    }
+
+    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/88ba9063/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlHelper.java
----------------------------------------------------------------------
diff --git a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlHelper.java b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlHelper.java
new file mode 100644
index 0000000..53d4783
--- /dev/null
+++ b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlHelper.java
@@ -0,0 +1,268 @@
+/**
+ * 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.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.jboss.forge.roaster.model.util.Strings;
+
+/**
+ * Various XML helper methods used for parsing XML routes.
+ */
+public final class CamelXmlHelper {
+
+    private CamelXmlHelper() {
+        // utility class
+    }
+
+    public static String getSafeAttribute(Node node, String key) {
+        if (node != null) {
+            Node attr = node.getAttributes().getNamedItem(key);
+            if (attr != null) {
+                return attr.getNodeValue();
+            }
+        }
+        return null;
+    }
+
+    public static List<Node> findAllEndpoints(Document dom) {
+        List<Node> nodes = new ArrayList<>();
+
+        NodeList list = dom.getElementsByTagName("endpoint");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            if ("endpoint".equals(child.getNodeName())) {
+                // it may not be a camel namespace, so skip those
+                String ns = child.getNamespaceURI();
+                if (ns == null) {
+                    NamedNodeMap attrs = child.getAttributes();
+                    if (attrs != null) {
+                        Node node = attrs.getNamedItem("xmlns");
+                        if (node != null) {
+                            ns = node.getNodeValue();
+                        }
+                    }
+                }
+                // assume no namespace its for camel
+                if (ns == null || ns.contains("camel")) {
+                    nodes.add(child);
+                }
+            }
+        }
+
+        list = dom.getElementsByTagName("onException");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            findAllUrisRecursive(child, nodes);
+        }
+        list = dom.getElementsByTagName("onCompletion");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            findAllUrisRecursive(child, nodes);
+        }
+        list = dom.getElementsByTagName("intercept");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            findAllUrisRecursive(child, nodes);
+        }
+        list = dom.getElementsByTagName("interceptFrom");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            findAllUrisRecursive(child, nodes);
+        }
+        list = dom.getElementsByTagName("interceptSendToEndpoint");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            findAllUrisRecursive(child, nodes);
+        }
+        list = dom.getElementsByTagName("rest");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            if ("route".equals(child.getNodeName()) || "to".equals(child.getNodeName())) {
+                findAllUrisRecursive(child, nodes);
+            }
+        }
+        list = dom.getElementsByTagName("route");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            if ("route".equals(child.getNodeName())) {
+                findAllUrisRecursive(child, nodes);
+            }
+        }
+
+        return nodes;
+    }
+
+    private static void findAllUrisRecursive(Node node, List<Node> nodes) {
+        // okay its a route so grab all uri attributes we can find
+        String url = getSafeAttribute(node, "uri");
+        if (url != null) {
+            nodes.add(node);
+        }
+
+        NodeList children = node.getChildNodes();
+        if (children != null) {
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child = children.item(i);
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    findAllUrisRecursive(child, nodes);
+                }
+            }
+        }
+    }
+
+    public static List<Node> findAllSimpleExpressions(Document dom) {
+        List<Node> nodes = new ArrayList<>();
+
+        NodeList list = dom.getElementsByTagName("route");
+        for (int i = 0; i < list.getLength(); i++) {
+            Node child = list.item(i);
+            if ("route".equals(child.getNodeName())) {
+                findAllSimpleExpressionsRecursive(child, nodes);
+            }
+        }
+
+        return nodes;
+    }
+
+    private static void findAllSimpleExpressionsRecursive(Node node, List<Node> nodes) {
+        // okay its a route so grab if its <simple>
+        if ("simple".equals(node.getNodeName())) {
+            nodes.add(node);
+        }
+
+        NodeList children = node.getChildNodes();
+        if (children != null) {
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child = children.item(i);
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    findAllSimpleExpressionsRecursive(child, nodes);
+                }
+            }
+        }
+    }
+
+    public static Element getSelectedCamelElementNode(String key, InputStream resourceInputStream) throws Exception {
+        Document root = loadCamelXmlFileAsDom(resourceInputStream);
+        Element selectedElement = null;
+        if (root != null) {
+            Node selectedNode = findCamelNodeInDocument(root, key);
+            if (selectedNode instanceof Element) {
+                selectedElement = (Element) selectedNode;
+            }
+        }
+        return selectedElement;
+    }
+
+    private static Document loadCamelXmlFileAsDom(InputStream resourceInputStream) throws Exception {
+        // must enforce the namespace to be http://camel.apache.org/schema/spring which is what the camel-core JAXB model uses
+        Document root = XmlLineNumberParser.parseXml(resourceInputStream, "camelContext,routes,rests", "http://camel.apache.org/schema/spring");
+        return root;
+    }
+
+    private static Node findCamelNodeInDocument(Document root, String key) {
+        Node selectedNode = null;
+        if (root != null && !Strings.isBlank(key)) {
+            String[] paths = key.split("/");
+            NodeList camels = getCamelContextElements(root);
+            if (camels != null) {
+                Map<String, Integer> rootNodeCounts = new HashMap<>();
+                for (int i = 0, size = camels.getLength(); i < size; i++) {
+                    Node node = camels.item(i);
+                    boolean first = true;
+                    for (String path : paths) {
+                        if (first) {
+                            first = false;
+                            String actual = getIdOrIndex(node, rootNodeCounts);
+                            if (!equal(actual, path)) {
+                                node = null;
+                            }
+                        } else {
+                            node = findCamelNodeForPath(node, path);
+                        }
+                        if (node == null) {
+                            break;
+                        }
+                    }
+                    if (node != null) {
+                        return node;
+                    }
+                }
+            }
+        }
+        return selectedNode;
+    }
+
+    private static Node findCamelNodeForPath(Node node, String path) {
+        NodeList childNodes = node.getChildNodes();
+        if (childNodes != null) {
+            Map<String, Integer> nodeCounts = new HashMap<>();
+            for (int i = 0, size = childNodes.getLength(); i < size; i++) {
+                Node child = childNodes.item(i);
+                if (child instanceof Element) {
+                    String actual = getIdOrIndex(child, nodeCounts);
+                    if (equal(actual, path)) {
+                        return child;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String getIdOrIndex(Node node, Map<String, Integer> nodeCounts) {
+        String answer = null;
+        if (node instanceof Element) {
+            Element element = (Element) node;
+            String elementName = element.getTagName();
+            if ("routes".equals(elementName)) {
+                elementName = "camelContext";
+            }
+            Integer countObject = nodeCounts.get(elementName);
+            int count = countObject != null ? countObject.intValue() : 0;
+            nodeCounts.put(elementName, ++count);
+            answer = element.getAttribute("id");
+            if (Strings.isBlank(answer)) {
+                answer = "_" + elementName + count;
+            }
+        }
+        return answer;
+    }
+
+    private static NodeList getCamelContextElements(Document dom) {
+        NodeList camels = dom.getElementsByTagName("camelContext");
+        if (camels == null || camels.getLength() == 0) {
+            camels = dom.getElementsByTagName("routes");
+        }
+        return camels;
+    }
+
+    private static boolean equal(Object a, Object b) {
+        return a == b ? true : a != null && b != null && a.equals(b);
+    }
+
+}