This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/master by this push:
     new aff6c95  Rest dsl parser (#2529)
aff6c95 is described below

commit aff6c95ad4968f7394726583c5601f38a0ac8eda
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Sun Sep 23 11:57:15 2018 +0200

    Rest dsl parser (#2529)
    
    CAMEL-12824: camel-route-parser - Add parser for rest-dsl
---
 .../org/apache/camel/parser/RestDslParser.java     | 112 ++++
 .../org/apache/camel/parser/XmlRestDslParser.java  |  89 +++
 .../helper/CamelJavaRestDslParserHelper.java       | 678 +++++++++++++++++++++
 .../parser/helper/CamelJavaTreeParserHelper.java   |   6 +-
 .../parser/helper/CamelXmlRestDslParserHelper.java | 279 +++++++++
 .../parser/model/RestConfigurationDetails.java     | 371 +++++++++++
 .../camel/parser/model/RestServiceDetails.java     | 189 ++++++
 .../apache/camel/parser/model/RestVerbDetails.java | 197 ++++++
 .../camel/parser/java/MyRestDslRouteBuilder.java   |  51 ++
 .../camel/parser/java/RoasterJavaDslTest.java      |   2 +
 .../camel/parser/java/RoasterJavaRestDslTest.java  | 103 ++++
 .../apache/camel/parser/xml/XmlRestDslTest.java    | 111 ++++
 .../org/apache/camel/parser/xml/myrest.xml         |  51 ++
 13 files changed, 2238 insertions(+), 1 deletion(-)

diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java
new file mode 100644
index 0000000..6703b76
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/RestDslParser.java
@@ -0,0 +1,112 @@
+/**
+ * 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.apache.camel.parser.helper.CamelJavaParserHelper;
+import org.apache.camel.parser.helper.CamelJavaRestDslParserHelper;
+import org.apache.camel.parser.model.RestConfigurationDetails;
+import org.apache.camel.parser.model.RestServiceDetails;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.MethodSource;
+
+/**
+ * A Camel parser that parses Camel Java Rest DSL source code.
+ * <p/>
+ * This implementation is higher level details, and uses the lower level 
parser {@link CamelJavaRestDslParserHelper}.
+ */
+public final class RestDslParser {
+
+    private RestDslParser() {
+    }
+
+    /**
+     * Parses the java source class and build a rest configuration model of 
the discovered rest configurations in the java source class.
+     *
+     * @param clazz                   the java source class
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @return a list of rest configurations (often there is only one)
+     */
+    public static List<RestConfigurationDetails> 
parseRestConfiguration(JavaClassSource clazz, String baseDir, String 
fullyQualifiedFileName,
+                                                                        
boolean includeInlinedRouteBuilders) {
+
+        List<MethodSource<JavaClassSource>> methods = new ArrayList<>();
+        MethodSource<JavaClassSource> method = 
CamelJavaParserHelper.findConfigureMethod(clazz);
+        if (method != null) {
+            methods.add(method);
+        }
+        if (includeInlinedRouteBuilders) {
+            List<MethodSource<JavaClassSource>> inlinedMethods = 
CamelJavaParserHelper.findInlinedConfigureMethods(clazz);
+            if (!inlinedMethods.isEmpty()) {
+                methods.addAll(inlinedMethods);
+            }
+        }
+
+        CamelJavaRestDslParserHelper parser = new 
CamelJavaRestDslParserHelper();
+        List<RestConfigurationDetails> list = new ArrayList<>();
+        for (MethodSource<JavaClassSource> configureMethod : methods) {
+            // there may be multiple route builder configure methods
+            List<RestConfigurationDetails> details = 
parser.parseRestConfiguration(clazz, baseDir, fullyQualifiedFileName, 
configureMethod);
+            list.addAll(details);
+        }
+        // we end up parsing bottom->up so reverse list
+        Collections.reverse(list);
+
+        return list;
+    }
+
+    /**
+     * Parses the java source class and build a rest service model of the 
discovered rest services in the java source class.
+     *
+     * @param clazz                   the java source class
+     * @param baseDir                 the base of the source code
+     * @param fullyQualifiedFileName  the fully qualified source code file name
+     * @return a list of rest services
+     */
+    public static List<RestServiceDetails> parseRestService(JavaClassSource 
clazz, String baseDir, String fullyQualifiedFileName,
+                                                            boolean 
includeInlinedRouteBuilders) {
+
+        List<MethodSource<JavaClassSource>> methods = new ArrayList<>();
+        MethodSource<JavaClassSource> method = 
CamelJavaParserHelper.findConfigureMethod(clazz);
+        if (method != null) {
+            methods.add(method);
+        }
+        if (includeInlinedRouteBuilders) {
+            List<MethodSource<JavaClassSource>> inlinedMethods = 
CamelJavaParserHelper.findInlinedConfigureMethods(clazz);
+            if (!inlinedMethods.isEmpty()) {
+                methods.addAll(inlinedMethods);
+            }
+        }
+
+        CamelJavaRestDslParserHelper parser = new 
CamelJavaRestDslParserHelper();
+        List<RestServiceDetails> list = new ArrayList<>();
+        for (MethodSource<JavaClassSource> configureMethod : methods) {
+            // there may be multiple route builder configure methods
+            List<RestServiceDetails> details = parser.parseRestService(clazz, 
baseDir, fullyQualifiedFileName, configureMethod);
+            list.addAll(details);
+        }
+        // we end up parsing bottom->up so reverse list
+        Collections.reverse(list);
+
+        return list;
+    }
+
+}
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRestDslParser.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRestDslParser.java
new file mode 100644
index 0000000..e28a295
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/XmlRestDslParser.java
@@ -0,0 +1,89 @@
+/**
+ * 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.Collections;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.camel.parser.helper.CamelXmlRestDslParserHelper;
+import org.apache.camel.parser.helper.XmlLineNumberParser;
+import org.apache.camel.parser.model.RestConfigurationDetails;
+import org.apache.camel.parser.model.RestServiceDetails;
+
+/**
+ * A Camel XML parser that parses Camel XML Rest DSL source code.
+ * <p/>
+ * This implementation is higher level details, and uses the lower level 
parser {@link CamelXmlRestDslParserHelper}.
+ */
+public final class XmlRestDslParser {
+
+    private XmlRestDslParser() {
+    }
+
+    /**
+     * Parses the XML file and build a rest configuration model of the 
discovered rest configurations in the XML source file.
+     *
+     * @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
+     * @return a list of rest configurations (often there is only one)
+     */
+    public static List<RestConfigurationDetails> 
parseRestConfiguration(InputStream xml, String baseDir, String 
fullyQualifiedFileName) {
+        // 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) {
+            CamelXmlRestDslParserHelper parser = new 
CamelXmlRestDslParserHelper();
+            return parser.parseRestConfiguration(dom, baseDir, 
fullyQualifiedFileName);
+        }
+
+        return Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Parses the java source class and build a rest service model of the 
discovered rest services in the java source class.
+     *
+     * @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
+     * @return a list of rest services
+     */
+    public static List<RestServiceDetails> parseRestService(InputStream xml, 
String baseDir, String fullyQualifiedFileName) {
+
+        // 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) {
+            CamelXmlRestDslParserHelper parser = new 
CamelXmlRestDslParserHelper();
+            return parser.parseRestService(dom, baseDir, 
fullyQualifiedFileName);
+        }
+
+        return Collections.EMPTY_LIST;
+    }
+
+}
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java
new file mode 100644
index 0000000..8c3018f
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaRestDslParserHelper.java
@@ -0,0 +1,678 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.parser.helper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.camel.parser.model.RestConfigurationDetails;
+import org.apache.camel.parser.model.RestServiceDetails;
+import org.apache.camel.parser.model.RestVerbDetails;
+import org.apache.camel.parser.roaster.StatementFieldSource;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Block;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Expression;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ExpressionStatement;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.InfixExpression;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MemberValuePair;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodDeclaration;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.MethodInvocation;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.NumberLiteral;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.QualifiedName;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SimpleName;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.StringLiteral;
+import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Type;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import 
org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.jboss.forge.roaster.model.Annotation;
+import org.jboss.forge.roaster.model.source.FieldSource;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.jboss.forge.roaster.model.source.MethodSource;
+
+/**
+ * A Camel Java Rest DSL parser that only depends on the Roaster API.
+ * <p/>
+ * This implement is used for parsing the Camel routes and build a tree 
structure of the Rest DSL services.
+ */
+public final class CamelJavaRestDslParserHelper {
+
+    public List<RestConfigurationDetails> 
parseRestConfiguration(JavaClassSource clazz, String baseDir, String 
fullyQualifiedFileName,
+                                                                 
MethodSource<JavaClassSource> configureMethod) {
+
+        List<RestConfigurationDetails> answer = new ArrayList<>();
+
+        if (configureMethod != null) {
+            MethodDeclaration md = (MethodDeclaration) 
configureMethod.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();
+                        boolean valid = isRestConfiguration(exp);
+                        if (valid) {
+                            RestConfigurationDetails node = new 
RestConfigurationDetails();
+                            answer.add(node);
+
+                            // include source code details
+                            int pos = exp.getStartPosition();
+                            int line = findLineNumber(fullyQualifiedFileName, 
pos);
+                            if (line > -1) {
+                                node.setLineNumber("" + line);
+                            }
+                            pos = exp.getStartPosition() + exp.getLength();
+                            line = findLineNumber(fullyQualifiedFileName, pos);
+                            if (line > -1) {
+                                node.setLineNumberEnd("" + line);
+                            }
+                            node.setFileName(fullyQualifiedFileName);
+                            node.setClassName(clazz.getQualifiedName());
+                            node.setMethodName(configureMethod.getName());
+
+                            parseExpression(node, fullyQualifiedFileName, 
clazz, configureMethod, block, exp);
+                        }
+                    }
+                }
+            }
+        }
+
+        return answer;
+    }
+
+    public List<RestServiceDetails> parseRestService(JavaClassSource clazz, 
String baseDir, String fullyQualifiedFileName,
+                                                     
MethodSource<JavaClassSource> configureMethod) {
+
+        List<RestServiceDetails> answer = new ArrayList<>();
+
+        if (configureMethod != null) {
+            MethodDeclaration md = (MethodDeclaration) 
configureMethod.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();
+                        boolean valid = isRest(exp);
+                        if (valid) {
+                            RestServiceDetails node = new RestServiceDetails();
+                            answer.add(node);
+
+                            // include source code details
+                            int pos = exp.getStartPosition();
+                            int line = findLineNumber(fullyQualifiedFileName, 
pos);
+                            if (line > -1) {
+                                node.setLineNumber("" + line);
+                            }
+                            pos = exp.getStartPosition() + exp.getLength();
+                            line = findLineNumber(fullyQualifiedFileName, pos);
+                            if (line > -1) {
+                                node.setLineNumberEnd("" + line);
+                            }
+                            node.setFileName(fullyQualifiedFileName);
+                            node.setClassName(clazz.getQualifiedName());
+                            node.setMethodName(configureMethod.getName());
+
+                            parseExpression(node, null, 
fullyQualifiedFileName, clazz, configureMethod, block, exp);
+
+                            // flip order of verbs as we parse bottom-up
+                            if (node.getVerbs() != null) {
+                                Collections.reverse(node.getVerbs());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return answer;
+    }
+
+    private boolean isRestConfiguration(Expression exp) {
+        String rootMethodName = null;
+
+        // find out if this is from a Camel route (eg from, route etc.)
+        Expression sub = exp;
+        while (sub instanceof MethodInvocation) {
+            sub = ((MethodInvocation) sub).getExpression();
+            if (sub instanceof MethodInvocation) {
+                Expression parent = ((MethodInvocation) sub).getExpression();
+                if (parent == null) {
+                    break;
+                }
+            }
+        }
+        if (sub instanceof MethodInvocation) {
+            rootMethodName = ((MethodInvocation) 
sub).getName().getIdentifier();
+        } else if (sub instanceof SimpleName) {
+            rootMethodName = ((SimpleName) sub).getIdentifier();
+        }
+
+        // must be from rest configuration
+        return "restConfiguration".equals(rootMethodName);
+    }
+
+    private boolean isRest(Expression exp) {
+        String rootMethodName = null;
+
+        // find out if this is from a Camel route (eg from, route etc.)
+        Expression sub = exp;
+        while (sub instanceof MethodInvocation) {
+            sub = ((MethodInvocation) sub).getExpression();
+            if (sub instanceof MethodInvocation) {
+                Expression parent = ((MethodInvocation) sub).getExpression();
+                if (parent == null) {
+                    break;
+                }
+            }
+        }
+        if (sub instanceof MethodInvocation) {
+            rootMethodName = ((MethodInvocation) 
sub).getName().getIdentifier();
+        } else if (sub instanceof SimpleName) {
+            rootMethodName = ((SimpleName) sub).getIdentifier();
+        }
+
+        // must be from rest
+        return "rest".equals(rootMethodName);
+    }
+
+    private void parseExpression(RestConfigurationDetails node, String 
fullyQualifiedFileName,
+                                 JavaClassSource clazz, 
MethodSource<JavaClassSource> configureMethod, Block block,
+                                 Expression exp) {
+        if (exp == null) {
+            return;
+        }
+        if (exp instanceof MethodInvocation) {
+            MethodInvocation mi = (MethodInvocation) exp;
+            doParseRestConfiguration(node, fullyQualifiedFileName, clazz, 
configureMethod, block, mi);
+            // if the method was called on another method, then recursive
+            exp = mi.getExpression();
+            parseExpression(node, fullyQualifiedFileName, clazz, 
configureMethod, block, exp);
+        }
+    }
+
+    private void parseExpression(RestServiceDetails node, RestVerbDetails 
verb, String fullyQualifiedFileName,
+                                 JavaClassSource clazz, 
MethodSource<JavaClassSource> configureMethod, Block block,
+                                 Expression exp) {
+        if (exp == null) {
+            // this rest service is not complete, if there is any details on 
verb then they are actually general
+            // for this rest service and we should pass the details to it
+            if (verb != null) {
+                node.setConsumes(verb.getConsumes());
+                node.setProduces(verb.getProduces());
+                
node.setSkipBindingOnErrorCode(verb.getSkipBindingOnErrorCode());
+                
node.setClientRequestValidation(verb.getClientRequestValidation());
+                node.setApiDocs(verb.getApiDocs());
+                node.setDescription(verb.getDescription());
+            }
+            return;
+        }
+        if (exp instanceof MethodInvocation) {
+            MethodInvocation mi = (MethodInvocation) exp;
+            verb = doParseRestService(node, verb, fullyQualifiedFileName, 
clazz, configureMethod, block, mi);
+            // if the method was called on another method, then recursive
+            exp = mi.getExpression();
+            parseExpression(node, verb, fullyQualifiedFileName, clazz, 
configureMethod, block, exp);
+        }
+    }
+
+    private void doParseRestConfiguration(RestConfigurationDetails node, 
String fullyQualifiedFileName,
+                                          JavaClassSource clazz, 
MethodSource<JavaClassSource> configureMethod, Block block,
+                                          MethodInvocation mi) {
+
+        // end line number is the first node in the method chain we parse
+        if (node.getLineNumberEnd() == null) {
+            int pos = mi.getStartPosition() + mi.getLength();
+            int line = findLineNumber(fullyQualifiedFileName, pos);
+            if (line > -1) {
+                node.setLineNumberEnd("" + line);
+            }
+        }
+
+        String name = mi.getName().getIdentifier();
+        if ("component".equals(name)) {
+            node.setComponent(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("apiComponent".equals(name)) {
+            node.setApiComponent(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("producerComponent".equals(name)) {
+            node.setProducerComponent(extractValueFromFirstArgument(clazz, 
block, mi));
+        } else if ("scheme".equals(name)) {
+            node.setScheme(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("host".equals(name)) {
+            node.setHost(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("apiHost".equals(name)) {
+            node.setApiHost(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("port".equals(name)) {
+            node.setPort(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("producerApiDoc".equals(name)) {
+            node.setProducerApiDoc(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("contextPath".equals(name)) {
+            node.setContextPath(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("apiContextPath".equals(name)) {
+            node.setApiContextPath(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("apiContextRouteId".equals(name)) {
+            node.setApiContextRouteId(extractValueFromFirstArgument(clazz, 
block, mi));
+        } else if ("apiContextIdPattern".equals(name)) {
+            node.setApiContextIdPattern(extractValueFromFirstArgument(clazz, 
block, mi));
+        } else if ("apiContextListening".equals(name)) {
+            node.setApiContextListening(extractValueFromFirstArgument(clazz, 
block, mi));
+        } else if ("apiVendorExtension".equals(name)) {
+            node.setApiVendorExtension(extractValueFromFirstArgument(clazz, 
block, mi));
+        } else if ("hostNameResolver".equals(name)) {
+            node.setHostNameResolver(extractValueFromFirstArgument(clazz, 
block, mi));
+        } else if ("bindingMode".equals(name)) {
+            node.setBindingMode(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("skipBindingOnErrorCode".equals(name)) {
+            
node.setSkipBindingOnErrorCode(extractValueFromFirstArgument(clazz, block, mi));
+        } else if ("clientRequestValidation".equals(name)) {
+            
node.setClientRequestValidation(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("enableCORS".equals(name)) {
+            node.setEnableCORS(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("jsonDataFormat".equals(name)) {
+            node.setJsonDataFormat(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("xmlDataFormat".equals(name)) {
+            node.setXmlDataFormat(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else if ("componentProperty".equals(name)) {
+            String key = extractValueFromFirstArgument(clazz, block, mi);
+            String value = extractValueFromSecondArgument(clazz, block, mi);
+            node.addComponentProperty(key, value);
+        } else if ("endpointProperty".equals(name)) {
+            String key = extractValueFromFirstArgument(clazz, block, mi);
+            String value = extractValueFromSecondArgument(clazz, block, mi);
+            node.addEndpointProperty(key, value);
+        } else if ("consumerProperty".equals(name)) {
+            String key = extractValueFromFirstArgument(clazz, block, mi);
+            String value = extractValueFromSecondArgument(clazz, block, mi);
+            node.addConsumerProperty(key, value);
+        } else if ("dataFormatProperty".equals(name)) {
+            String key = extractValueFromFirstArgument(clazz, block, mi);
+            String value = extractValueFromSecondArgument(clazz, block, mi);
+            node.addDataFormatProperty(key, value);
+        } else if ("apiProperty".equals(name)) {
+            String key = extractValueFromFirstArgument(clazz, block, mi);
+            String value = extractValueFromSecondArgument(clazz, block, mi);
+            node.addApiProperty(key, value);
+        } else if ("corsHeaderProperty".equals(name)) {
+            String key = extractValueFromFirstArgument(clazz, block, mi);
+            String value = extractValueFromSecondArgument(clazz, block, mi);
+            node.addCorsHeader(key, value);
+        }
+    }
+
+    private RestVerbDetails doParseRestService(RestServiceDetails node, 
RestVerbDetails verb, String fullyQualifiedFileName,
+                                               JavaClassSource clazz, 
MethodSource<JavaClassSource> configureMethod, Block block,
+                                               MethodInvocation mi) {
+
+        // end line number is the first node in the method chain we parse
+        if (node.getLineNumberEnd() == null) {
+            int pos = mi.getStartPosition() + mi.getLength();
+            int line = findLineNumber(fullyQualifiedFileName, pos);
+            if (line > -1) {
+                node.setLineNumberEnd("" + line);
+            }
+        }
+
+        String name = mi.getName().getIdentifier();
+        if ("rest".equals(name)) {
+            node.setPath(extractValueFromFirstArgument(clazz, block, mi));
+        } else if (isParentMethod(mi, "rest")) {
+            verb = doParseRestVerb(node, verb, clazz, configureMethod, block, 
mi);
+        }
+        return verb;
+    }
+
+    private RestVerbDetails doParseRestVerb(RestServiceDetails node, 
RestVerbDetails verb,
+                                            JavaClassSource clazz, 
MethodSource<JavaClassSource> configureMethod, Block block,
+                                            MethodInvocation mi) {
+        if (verb == null) {
+            verb = new RestVerbDetails();
+        }
+
+        String name = mi.getName().getIdentifier();
+        if ("description".equals(name)) {
+            verb.setDescription(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else  if ("bindingMode".equals(name)) {
+            verb.setBindingMode(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else  if ("skipBindingOnErrorcode".equals(name)) {
+            
verb.setSkipBindingOnErrorCode(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("clientRequestValidation".equals(name)) {
+            
verb.setClientRequestValidation(extractValueFromFirstArgument(clazz, block, 
mi));
+        } else  if ("consumes".equals(name)) {
+            verb.setConsumes(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("produces".equals(name)) {
+            verb.setProduces(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("type".equals(name)) {
+            verb.setType(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("outType".equals(name)) {
+            verb.setOutType(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("apiDocs".equals(name)) {
+            verb.setApiDocs(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("to".equals(name)) {
+            verb.setTo(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("toD".equals(name)) {
+            verb.setToD(extractValueFromFirstArgument(clazz, block, mi));
+        } else  if ("tag".equals(name)) {
+            // tag is only available on the node
+            node.setTag(extractValueFromFirstArgument(clazz, block, mi));
+        }
+
+        if ("delete".equals(name)) {
+            node.addVerb(verb);
+            verb.setMethod("delete");
+            verb.setUri(extractValueFromFirstArgument(clazz, block, mi));
+            verb = null; // reset as this verb is not complete
+        } else if ("get".equals(name)) {
+            node.addVerb(verb);
+            verb.setMethod("get");
+            verb.setUri(extractValueFromFirstArgument(clazz, block, mi));
+            verb = null; // reset as this verb is not complete
+        } else if ("head".equals(name)) {
+            node.addVerb(verb);
+            verb.setMethod("head");
+            verb.setUri(extractValueFromFirstArgument(clazz, block, mi));
+            verb = null; // reset as this verb is not complete
+        } else if ("patch".equals(name)) {
+            node.addVerb(verb);
+            verb.setMethod("patch");
+            verb.setUri(extractValueFromFirstArgument(clazz, block, mi));
+            verb = null; // reset as this verb is not complete
+        } else if ("post".equals(name)) {
+            node.addVerb(verb);
+            verb.setMethod("post");
+            verb.setUri(extractValueFromFirstArgument(clazz, block, mi));
+            verb = null; // reset as this verb is not complete
+        } else if ("put".equals(name)) {
+            node.addVerb(verb);
+            verb.setMethod("put");
+            verb.setUri(extractValueFromFirstArgument(clazz, block, mi));
+            verb = null; // reset as this verb is not complete
+        }
+
+        return verb;
+    }
+
+    private static boolean isParentMethod(MethodInvocation mi, String 
parentName) {
+        String name = mi.getName().getIdentifier();
+        if (parentName.equals(name)) {
+            return true;
+        }
+
+        // find out if this is from a Camel route (eg from, route etc.)
+        Expression sub = mi;
+        while (sub instanceof MethodInvocation) {
+            sub = ((MethodInvocation) sub).getExpression();
+            if (sub instanceof MethodInvocation) {
+                name = ((MethodInvocation) sub).getName().getIdentifier();
+                if (parentName.equals(name)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private static String extractValueFromFirstArgument(JavaClassSource clazz, 
Block block, MethodInvocation mi) {
+        List args = mi.arguments();
+        if (args != null && args.size() > 0) {
+            Expression exp = (Expression) args.get(0);
+            return getLiteralValue(clazz, block, exp);
+        }
+        return null;
+    }
+
+    private static String extractValueFromSecondArgument(JavaClassSource 
clazz, Block block, MethodInvocation mi) {
+        List args = mi.arguments();
+        if (args != null && args.size() > 1) {
+            Expression exp = (Expression) args.get(1);
+            return getLiteralValue(clazz, block, exp);
+        }
+        return null;
+    }
+
+    @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, then its an enum where we should grab the 
simple name
+        if (expression instanceof QualifiedName) {
+            QualifiedName qn = (QualifiedName) expression;
+            return qn.getName().getIdentifier();
+        }
+
+        if (expression instanceof SimpleName) {
+            FieldSource<JavaClassSource> field = getField(clazz, block, 
(SimpleName) expression);
+            if (field != null) {
+                // is the field annotated with a Camel endpoint
+                if (field.getAnnotations() != null) {
+                    for (Annotation ann : field.getAnnotations()) {
+                        boolean valid = 
"org.apache.camel.EndpointInject".equals(ann.getQualifiedName()) || 
"org.apache.camel.cdi.Uri".equals(ann.getQualifiedName());
+                        if (valid) {
+                            Expression exp = (Expression) ann.getInternal();
+                            if (exp instanceof SingleMemberAnnotation) {
+                                exp = ((SingleMemberAnnotation) 
exp).getValue();
+                            } else if (exp instanceof NormalAnnotation) {
+                                List values = ((NormalAnnotation) 
exp).values();
+                                for (Object value : values) {
+                                    MemberValuePair pair = (MemberValuePair) 
value;
+                                    if 
("uri".equals(pair.getName().toString())) {
+                                        exp = pair.getValue();
+                                        break;
+                                    }
+                                }
+                            }
+                            if (exp != null) {
+                                return getLiteralValue(clazz, block, exp);
+                            }
+                        }
+                    }
+                }
+                // is the field an org.apache.camel.Endpoint type?
+                if ("Endpoint".equals(field.getType().getSimpleName())) {
+                    // then grab the uri from the first argument
+                    VariableDeclarationFragment vdf = 
(VariableDeclarationFragment) field.getInternal();
+                    expression = vdf.getInitializer();
+                    if (expression instanceof MethodInvocation) {
+                        MethodInvocation mi = (MethodInvocation) expression;
+                        List args = mi.arguments();
+                        if (args != null && args.size() > 0) {
+                            // the first argument has the endpoint uri
+                            expression = (Expression) args.get(0);
+                            return getLiteralValue(clazz, block, expression);
+                        }
+                    }
+                } else {
+                    // no annotations so try its initializer
+                    VariableDeclarationFragment vdf = 
(VariableDeclarationFragment) field.getInternal();
+                    expression = vdf.getInitializer();
+                    if (expression == null) {
+                        // its a field which has no initializer, then add a 
dummy value assuming the field will be initialized at runtime
+                        return "{{" + field.getName() + "}}";
+                    } else {
+                        return getLiteralValue(clazz, block, expression);
+                    }
+                }
+            } else {
+                // we could not find the field in this class/method, so its 
maybe from some other super class, so insert a dummy value
+                final String fieldName = ((SimpleName) 
expression).getIdentifier();
+                return "{{" + fieldName + "}}";
+            }
+        } else if (expression instanceof InfixExpression) {
+            String answer = null;
+            // is it a string that is concat together?
+            InfixExpression ie = (InfixExpression) expression;
+            if (InfixExpression.Operator.PLUS.equals(ie.getOperator())) {
+
+                String val1 = getLiteralValue(clazz, block, 
ie.getLeftOperand());
+                String val2 = getLiteralValue(clazz, block, 
ie.getRightOperand());
+
+                // if numeric then we plus the values, otherwise we string 
concat
+                boolean numeric = isNumericOperator(clazz, block, 
ie.getLeftOperand()) && isNumericOperator(clazz, block, ie.getRightOperand());
+                if (numeric) {
+                    Long num1 = val1 != null ? Long.valueOf(val1) : 0;
+                    Long num2 = val2 != null ? Long.valueOf(val2) : 0;
+                    answer = "" + (num1 + num2);
+                } else {
+                    answer = (val1 != null ? val1 : "") + (val2 != null ? val2 
: "");
+                }
+
+                if (!answer.isEmpty()) {
+                    // include extended when we concat on 2 or more lines
+                    List extended = ie.extendedOperands();
+                    if (extended != null) {
+                        for (Object ext : extended) {
+                            String val3 = getLiteralValue(clazz, block, 
(Expression) ext);
+                            if (numeric) {
+                                Long num3 = val3 != null ? Long.valueOf(val3) 
: 0;
+                                Long num = Long.valueOf(answer);
+                                answer = "" + (num + num3);
+                            } else {
+                                answer += val3 != null ? val3 : "";
+                            }
+                        }
+                    }
+                }
+            }
+            return answer;
+        }
+
+        return null;
+    }
+
+    private static boolean isNumericOperator(JavaClassSource clazz, Block 
block, Expression expression) {
+        if (expression instanceof NumberLiteral) {
+            return true;
+        } else if (expression instanceof SimpleName) {
+            FieldSource field = getField(clazz, block, (SimpleName) 
expression);
+            if (field != null) {
+                return field.getType().isType("int") || 
field.getType().isType("long")
+                        || field.getType().isType("Integer") || 
field.getType().isType("Long");
+            }
+        }
+        return false;
+    }
+
+    private static int findLineNumber(String fullyQualifiedFileName, int 
position) {
+        int lines = 0;
+
+        try {
+            int current = 0;
+            try (BufferedReader br = new BufferedReader(new FileReader(new 
File(fullyQualifiedFileName)))) {
+                String line;
+                while ((line = br.readLine()) != null) {
+                    lines++;
+                    current += line.length() + 1; // add 1 for line feed
+                    if (current >= position) {
+                        return lines;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+            return -1;
+        }
+
+        return lines;
+    }
+
+}
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
index d48ce8e..d987c10 100644
--- 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelJavaTreeParserHelper.java
@@ -265,8 +265,12 @@ public final class CamelJavaTreeParserHelper {
             if (line > -1) {
                 newNode.setLineNumber("" + line);
             }
+            pos = mi.getName().getStartPosition() + mi.getName().getLength();
+            line = findLineNumber(fullyQualifiedFileName, pos);
+            if (line > -1) {
+                newNode.setLineNumberEnd("" + line);
+            }
             newNode.setFileName(fullyQualifiedFileName);
-
             newNode.setClassName(clazz.getQualifiedName());
             newNode.setMethodName(configureMethod.getName());
 
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlRestDslParserHelper.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlRestDslParserHelper.java
new file mode 100644
index 0000000..bb04cab
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/helper/CamelXmlRestDslParserHelper.java
@@ -0,0 +1,279 @@
+/**
+ * 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.List;
+
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.camel.parser.model.RestConfigurationDetails;
+import org.apache.camel.parser.model.RestServiceDetails;
+import org.apache.camel.parser.model.RestVerbDetails;
+
+public final class CamelXmlRestDslParserHelper {
+
+    public List<RestConfigurationDetails> parseRestConfiguration(Node xmlNode, 
String baseDir, String fullyQualifiedFileName) {
+
+        List<RestConfigurationDetails> answer = new ArrayList<>();
+
+        RestConfigurationDetails detail = new RestConfigurationDetails();
+        detail.setFileName(fullyQualifiedFileName);
+        walkXmlTree(xmlNode, detail);
+        answer.add(detail);
+
+        return answer;
+    }
+
+    public List<RestServiceDetails> parseRestService(Node xmlNode, String 
baseDir, String fullyQualifiedFileName) {
+
+        List<RestServiceDetails> answer = new ArrayList<>();
+
+        RestServiceDetails detail = new RestServiceDetails();
+        detail.setFileName(fullyQualifiedFileName);
+        walkXmlTree(xmlNode, detail);
+        answer.add(detail);
+
+        return answer;
+    }
+
+    private void walkXmlTree(Node xmlNode, RestConfigurationDetails detail) {
+        if ("restConfiguration".equals(xmlNode.getNodeName())) {
+            String lineNumber = (String) 
xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER);
+            String lineNumberEnd = (String) 
xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+            detail.setLineNumber(lineNumber);
+            detail.setLineNumberEnd(lineNumberEnd);
+
+            NamedNodeMap map = xmlNode.getAttributes();
+            detail.setComponent(extractAttribute(map, "component"));
+            detail.setApiComponent(extractAttribute(map, "apiComponent"));
+            detail.setProducerComponent(extractAttribute(map, 
"producerComponent"));
+            detail.setScheme(extractAttribute(map, "scheme"));
+            detail.setHost(extractAttribute(map, "host"));
+            detail.setApiHost(extractAttribute(map, "apiHost"));
+            detail.setPort(extractAttribute(map, "port"));
+            detail.setProducerApiDoc(extractAttribute(map, "producerApiDoc"));
+            detail.setContextPath(extractAttribute(map, "contextPath"));
+            detail.setApiContextPath(extractAttribute(map, "apiContextPath"));
+            detail.setApiContextRouteId(extractAttribute(map, 
"apiContextRouteId"));
+            detail.setApiContextIdPattern(extractAttribute(map, 
"apiContextIdPattern"));
+            detail.setApiContextListening(extractAttribute(map, 
"apiContextListening"));
+            detail.setApiVendorExtension(extractAttribute(map, 
"apiVendorExtension"));
+            detail.setHostNameResolver(extractAttribute(map, 
"hostNameResolver"));
+            detail.setBindingMode(extractAttribute(map, "bindingMode"));
+            detail.setSkipBindingOnErrorCode(extractAttribute(map, 
"skipBindingOnErrorCode"));
+            detail.setClientRequestValidation(extractAttribute(map, 
"clientRequestValidation"));
+            detail.setEnableCORS(extractAttribute(map, "enableCORS"));
+            detail.setJsonDataFormat(extractAttribute(map, "jsonDataFormat"));
+            detail.setXmlDataFormat(extractAttribute(map, "xmlDataFormat"));
+        }
+
+        if ("componentProperty".equals(xmlNode.getNodeName())
+            && (xmlNode.getParentNode() != null && 
"restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) {
+            NamedNodeMap map = xmlNode.getAttributes();
+            String key = extractAttribute(map, "key");
+            String value = extractAttribute(map, "value");
+            if (key != null && value != null) {
+                detail.addComponentProperty(key, value);
+            }
+        } else if ("endpointProperty".equals(xmlNode.getNodeName())
+            && (xmlNode.getParentNode() != null && 
"restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) {
+            NamedNodeMap map = xmlNode.getAttributes();
+            String key = extractAttribute(map, "key");
+            String value = extractAttribute(map, "value");
+            if (key != null && value != null) {
+                detail.addEndpointProperty(key, value);
+            }
+        } else if ("consumerProperty".equals(xmlNode.getNodeName())
+            && (xmlNode.getParentNode() != null && 
"restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) {
+            NamedNodeMap map = xmlNode.getAttributes();
+            String key = extractAttribute(map, "key");
+            String value = extractAttribute(map, "value");
+            if (key != null && value != null) {
+                detail.addConsumerProperty(key, value);
+            }
+        } else if ("dataFormatProperty".equals(xmlNode.getNodeName())
+            && (xmlNode.getParentNode() != null && 
"restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) {
+            NamedNodeMap map = xmlNode.getAttributes();
+            String key = extractAttribute(map, "key");
+            String value = extractAttribute(map, "value");
+            if (key != null && value != null) {
+                detail.addDataFormatProperty(key, value);
+            }
+        } else if ("apiProperty".equals(xmlNode.getNodeName())
+            && (xmlNode.getParentNode() != null && 
"restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) {
+            NamedNodeMap map = xmlNode.getAttributes();
+            String key = extractAttribute(map, "key");
+            String value = extractAttribute(map, "value");
+            if (key != null && value != null) {
+                detail.addApiProperty(key, value);
+            }
+        } else if ("corsHeaders".equals(xmlNode.getNodeName())
+            && (xmlNode.getParentNode() != null && 
"restConfiguration".equals(xmlNode.getParentNode().getNodeName()))) {
+            NamedNodeMap map = xmlNode.getAttributes();
+            String key = extractAttribute(map, "key");
+            String value = extractAttribute(map, "value");
+            if (key != null && value != null) {
+                detail.addCorsHeader(key, value);
+            }
+        }
+
+        // walk the rest of the children
+        NodeList children = xmlNode.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                walkXmlTree(child, detail);
+            }
+        }
+    }
+
+    private void walkXmlTree(Node xmlNode, RestServiceDetails detail) {
+        if ("rest".equals(xmlNode.getNodeName())) {
+            String lineNumber = (String) 
xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER);
+            String lineNumberEnd = (String) 
xmlNode.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+            detail.setLineNumber(lineNumber);
+            detail.setLineNumberEnd(lineNumberEnd);
+            extractAttributes(xmlNode, detail);
+        }
+
+        if (isParentRest(xmlNode)) {
+            if ("delete".equals(xmlNode.getNodeName())) {
+                RestVerbDetails verb = new RestVerbDetails();
+                verb.setMethod("delete");
+                detail.addVerb(verb);
+                extractAttributes(xmlNode, verb);
+            } else if ("get".equals(xmlNode.getNodeName())) {
+                RestVerbDetails verb = new RestVerbDetails();
+                verb.setMethod("get");
+                detail.addVerb(verb);
+                extractAttributes(xmlNode, verb);
+            } else if ("head".equals(xmlNode.getNodeName())) {
+                RestVerbDetails verb = new RestVerbDetails();
+                verb.setMethod("head");
+                detail.addVerb(verb);
+                extractAttributes(xmlNode, verb);
+            } else if ("patch".equals(xmlNode.getNodeName())) {
+                RestVerbDetails verb = new RestVerbDetails();
+                verb.setMethod("patch");
+                detail.addVerb(verb);
+                extractAttributes(xmlNode, verb);
+            } else if ("post".equals(xmlNode.getNodeName())) {
+                RestVerbDetails verb = new RestVerbDetails();
+                verb.setMethod("post");
+                detail.addVerb(verb);
+                extractAttributes(xmlNode, verb);
+            } else if ("put".equals(xmlNode.getNodeName())) {
+                RestVerbDetails verb = new RestVerbDetails();
+                verb.setMethod("put");
+                detail.addVerb(verb);
+                extractAttributes(xmlNode, verb);
+            }
+
+            if ("description".equals(xmlNode.getNodeName())) {
+                String value = xmlNode.getTextContent();
+                RestVerbDetails verb = getLastVerb(detail);
+                if (verb != null) {
+                    verb.setDescription(value);
+                } else {
+                    detail.setDescription(value);
+                }
+            } else if ("to".equals(xmlNode.getNodeName())) {
+                NamedNodeMap map = xmlNode.getAttributes();
+                String uri = extractAttribute(map, "uri");
+                RestVerbDetails verb = getLastVerb(detail);
+                if (verb != null) {
+                    verb.setTo(uri);
+                }
+            } else if ("toD".equals(xmlNode.getNodeName())) {
+                NamedNodeMap map = xmlNode.getAttributes();
+                String uri = extractAttribute(map, "uri");
+                RestVerbDetails verb = getLastVerb(detail);
+                if (verb != null) {
+                    verb.setToD(uri);
+                }
+            }
+        }
+
+        // walk the rest of the children
+        NodeList children = xmlNode.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                walkXmlTree(child, detail);
+            }
+        }
+    }
+
+    private static void extractAttributes(Node xmlNode, RestServiceDetails 
detail) {
+        NamedNodeMap map = xmlNode.getAttributes();
+        detail.setConsumes(extractAttribute(map, "consumes"));
+        detail.setProduces(extractAttribute(map, "produces"));
+        detail.setApiDocs(extractAttribute(map, "apiDocs"));
+        detail.setBindingMode(extractAttribute(map, "bindingMode"));
+        detail.setClientRequestValidation(extractAttribute(map, 
"clientRequestValidation"));
+        detail.setEnableCORS(extractAttribute(map, "enableCORS"));
+        detail.setPath(extractAttribute(map, "path"));
+        detail.setSkipBindingOnErrorCode(extractAttribute(map, 
"skipBindingOnErrorCode"));
+        detail.setTag(extractAttribute(map, "tag"));
+    }
+
+    private static void extractAttributes(Node xmlNode, RestVerbDetails 
detail) {
+        NamedNodeMap map = xmlNode.getAttributes();
+        detail.setUri(extractAttribute(map, "uri"));
+        detail.setConsumes(extractAttribute(map, "consumes"));
+        detail.setProduces(extractAttribute(map, "produces"));
+        detail.setApiDocs(extractAttribute(map, "apiDocs"));
+        detail.setBindingMode(extractAttribute(map, "bindingMode"));
+        detail.setClientRequestValidation(extractAttribute(map, 
"clientRequestValidation"));
+        detail.setSkipBindingOnErrorCode(extractAttribute(map, 
"skipBindingOnErrorCode"));
+        detail.setType(extractAttribute(map, "type"));
+        detail.setOutType(extractAttribute(map, "outType"));
+    }
+
+    private static RestVerbDetails getLastVerb(RestServiceDetails detail) {
+        if (detail.getVerbs() == null) {
+            return null;
+        }
+        return detail.getVerbs().get(detail.getVerbs().size() - 1);
+    }
+
+    private static boolean isParentRest(Node node) {
+        if (node == null) {
+            return false;
+        }
+        String name = node.getNodeName();
+        if ("rest".equals(name)) {
+            return true;
+        } else {
+            return isParentRest(node.getParentNode());
+        }
+    }
+
+    private static String extractAttribute(NamedNodeMap map, String name) {
+        if (map != null) {
+            Node attr = map.getNamedItem(name);
+            if (attr != null) {
+                return attr.getTextContent();
+            }
+        }
+        return null;
+    }
+
+}
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java
new file mode 100644
index 0000000..b1db413
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestConfigurationDetails.java
@@ -0,0 +1,371 @@
+/**
+ * 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;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class RestConfigurationDetails {
+
+    // source code details
+    private String fileName;
+    private String lineNumber;
+    private String lineNumberEnd;
+    private int linePosition;
+
+    // java source code details
+    private String className;
+    private String methodName;
+
+    // camel rest configuration details
+    private String component;
+    private String apiComponent;
+    private String producerComponent;
+    private String scheme;
+    private String host;
+    private String apiHost;
+    private String port;
+    private String producerApiDoc;
+    private String contextPath;
+    private String apiContextPath;
+    private String apiContextRouteId;
+    private String apiContextIdPattern;
+    private String apiContextListening;
+    private String apiVendorExtension;
+    private String hostNameResolver;
+    private String bindingMode;
+    private String skipBindingOnErrorCode;
+    private String clientRequestValidation;
+    private String enableCORS;
+    private String jsonDataFormat;
+    private String xmlDataFormat;
+    private Map<String, String> componentProperties;
+    private Map<String, String> endpointProperties;
+    private Map<String, String> consumerProperties;
+    private Map<String, String> dataFormatProperties;
+    private Map<String, String> apiProperties;
+    private Map<String, String> corsHeaders;
+
+    public RestConfigurationDetails() {
+    }
+
+    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 int getLinePosition() {
+        return linePosition;
+    }
+
+    public void setLinePosition(int linePosition) {
+        this.linePosition = linePosition;
+    }
+
+    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 getComponent() {
+        return component;
+    }
+
+    public void setComponent(String component) {
+        this.component = component;
+    }
+
+    public String getApiComponent() {
+        return apiComponent;
+    }
+
+    public void setApiComponent(String apiComponent) {
+        this.apiComponent = apiComponent;
+    }
+
+    public String getProducerComponent() {
+        return producerComponent;
+    }
+
+    public void setProducerComponent(String producerComponent) {
+        this.producerComponent = producerComponent;
+    }
+
+    public String getScheme() {
+        return scheme;
+    }
+
+    public void setScheme(String scheme) {
+        this.scheme = scheme;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getApiHost() {
+        return apiHost;
+    }
+
+    public void setApiHost(String apiHost) {
+        this.apiHost = apiHost;
+    }
+
+    public String getPort() {
+        return port;
+    }
+
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    public String getProducerApiDoc() {
+        return producerApiDoc;
+    }
+
+    public void setProducerApiDoc(String producerApiDoc) {
+        this.producerApiDoc = producerApiDoc;
+    }
+
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    public void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    public String getApiContextPath() {
+        return apiContextPath;
+    }
+
+    public void setApiContextPath(String apiContextPath) {
+        this.apiContextPath = apiContextPath;
+    }
+
+    public String getApiContextRouteId() {
+        return apiContextRouteId;
+    }
+
+    public void setApiContextRouteId(String apiContextRouteId) {
+        this.apiContextRouteId = apiContextRouteId;
+    }
+
+    public String getApiContextIdPattern() {
+        return apiContextIdPattern;
+    }
+
+    public void setApiContextIdPattern(String apiContextIdPattern) {
+        this.apiContextIdPattern = apiContextIdPattern;
+    }
+
+    public String getApiContextListening() {
+        return apiContextListening;
+    }
+
+    public void setApiContextListening(String apiContextListening) {
+        this.apiContextListening = apiContextListening;
+    }
+
+    public String getApiVendorExtension() {
+        return apiVendorExtension;
+    }
+
+    public void setApiVendorExtension(String apiVendorExtension) {
+        this.apiVendorExtension = apiVendorExtension;
+    }
+
+    public String getHostNameResolver() {
+        return hostNameResolver;
+    }
+
+    public void setHostNameResolver(String hostNameResolver) {
+        this.hostNameResolver = hostNameResolver;
+    }
+
+    public String getBindingMode() {
+        return bindingMode;
+    }
+
+    public void setBindingMode(String bindingMode) {
+        this.bindingMode = bindingMode;
+    }
+
+    public String getSkipBindingOnErrorCode() {
+        return skipBindingOnErrorCode;
+    }
+
+    public void setSkipBindingOnErrorCode(String skipBindingOnErrorCode) {
+        this.skipBindingOnErrorCode = skipBindingOnErrorCode;
+    }
+
+    public String getClientRequestValidation() {
+        return clientRequestValidation;
+    }
+
+    public void setClientRequestValidation(String clientRequestValidation) {
+        this.clientRequestValidation = clientRequestValidation;
+    }
+
+    public String getEnableCORS() {
+        return enableCORS;
+    }
+
+    public void setEnableCORS(String enableCORS) {
+        this.enableCORS = enableCORS;
+    }
+
+    public String getJsonDataFormat() {
+        return jsonDataFormat;
+    }
+
+    public void setJsonDataFormat(String jsonDataFormat) {
+        this.jsonDataFormat = jsonDataFormat;
+    }
+
+    public String getXmlDataFormat() {
+        return xmlDataFormat;
+    }
+
+    public void setXmlDataFormat(String xmlDataFormat) {
+        this.xmlDataFormat = xmlDataFormat;
+    }
+
+    public Map<String, String> getComponentProperties() {
+        return componentProperties;
+    }
+
+    public void setComponentProperties(Map<String, String> 
componentProperties) {
+        this.componentProperties = componentProperties;
+    }
+
+    public Map<String, String> getEndpointProperties() {
+        return endpointProperties;
+    }
+
+    public void setEndpointProperties(Map<String, String> endpointProperties) {
+        this.endpointProperties = endpointProperties;
+    }
+
+    public Map<String, String> getConsumerProperties() {
+        return consumerProperties;
+    }
+
+    public void setConsumerProperties(Map<String, String> consumerProperties) {
+        this.consumerProperties = consumerProperties;
+    }
+
+    public Map<String, String> getDataFormatProperties() {
+        return dataFormatProperties;
+    }
+
+    public void setDataFormatProperties(Map<String, String> 
dataFormatProperties) {
+        this.dataFormatProperties = dataFormatProperties;
+    }
+
+    public Map<String, String> getApiProperties() {
+        return apiProperties;
+    }
+
+    public void setApiProperties(Map<String, String> apiProperties) {
+        this.apiProperties = apiProperties;
+    }
+
+    public Map<String, String> getCorsHeaders() {
+        return corsHeaders;
+    }
+
+    public void setCorsHeaders(Map<String, String> corsHeaders) {
+        this.corsHeaders = corsHeaders;
+    }
+
+    public void addComponentProperty(String key, String value) {
+        if (componentProperties == null) {
+            componentProperties = new LinkedHashMap<>();
+        }
+        componentProperties.put(key, value);
+    }
+
+    public void addEndpointProperty(String key, String value) {
+        if (endpointProperties == null) {
+            endpointProperties = new LinkedHashMap<>();
+        }
+        endpointProperties.put(key, value);
+    }
+
+    public void addConsumerProperty(String key, String value) {
+        if (consumerProperties == null) {
+            consumerProperties = new LinkedHashMap<>();
+        }
+        consumerProperties.put(key, value);
+    }
+
+    public void addDataFormatProperty(String key, String value) {
+        if (dataFormatProperties == null) {
+            dataFormatProperties = new LinkedHashMap<>();
+        }
+        dataFormatProperties.put(key, value);
+    }
+
+    public void addApiProperty(String key, String value) {
+        if (apiProperties == null) {
+            apiProperties = new LinkedHashMap<>();
+        }
+        apiProperties.put(key, value);
+    }
+
+    public void addCorsHeader(String key, String value) {
+        if (corsHeaders == null) {
+            corsHeaders = new LinkedHashMap<>();
+        }
+        corsHeaders.put(key, value);
+    }
+}
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java
new file mode 100644
index 0000000..8e5d60d
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestServiceDetails.java
@@ -0,0 +1,189 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RestServiceDetails {
+
+    // source code details
+    private String fileName;
+    private String lineNumber;
+    private String lineNumberEnd;
+    private int linePosition;
+
+    // java source code details
+    private String className;
+    private String methodName;
+
+    // camel service details
+    private String path;
+    private String tag;
+    private String consumes;
+    private String produces;
+    private String bindingMode;
+    private String skipBindingOnErrorCode;
+    private String clientRequestValidation;
+    private String enableCORS;
+    private String apiDocs;
+    private String description;
+    private List<RestVerbDetails> verbs;
+
+    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 int getLinePosition() {
+        return linePosition;
+    }
+
+    public void setLinePosition(int linePosition) {
+        this.linePosition = linePosition;
+    }
+
+    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 getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getTag() {
+        return tag;
+    }
+
+    public void setTag(String tag) {
+        this.tag = tag;
+    }
+
+    public String getConsumes() {
+        return consumes;
+    }
+
+    public void setConsumes(String consumes) {
+        this.consumes = consumes;
+    }
+
+    public String getProduces() {
+        return produces;
+    }
+
+    public void setProduces(String produces) {
+        this.produces = produces;
+    }
+
+    public String getBindingMode() {
+        return bindingMode;
+    }
+
+    public void setBindingMode(String bindingMode) {
+        this.bindingMode = bindingMode;
+    }
+
+    public String getSkipBindingOnErrorCode() {
+        return skipBindingOnErrorCode;
+    }
+
+    public void setSkipBindingOnErrorCode(String skipBindingOnErrorCode) {
+        this.skipBindingOnErrorCode = skipBindingOnErrorCode;
+    }
+
+    public String getClientRequestValidation() {
+        return clientRequestValidation;
+    }
+
+    public void setClientRequestValidation(String clientRequestValidation) {
+        this.clientRequestValidation = clientRequestValidation;
+    }
+
+    public String getEnableCORS() {
+        return enableCORS;
+    }
+
+    public void setEnableCORS(String enableCORS) {
+        this.enableCORS = enableCORS;
+    }
+
+    public String getApiDocs() {
+        return apiDocs;
+    }
+
+    public void setApiDocs(String apiDocs) {
+        this.apiDocs = apiDocs;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<RestVerbDetails> getVerbs() {
+        return verbs;
+    }
+
+    public void setVerbs(List<RestVerbDetails> verbs) {
+        this.verbs = verbs;
+    }
+
+    public void addVerb(RestVerbDetails verb) {
+        if (verbs == null) {
+            verbs = new ArrayList<>();
+        }
+        verbs.add(verb);
+    }
+}
diff --git 
a/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java
 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java
new file mode 100644
index 0000000..ba384e2
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/main/java/org/apache/camel/parser/model/RestVerbDetails.java
@@ -0,0 +1,197 @@
+/**
+ * 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 RestVerbDetails {
+
+    // source code details
+    private String fileName;
+    private String lineNumber;
+    private String lineNumberEnd;
+    private int linePosition;
+
+    // java source code details
+    private String className;
+    private String methodName;
+
+    // camel verb details
+    private String method;
+    private String uri;
+    private String consumes;
+    private String produces;
+    private String bindingMode;
+    private String skipBindingOnErrorCode;
+    private String clientRequestValidation;
+    private String type;
+    private String outType;
+    private String description;
+    private String apiDocs;
+    private String to;
+    private String toD;
+
+    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 int getLinePosition() {
+        return linePosition;
+    }
+
+    public void setLinePosition(int linePosition) {
+        this.linePosition = linePosition;
+    }
+
+    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 getMethod() {
+        return method;
+    }
+
+    public void setMethod(String method) {
+        this.method = method;
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getConsumes() {
+        return consumes;
+    }
+
+    public void setConsumes(String consumes) {
+        this.consumes = consumes;
+    }
+
+    public String getProduces() {
+        return produces;
+    }
+
+    public void setProduces(String produces) {
+        this.produces = produces;
+    }
+
+    public String getBindingMode() {
+        return bindingMode;
+    }
+
+    public void setBindingMode(String bindingMode) {
+        this.bindingMode = bindingMode;
+    }
+
+    public String getSkipBindingOnErrorCode() {
+        return skipBindingOnErrorCode;
+    }
+
+    public void setSkipBindingOnErrorCode(String skipBindingOnErrorCode) {
+        this.skipBindingOnErrorCode = skipBindingOnErrorCode;
+    }
+
+    public String getClientRequestValidation() {
+        return clientRequestValidation;
+    }
+
+    public void setClientRequestValidation(String clientRequestValidation) {
+        this.clientRequestValidation = clientRequestValidation;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getOutType() {
+        return outType;
+    }
+
+    public void setOutType(String outType) {
+        this.outType = outType;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getApiDocs() {
+        return apiDocs;
+    }
+
+    public void setApiDocs(String apiDocs) {
+        this.apiDocs = apiDocs;
+    }
+
+    public String getTo() {
+        return to;
+    }
+
+    public void setTo(String to) {
+        this.to = to;
+    }
+
+    public String getToD() {
+        return toD;
+    }
+
+    public void setToD(String toD) {
+        this.toD = toD;
+    }
+}
diff --git 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java
 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java
new file mode 100644
index 0000000..70afcfb
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java
@@ -0,0 +1,51 @@
+/**
+ * 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.java;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.RestBindingMode;
+import org.apache.camel.model.rest.RestHostNameResolver;
+
+public class MyRestDslRouteBuilder extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        restConfiguration()
+            .contextPath("myapi").port(1234)
+            .component("jetty")
+            .apiComponent("swagger")
+            .apiHost("localhost")
+            .apiContextPath("myapi/swagger")
+            .skipBindingOnErrorCode(true)
+            .scheme("https")
+            .hostNameResolver(RestHostNameResolver.allLocalIp)
+            .bindingMode(RestBindingMode.json)
+            .componentProperty("foo", "123")
+            .endpointProperty("pretty", "false")
+            .consumerProperty("bar", "456")
+            .corsHeaderProperty("key1", "value1")
+            .corsHeaderProperty("key2", "value2");
+
+        rest("/foo").consumes("xml").produces("json").description("my foo 
service")
+            .get("{id}").apiDocs(false)
+                .description("get by id")
+                .to("log:id")
+            .post().bindingMode(RestBindingMode.xml)
+                .description("post something")
+                .toD("log:post");
+    }
+}
diff --git 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
index 2173742..51cc75f 100644
--- 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
+++ 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaDslTest.java
@@ -49,6 +49,8 @@ public class RoasterJavaDslTest extends CamelTestSupport {
         assertEquals("bar", details.getRouteId());
         assertEquals("configure", details.getMethodName());
         assertEquals("org.apache.camel.parser.java.MyJavaDslRouteBuilder", 
details.getClassName());
+        assertEquals("28", list.get(0).getLineNumber());
+        assertEquals("28", list.get(0).getLineNumberEnd());
 
         String tree = details.dump(0);
         LOG.info("\n" + tree);
diff --git 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java
 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java
new file mode 100644
index 0000000..e5ae2a5
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/java/RoasterJavaRestDslTest.java
@@ -0,0 +1,103 @@
+/**
+ * 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.java;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.camel.parser.RestDslParser;
+import org.apache.camel.parser.model.RestConfigurationDetails;
+import org.apache.camel.parser.model.RestServiceDetails;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.jboss.forge.roaster.Roaster;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.junit.Test;
+
+public class RoasterJavaRestDslTest extends CamelTestSupport {
+
+    @Override
+    public boolean isDumpRouteCoverage() {
+        return false;
+    }
+
+    @Test
+    public void parseRestConfiguration() throws Exception {
+        JavaClassSource clazz = (JavaClassSource) Roaster.parse(new 
File("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java"));
+
+        List<RestConfigurationDetails> list = 
RestDslParser.parseRestConfiguration(clazz, ".",
+            
"src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java", true);
+        assertEquals(1, list.size());
+        RestConfigurationDetails details = list.get(0);
+        assertEquals("27", details.getLineNumber());
+        assertEquals("41", details.getLineNumberEnd());
+        
assertEquals("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java",
 details.getFileName());
+        assertEquals("configure", details.getMethodName());
+        assertEquals("org.apache.camel.parser.java.MyRestDslRouteBuilder", 
details.getClassName());
+        assertEquals("1234", details.getPort());
+        assertEquals("myapi", details.getContextPath());
+        assertEquals("jetty", details.getComponent());
+        assertEquals("json", details.getBindingMode());
+        assertEquals("swagger", details.getApiComponent());
+        assertEquals("myapi/swagger", details.getApiContextPath());
+        assertEquals("localhost", details.getApiHost());
+        assertEquals("true", details.getSkipBindingOnErrorCode());
+        assertEquals("https", details.getScheme());
+        assertEquals("allLocalIp", details.getHostNameResolver());
+
+        assertEquals(1, details.getComponentProperties().size());
+        assertEquals("123", details.getComponentProperties().get("foo"));
+        assertEquals(1, details.getEndpointProperties().size());
+        assertEquals("false", details.getEndpointProperties().get("pretty"));
+        assertEquals(1, details.getEndpointProperties().size());
+        assertEquals("456", details.getConsumerProperties().get("bar"));
+        assertEquals(2, details.getCorsHeaders().size());
+        assertEquals("value1", details.getCorsHeaders().get("key1"));
+        assertEquals("value2", details.getCorsHeaders().get("key2"));
+    }
+
+    @Test
+    public void parseRestService() throws Exception {
+        JavaClassSource clazz = (JavaClassSource) Roaster.parse(new 
File("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java"));
+
+        List<RestServiceDetails> list = RestDslParser.parseRestService(clazz, 
".",
+            
"src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java", true);
+        assertEquals(1, list.size());
+        RestServiceDetails details = list.get(0);
+        assertEquals("43", details.getLineNumber());
+        assertEquals("49", details.getLineNumberEnd());
+        
assertEquals("src/test/java/org/apache/camel/parser/java/MyRestDslRouteBuilder.java",
 details.getFileName());
+        assertEquals("configure", details.getMethodName());
+        assertEquals("org.apache.camel.parser.java.MyRestDslRouteBuilder", 
details.getClassName());
+
+        assertEquals("/foo", details.getPath());
+        assertEquals("my foo service", details.getDescription());
+        assertEquals("json", details.getProduces());
+        assertEquals("json", details.getProduces());
+        assertEquals(2, details.getVerbs().size());
+        assertEquals("get", details.getVerbs().get(0).getMethod());
+        assertEquals("{id}", details.getVerbs().get(0).getUri());
+        assertEquals("get by id", details.getVerbs().get(0).getDescription());
+        assertEquals("log:id", details.getVerbs().get(0).getTo());
+        assertEquals("false", details.getVerbs().get(0).getApiDocs());
+        assertEquals("post", details.getVerbs().get(1).getMethod());
+        assertEquals("post something", 
details.getVerbs().get(1).getDescription());
+        assertEquals("xml", details.getVerbs().get(1).getBindingMode());
+        assertEquals("log:post", details.getVerbs().get(1).getToD());
+        assertNull(details.getVerbs().get(1).getUri());
+    }
+
+}
diff --git 
a/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/XmlRestDslTest.java
 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/XmlRestDslTest.java
new file mode 100644
index 0000000..78e1156
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/test/java/org/apache/camel/parser/xml/XmlRestDslTest.java
@@ -0,0 +1,111 @@
+/**
+ * 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.xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.camel.parser.RestDslParser;
+import org.apache.camel.parser.XmlRestDslParser;
+import org.apache.camel.parser.model.RestConfigurationDetails;
+import org.apache.camel.parser.model.RestServiceDetails;
+import org.jboss.forge.roaster.Roaster;
+import org.jboss.forge.roaster.model.source.JavaClassSource;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class XmlRestDslTest {
+
+    @Test
+    public void testXmlTree() throws Exception {
+        InputStream is = new 
FileInputStream("src/test/resources/org/apache/camel/parser/xml/myrest.xml");
+        String fqn = 
"src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml";
+        String baseDir = "src/test/resources";
+        List<RestConfigurationDetails> list = 
XmlRestDslParser.parseRestConfiguration(is, baseDir, fqn);
+
+        assertEquals(1, list.size());
+        RestConfigurationDetails details = list.get(0);
+        
assertEquals("src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml", 
details.getFileName());
+        assertNull(details.getMethodName());
+        assertNull(details.getClassName());
+
+        assertEquals("29", details.getLineNumber());
+        assertEquals("35", details.getLineNumberEnd());
+        assertEquals("1234", details.getPort());
+        assertEquals("myapi", details.getContextPath());
+        assertEquals("jetty", details.getComponent());
+        assertEquals("json", details.getBindingMode());
+        assertEquals("swagger", details.getApiComponent());
+        assertEquals("myapi/swagger", details.getApiContextPath());
+        assertEquals("localhost", details.getApiHost());
+        assertEquals("true", details.getSkipBindingOnErrorCode());
+        assertEquals("https", details.getScheme());
+        assertEquals("allLocalIp", details.getHostNameResolver());
+
+        assertEquals(1, details.getComponentProperties().size());
+        assertEquals("123", details.getComponentProperties().get("foo"));
+        assertEquals(1, details.getEndpointProperties().size());
+        assertEquals("false", details.getEndpointProperties().get("pretty"));
+        assertEquals(1, details.getEndpointProperties().size());
+        assertEquals("456", details.getConsumerProperties().get("bar"));
+        assertEquals(2, details.getCorsHeaders().size());
+        assertEquals("value1", details.getCorsHeaders().get("key1"));
+        assertEquals("value2", details.getCorsHeaders().get("key2"));
+    }
+
+    @Test
+    public void parseRestService() throws Exception {
+        InputStream is = new 
FileInputStream("src/test/resources/org/apache/camel/parser/xml/myrest.xml");
+        String fqn = 
"src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml";
+        String baseDir = "src/test/resources";
+        List<RestServiceDetails> list = XmlRestDslParser.parseRestService(is, 
baseDir, fqn);
+
+        assertEquals(1, list.size());
+        RestServiceDetails details = list.get(0);
+        
assertEquals("src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml", 
details.getFileName());
+        assertNull(details.getMethodName());
+        assertNull(details.getClassName());
+
+        assertEquals("37", details.getLineNumber());
+        assertEquals("47", details.getLineNumberEnd());
+        
assertEquals("src/test/resources/org/apache/camel/camel/parser/xml/myrest.xml", 
details.getFileName());
+        assertNull(details.getMethodName());
+        assertNull(details.getClassName());
+
+        assertEquals("/foo", details.getPath());
+        assertEquals("my foo service", details.getDescription());
+        assertEquals("json", details.getProduces());
+        assertEquals("json", details.getProduces());
+        assertEquals(2, details.getVerbs().size());
+        assertEquals("get", details.getVerbs().get(0).getMethod());
+        assertEquals("{id}", details.getVerbs().get(0).getUri());
+        assertEquals("get by id", details.getVerbs().get(0).getDescription());
+        assertEquals("log:id", details.getVerbs().get(0).getTo());
+        assertEquals("false", details.getVerbs().get(0).getApiDocs());
+        assertEquals("post", details.getVerbs().get(1).getMethod());
+        assertEquals("post something", 
details.getVerbs().get(1).getDescription());
+        assertEquals("xml", details.getVerbs().get(1).getBindingMode());
+        assertEquals("log:post", details.getVerbs().get(1).getToD());
+        assertNull(details.getVerbs().get(1).getUri());
+    }
+
+
+}
diff --git 
a/tooling/camel-route-parser/src/test/resources/org/apache/camel/parser/xml/myrest.xml
 
b/tooling/camel-route-parser/src/test/resources/org/apache/camel/parser/xml/myrest.xml
new file mode 100644
index 0000000..82f493a
--- /dev/null
+++ 
b/tooling/camel-route-parser/src/test/resources/org/apache/camel/parser/xml/myrest.xml
@@ -0,0 +1,51 @@
+<?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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring 
http://camel.apache.org/schema/spring/camel-spring.xsd";>
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring";>
+    <restConfiguration component="jetty" port="1234" bindingMode="json" 
apiComponent="swagger" scheme="https"
+                       contextPath="myapi" apiContextPath="myapi/swagger" 
hostNameResolver="allLocalIp" apiHost="localhost"
+                       skipBindingOnErrorCode="true">
+      <componentProperty key="foo" value="123"/>
+      <endpointProperty key="pretty" value="false"/>
+      <consumerProperty key="bar" value="456"/>
+      <corsHeaders key="key1" value="value1"/>
+      <corsHeaders key="key2" value="value2"/>
+    </restConfiguration>
+
+    <rest path="/foo" consumes="xml" produces="json">
+      <description>my foo service</description>
+      <get uri="{id}" apiDocs="false">
+        <description>get by id</description>
+        <to uri="log:id"/>
+      </get>
+      <post bindingMode="xml">
+        <description>post something</description>
+        <toD uri="log:post"/>
+      </post>
+    </rest>
+
+  </camelContext>
+
+</beans>

Reply via email to