You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gn...@apache.org on 2020/01/27 15:47:26 UTC

[camel] 05/10: [CAMEL-14437] Use a single model / json representation for all internal tooling generators

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

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

commit f63d6fb7016a57c430db9874ffe0208d1a62e2c4
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Jan 24 14:38:03 2020 +0100

    [CAMEL-14437] Use a single model / json representation for all internal tooling generators
---
 bom/camel-bom/pom.xml                              |   5 +
 parent/pom.xml                                     |   5 +
 tooling/apt/pom.xml                                |   4 +
 .../apt/CoreEipAnnotationProcessorHelper.java      | 610 +++++-----------
 .../camel/tools/apt/DocumentationHelper.java       | 131 ----
 .../tools/apt/EndpointAnnotationProcessor.java     | 769 ++++++++++-----------
 .../camel/tools/apt/ModelAnnotationProcessor.java  |  18 +-
 .../tools/apt/PropertyConfigurerGenerator.java     | 168 ++---
 .../tools/apt/PropertyPlaceholderGenerator.java    | 212 +++---
 .../tools/apt/SpringAnnotationProcessorHelper.java | 325 ++-------
 .../camel/tools/apt/helper/EndpointHelper.java     |  64 +-
 .../camel/tools/apt/model/ComponentModel.java      | 192 -----
 .../camel/tools/apt/model/ComponentOption.java     | 160 -----
 .../camel/tools/apt/model/EndpointOption.java      | 178 -----
 .../apache/camel/tools/apt/model/EndpointPath.java | 129 ----
 .../camel/tools/apt/model/PropertyOption.java      |  27 -
 .../tools/apt/EndpointOptionComparatorTest.java    |  50 +-
 tooling/{apt => camel-tooling-model}/pom.xml       |  68 +-
 .../org/apache/camel/tooling/model/BaseModel.java  | 218 ++++++
 .../camel/tooling/model/BaseOptionModel.java}      | 183 +++--
 .../apache/camel/tooling/model/ComponentModel.java | 254 +++++++
 .../camel/tooling/model/DataFormatModel.java       | 122 ++++
 .../org/apache/camel/tooling/model/EipModel.java   | 127 ++++
 .../apache/camel/tooling}/model/ExampleModel.java  |  14 +-
 .../apache/camel/tooling/model/LanguageModel.java  | 122 ++++
 .../org/apache/camel/tooling/model/OtherModel.java |  86 +++
 .../tooling}/model/ComponentOptionModelTest.java   |   7 +-
 .../apache/camel/tooling/util/PackageHelper.java   |  16 +
 .../camel/tooling/util/PackageHelperTest.java      |   6 +
 tooling/maven/camel-package-maven-plugin/pom.xml   |   4 +
 .../camel/maven/packaging/EndpointDslMojo.java     | 130 +---
 .../packaging/PackageArchetypeCatalogMojo.java     |  15 +-
 .../maven/packaging/PackageComponentMojo.java      |   5 +-
 .../maven/packaging/PackageDataFormatMojo.java     | 428 ++++--------
 .../camel/maven/packaging/PackageLanguageMojo.java | 274 ++------
 .../camel/maven/packaging/PackageModelMojo.java    |   2 -
 .../camel/maven/packaging/PackageOtherMojo.java    | 143 +---
 .../maven/packaging/PrepareCatalogKarafMojo.java   |  42 +-
 .../camel/maven/packaging/PrepareCatalogMojo.java  |  52 +-
 .../maven/packaging/PrepareComponentMojo.java      |  19 +-
 .../camel/maven/packaging/PrepareExampleMojo.java  |  12 +-
 .../maven/packaging/PrepareParentPomMojo.java      |  18 +-
 .../maven/packaging/PrepareReleasePomMojo.java     |  16 +-
 .../maven/packaging/PrepareUserGuideMojo.java      | 190 +----
 .../packaging/UpdateDocComponentListMojo.java      | 221 +-----
 .../camel/maven/packaging/UpdateReadmeMojo.java    | 463 ++++---------
 .../maven/packaging/ValidateComponentMojo.java     |  13 +-
 .../camel/maven/packaging/ValidateHelper.java      |  15 +-
 .../maven/packaging/model/ComponentModel.java      | 220 ------
 .../packaging/model/ComponentOptionModel.java      | 197 ------
 .../maven/packaging/model/DataFormatModel.java     | 163 -----
 .../packaging/model/DataFormatOptionModel.java     | 118 ----
 .../camel/maven/packaging/model/EipModel.java      | 128 ----
 .../maven/packaging/model/EipOptionModel.java      | 187 -----
 .../camel/maven/packaging/model/LanguageModel.java | 163 -----
 .../maven/packaging/model/LanguageOptionModel.java | 118 ----
 .../camel/maven/packaging/model/OtherModel.java    | 153 ----
 .../maven/packaging/model/OtherOptionModel.java    | 197 ------
 .../model/SpringBootAutoConfigureOptionModel.java  |  93 ---
 .../maven/packaging/model/SpringBootModel.java     |  60 --
 .../src/main/resources/dataformat-options.mvel     |   8 +-
 .../src/main/resources/eip-options.mvel            |   6 +-
 .../src/main/resources/endpoint-options.mvel       |   6 +-
 .../src/main/resources/language-options.mvel       |   8 +-
 .../src/main/resources/readme-examples.mvel        |   2 +-
 .../main/resources/website-components-list.mvel    |   2 +-
 .../main/resources/website-dataformats-list.mvel   |   2 +-
 .../src/main/resources/website-languages-list.mvel |   2 +-
 .../src/main/resources/website-others-list.mvel    |   2 +-
 tooling/pom.xml                                    |   1 +
 70 files changed, 2506 insertions(+), 5662 deletions(-)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index a96ec06..952a197 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -1705,6 +1705,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-tooling-model</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-tooling-util</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/parent/pom.xml b/parent/pom.xml
index 976ddea..b20f860 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -739,6 +739,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
+                <artifactId>camel-tooling-model</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
                 <artifactId>camel-api</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/tooling/apt/pom.xml b/tooling/apt/pom.xml
index 2a81ce2..7a1d1cc 100644
--- a/tooling/apt/pom.xml
+++ b/tooling/apt/pom.xml
@@ -57,6 +57,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-tooling-util</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-tooling-model</artifactId>
+        </dependency>
 
         <!-- logging -->
         <dependency>
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java
index 4eeea7d..1e8d8f9 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/CoreEipAnnotationProcessorHelper.java
@@ -17,13 +17,14 @@
 package org.apache.camel.tools.apt;
 
 import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
-import java.util.stream.Collectors;
 
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
@@ -34,6 +35,7 @@ import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.ElementFilter;
 import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementRef;
@@ -45,18 +47,10 @@ import javax.xml.bind.annotation.XmlValue;
 import org.apache.camel.spi.AsPredicate;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
-
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findJavaDoc;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findTypeElement;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findTypeElementChildren;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.hasSuperClass;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.implementsInterface;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.processFile;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.sanitizeDescription;
-import static org.apache.camel.tooling.util.Strings.canonicalClassName;
-import static org.apache.camel.tooling.util.Strings.isNullOrEmpty;
-import static org.apache.camel.tooling.util.Strings.safeNull;
+import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.tooling.model.EipModel.EipOptionModel;
 
 /**
  * Process all camel-core's model classes (EIPs and DSL) and generate json
@@ -86,9 +80,24 @@ public class CoreEipAnnotationProcessorHelper {
 
     private boolean skipUnwanted = true;
 
+    protected void generatePropertyPlaceholderProviderSource(ProcessingEnvironment processingEnv,
+                                                             TypeElement parent, String fqnDef,
+                                                             Set<EipOptionModel> options) {
+
+        String def = fqnDef.substring(fqnDef.lastIndexOf('.') + 1);
+        String cn = def + "PropertyPlaceholderProvider";
+        String fqn = "org.apache.camel.model.placeholder." + cn;
+        try (Writer w = processingEnv.getFiler().createSourceFile(fqn, parent).openWriter()) {
+            PropertyPlaceholderGenerator.generatePropertyPlaceholderProviderSource(def, fqnDef, cn, options, w);
+        } catch (Exception e) {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
+            AnnotationProcessorHelper.dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
+        }
+    }
+
     protected void processModelClass(final ProcessingEnvironment processingEnv, final RoundEnvironment roundEnv,
                                      final TypeElement classElement, Set<String> propertyPlaceholderDefinitions) {
-        final String javaTypeName = canonicalClassName(classElement.getQualifiedName().toString());
+        final String javaTypeName = Strings.canonicalClassName(classElement.getQualifiedName().toString());
         String packageName = javaTypeName.substring(0, javaTypeName.lastIndexOf("."));
 
         // skip abstract classes
@@ -109,7 +118,7 @@ public class CoreEipAnnotationProcessorHelper {
         }
 
         String aName = rootElement.name();
-        if (isNullOrEmpty(aName) || "##default".equals(aName)) {
+        if (Strings.isNullOrEmpty(aName) || "##default".equals(aName)) {
             XmlType typeElement = classElement.getAnnotation(XmlType.class);
             aName = typeElement.name();
         }
@@ -117,14 +126,14 @@ public class CoreEipAnnotationProcessorHelper {
 
         // lets use the xsd name as the file name
         String fileName;
-        if (isNullOrEmpty(name) || "##default".equals(name)) {
-            fileName = classElement.getSimpleName().toString() + ".json";
+        if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
+            fileName = classElement.getSimpleName().toString() + PackageHelper.JSON_SUFIX;
         } else {
-            fileName = name + ".json";
+            fileName = name + PackageHelper.JSON_SUFIX;
         }
 
         // write json schema and property placeholder provider
-        processFile(processingEnv, packageName, fileName, writer -> writeJSonSchemeAndPropertyPlaceholderProvider(processingEnv, writer,
+        AnnotationProcessorHelper.processFile(processingEnv, packageName, fileName, writer -> writeJSonSchemeAndPropertyPlaceholderProvider(processingEnv, writer,
                 roundEnv, classElement, rootElement, javaTypeName, name, propertyPlaceholderDefinitions));
     }
 
@@ -135,16 +144,26 @@ public class CoreEipAnnotationProcessorHelper {
 
         // get endpoint information which is divided into paths and options
         // (though there should really only be one path)
-        Set<EipOption> eipOptions = new TreeSet<>(new EipOptionComparator(eipModel));
+        Set<EipOptionModel> eipOptions = new TreeSet<>(new EipOptionComparator(eipModel));
         findClassProperties(processingEnv, writer, roundEnv, eipOptions, classElement, classElement, "", modelName);
 
+        eipOptions.forEach(eipModel::addOption);
+
         // after we have found all the options then figure out if the model
         // accepts input/output
         eipModel.setInput(hasInput(processingEnv, roundEnv, classElement));
-        eipModel.setOutput(hasOutput(eipModel, eipOptions));
+        eipModel.setOutput(hasOutput(eipModel));
+
+        if (Strings.isNullOrEmpty(eipModel.getTitle())) {
+            eipModel.setTitle(Strings.asTitle(eipModel.getName()));
+        }
+        if (eipModel.isOutput()) {
+            // filter out outputs if we do not support it
+            eipModel.getOptions().removeIf(o -> "outputs".equals(o.getName()));
+        }
 
         // write json schema file
-        String json = createParameterJsonSchema(eipModel, eipOptions);
+        String json = EipModel.createParameterJsonSchema(eipModel);
         writer.println(json);
 
         // generate property placeholder provider java source code
@@ -152,100 +171,38 @@ public class CoreEipAnnotationProcessorHelper {
     }
 
     protected void generatePropertyPlaceholderProviderSource(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement,
-                                                             EipModel eipModel, Set<EipOption> options, Set<String> propertyPlaceholderDefinitions) {
+                                                             EipModel eipModel, Set<EipOptionModel> options, Set<String> propertyPlaceholderDefinitions) {
 
         // not ever model classes support property placeholders as this has been limited to mainly Camel routes
         // so filter out unwanted models
         boolean rest = classElement.getQualifiedName().toString().startsWith("org.apache.camel.model.rest");
-        boolean processor = hasSuperClass(processingEnv, roundEnv, classElement, "org.apache.camel.model.ProcessorDefinition");
-        boolean language = hasSuperClass(processingEnv, roundEnv, classElement, "org.apache.camel.model.language.ExpressionDefinition");
-        boolean dataformat = hasSuperClass(processingEnv, roundEnv, classElement, "org.apache.camel.model.DataFormatDefinition");
+        boolean processor = AnnotationProcessorHelper.hasSuperClass(processingEnv, roundEnv, classElement, "org.apache.camel.model.ProcessorDefinition");
+        boolean language = AnnotationProcessorHelper.hasSuperClass(processingEnv, roundEnv, classElement, "org.apache.camel.model.language.ExpressionDefinition");
+        boolean dataformat = AnnotationProcessorHelper.hasSuperClass(processingEnv, roundEnv, classElement, "org.apache.camel.model.DataFormatDefinition");
 
         if (!rest && !processor && !language && !dataformat) {
             return;
         }
 
-        TypeElement parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.PropertyPlaceholderConfigurer");
-        String def = classElement.getSimpleName().toString();
+        TypeElement parent = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.PropertyPlaceholderConfigurer");
         String fqnDef = classElement.getQualifiedName().toString();
-        String cn = def + "PropertyPlaceholderProvider";
-        String fqn = "org.apache.camel.model.placeholder." + cn;
 
-        PropertyPlaceholderGenerator.generatePropertyPlaceholderProviderSource(processingEnv, parent, def, fqnDef, cn, fqn, options);
+        generatePropertyPlaceholderProviderSource(processingEnv, parent, fqnDef, options);
         propertyPlaceholderDefinitions.add(fqnDef);
 
         // we also need to generate from when we generate route as from can also configure property placeholders
-        if ("RouteDefinition".equals(def)) {
-            def = "FromDefinition";
+        if (fqnDef.equals("org.apache.camel.model.RouteDefinition")) {
             fqnDef = "org.apache.camel.model.FromDefinition";
-            cn = "FromDefinitionPropertyPlaceholderProvider";
-            fqn = "org.apache.camel.model.placeholder.FromDefinitionPropertyPlaceholderProvider";
 
             options.clear();
-            options.add(new EipOption("id", null, null, "java.lang.String", false, null, null, false, null, false, null, false, null, false));
-            options.add(new EipOption("uri", null, null, "java.lang.String", false, null, null, false, null, false, null, false, null, false));
+            options.add(createOption("id", null, null, "java.lang.String", false, null, null, false, null, false, null, false, null, false));
+            options.add(createOption("uri", null, null, "java.lang.String", false, null, null, false, null, false, null, false, null, false));
 
-            PropertyPlaceholderGenerator.generatePropertyPlaceholderProviderSource(processingEnv, parent, def, fqnDef, cn, fqn, options);
+            generatePropertyPlaceholderProviderSource(processingEnv, parent, fqnDef, options);
             propertyPlaceholderDefinitions.add(fqnDef);
         }
     }
 
-    public String createParameterJsonSchema(EipModel eipModel, Set<EipOption> options) {
-        StringBuilder buffer = new StringBuilder("{");
-        // eip model
-        buffer.append("\n \"model\": {");
-        buffer.append("\n    \"kind\": \"").append("model").append("\",");
-        buffer.append("\n    \"name\": \"").append(eipModel.getName()).append("\",");
-        if (eipModel.getTitle() != null) {
-            buffer.append("\n    \"title\": \"").append(eipModel.getTitle()).append("\",");
-        } else {
-            // fallback and use name as title
-            buffer.append("\n    \"title\": \"").append(asTitle(eipModel.getName())).append("\",");
-        }
-        buffer.append("\n    \"description\": \"").append(safeNull(eipModel.getDescription())).append("\",");
-        if (eipModel.getFirstVersion() != null) {
-            buffer.append("\n    \"firstVersion\": \"").append(safeNull(eipModel.getFirstVersion())).append("\",");
-        }
-        buffer.append("\n    \"javaType\": \"").append(eipModel.getJavaType()).append("\",");
-        buffer.append("\n    \"label\": \"").append(safeNull(eipModel.getLabel())).append("\",");
-        buffer.append("\n    \"deprecated\": ").append(eipModel.isDeprecated()).append(",");
-        if (eipModel.getDeprecationNote() != null) {
-            buffer.append("\n    \"deprecationNote\": \"").append(safeNull(eipModel.getDeprecationNote())).append("\",");
-        }
-        buffer.append("\n    \"input\": ").append(eipModel.getInput()).append(",");
-        buffer.append("\n    \"output\": ").append(eipModel.getOutput());
-        buffer.append("\n  },");
-
-        buffer.append("\n  \"properties\": {");
-        boolean first = true;
-
-        if ("false".equals(eipModel.getOutput())) {
-            // filter out outputs if we do not support it (and preserve order so
-            // we need to use linked hash-set)
-            options = options.stream().filter(o -> !"outputs".equals(o.getName())).collect(Collectors.toCollection(LinkedHashSet::new));
-        }
-
-        for (EipOption entry : options) {
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-
-            // as its json we need to sanitize the docs
-            String doc = entry.getDocumentation();
-            doc = sanitizeDescription(doc, false);
-            buffer.append(JSonSchemaHelper.toJson(entry.getName(), entry.getDisplayName(), entry.getKind(), entry.isRequired(), entry.getType(), entry.getDefaultValue(), doc,
-                                                  entry.isDeprecated(), entry.getDeprecationNote(), false, null, null, entry.isEnumType(), entry.getEnums(), entry.isOneOf(),
-                                                  entry.getOneOfTypes(), entry.isAsPredicate(), null, null, false, null, null));
-        }
-        buffer.append("\n  }");
-
-        buffer.append("\n}\n");
-        return buffer.toString();
-    }
-
     protected EipModel findEipModelProperties(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement, String javaTypeName, String name) {
         EipModel model = new EipModel();
         model.setJavaType(javaTypeName);
@@ -270,13 +227,13 @@ public class CoreEipAnnotationProcessorHelper {
         // favor to use class javadoc of component as description
         if (model.getJavaType() != null) {
             Elements elementUtils = processingEnv.getElementUtils();
-            TypeElement typeElement = findTypeElement(processingEnv, roundEnv, model.getJavaType());
+            TypeElement typeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, model.getJavaType());
             if (typeElement != null) {
                 String doc = elementUtils.getDocComment(typeElement);
                 if (doc != null) {
                     // need to sanitize the description first (we only want a
                     // summary)
-                    doc = sanitizeDescription(doc, true);
+                    doc = JSonSchemaHelper.sanitizeDescription(doc, true);
                     // the javadoc may actually be empty, so only change the doc
                     // if we got something
                     if (!Strings.isNullOrEmpty(doc)) {
@@ -289,7 +246,7 @@ public class CoreEipAnnotationProcessorHelper {
         return model;
     }
 
-    protected void findClassProperties(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv, Set<EipOption> eipOptions, TypeElement originalClassType,
+    protected void findClassProperties(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv, Set<EipOptionModel> eipOptions, TypeElement originalClassType,
                                        TypeElement classElement, String prefix, String modelName) {
         while (true) {
             List<VariableElement> fieldElements = ElementFilter.fieldsIn(classElement.getEnclosedElements());
@@ -357,8 +314,8 @@ public class CoreEipAnnotationProcessorHelper {
             TypeElement baseTypeElement = null;
             TypeMirror superclass = classElement.getSuperclass();
             if (superclass != null) {
-                String superClassName = canonicalClassName(superclass.toString());
-                baseTypeElement = findTypeElement(processingEnv, roundEnv, superClassName);
+                String superClassName = Strings.canonicalClassName(superclass.toString());
+                baseTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, superClassName);
             }
             if (baseTypeElement != null) {
                 classElement = baseTypeElement;
@@ -369,11 +326,11 @@ public class CoreEipAnnotationProcessorHelper {
     }
 
     private boolean processAttribute(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement,
-                                     VariableElement fieldElement, String fieldName, XmlAttribute attribute, Set<EipOption> eipOptions, String prefix, String modelName) {
+                                     VariableElement fieldElement, String fieldName, XmlAttribute attribute, Set<EipOptionModel> eipOptions, String prefix, String modelName) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         String name = attribute.name();
-        if (isNullOrEmpty(name) || "##default".equals(name)) {
+        if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
             name = fieldName;
         }
 
@@ -392,13 +349,13 @@ public class CoreEipAnnotationProcessorHelper {
         name = prefix + name;
         TypeMirror fieldType = fieldElement.asType();
         String fieldTypeName = fieldType.toString();
-        TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+        TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
         if (metadata != null && !Strings.isNullOrEmpty(metadata.javaType())) {
             fieldTypeName = metadata.javaType();
         }
 
         String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
-        String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+        String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
         boolean required = attribute.required();
         // metadata may overrule element required
         required = findRequired(fieldElement, required);
@@ -415,7 +372,7 @@ public class CoreEipAnnotationProcessorHelper {
         } else {
             isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
             if (isEnum) {
-                TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
                 if (enumClass != null) {
                     // find all the enum constants which has the possible enum
                     // value that can be used
@@ -440,7 +397,7 @@ public class CoreEipAnnotationProcessorHelper {
             deprecationNote = metadata.deprecationNote();
         }
 
-        EipOption ep = new EipOption(name, displayName, "attribute", fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, false, null,
+        EipOptionModel ep = createOption(name, displayName, "attribute", fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, false, null,
                                      false);
         eipOptions.add(ep);
 
@@ -448,7 +405,7 @@ public class CoreEipAnnotationProcessorHelper {
     }
 
     private void processValue(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement, VariableElement fieldElement,
-                              String fieldName, XmlValue value, Set<EipOption> eipOptions, String prefix, String modelName) {
+                              String fieldName, XmlValue value, Set<EipOptionModel> eipOptions, String prefix, String modelName) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         // XmlValue has no name attribute
@@ -467,7 +424,7 @@ public class CoreEipAnnotationProcessorHelper {
         String fieldTypeName = fieldType.toString();
 
         String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
-        String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+        String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
         boolean required = true;
         // metadata may overrule element required
         required = findRequired(fieldElement, required);
@@ -483,12 +440,12 @@ public class CoreEipAnnotationProcessorHelper {
             deprecationNote = metadata.deprecationNote();
         }
 
-        EipOption ep = new EipOption(name, displayName, "value", fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, false, null, false, null, false);
+        EipOptionModel ep = createOption(name, displayName, "value", fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, false, null, false, null, false);
         eipOptions.add(ep);
     }
 
     private void processElement(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement, XmlElement element, VariableElement fieldElement,
-                                Set<EipOption> eipOptions, String prefix) {
+                                Set<EipOptionModel> eipOptions, String prefix) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         String fieldName;
@@ -499,16 +456,16 @@ public class CoreEipAnnotationProcessorHelper {
 
             String kind = "element";
             String name = element.name();
-            if (isNullOrEmpty(name) || "##default".equals(name)) {
+            if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
                 name = fieldName;
             }
             name = prefix + name;
             TypeMirror fieldType = fieldElement.asType();
             String fieldTypeName = fieldType.toString();
-            TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+            TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
 
             String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
             boolean required = element.required();
             // metadata may overrule element required
             required = findRequired(fieldElement, required);
@@ -532,7 +489,7 @@ public class CoreEipAnnotationProcessorHelper {
             } else {
                 isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
                 if (isEnum) {
-                    TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                    TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
                     if (enumClass != null) {
                         // find all the enum constants which has the possible
                         // enum value that can be used
@@ -556,12 +513,12 @@ public class CoreEipAnnotationProcessorHelper {
                 kind = "expression";
                 for (String language : ONE_OF_LANGUAGES) {
                     fieldTypeName = language;
-                    TypeElement languages = findTypeElement(processingEnv, roundEnv, language);
+                    TypeElement languages = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, language);
                     if (languages != null) {
-                        String superClassName = canonicalClassName(languages.toString());
+                        String superClassName = Strings.canonicalClassName(languages.toString());
                         // find all classes that has that superClassName
                         Set<TypeElement> children = new LinkedHashSet<>();
-                        findTypeElementChildren(processingEnv, roundEnv, children, superClassName);
+                        AnnotationProcessorHelper.findTypeElementChildren(processingEnv, roundEnv, children, superClassName);
                         for (TypeElement child : children) {
                             XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
                             if (rootElement != null) {
@@ -588,14 +545,14 @@ public class CoreEipAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, isOneOf,
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, isOneOf,
                                          oneOfTypes, asPredicate);
             eipOptions.add(ep);
         }
     }
 
     private void processElements(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement, XmlElements elements, VariableElement fieldElement,
-                                 Set<EipOption> eipOptions, String prefix) {
+                                 Set<EipOptionModel> eipOptions, String prefix) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         String fieldName;
@@ -609,7 +566,7 @@ public class CoreEipAnnotationProcessorHelper {
             String fieldTypeName = fieldType.toString();
 
             String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
 
             boolean required = true;
             required = findRequired(fieldElement, required);
@@ -632,89 +589,89 @@ public class CoreEipAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, false, null, true, oneOfTypes,
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, false, null, true, oneOfTypes,
                                          false);
             eipOptions.add(ep);
         }
     }
 
-    private void processRoute(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement, Set<EipOption> eipOptions,
+    private void processRoute(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement, Set<EipOptionModel> eipOptions,
                               String prefix) {
 
         Elements elementUtils = processingEnv.getElementUtils();
 
         // group
-        String docComment = findJavaDoc(elementUtils, null, "group", null, classElement, true);
-        EipOption ep = new EipOption("group", "Group", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "group", null, classElement, true);
+        EipOptionModel ep = createOption("group", "Group", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // group
-        docComment = findJavaDoc(elementUtils, null, "streamCache", null, classElement, true);
-        ep = new EipOption("streamCache", "Stream Cache", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "streamCache", null, classElement, true);
+        ep = createOption("streamCache", "Stream Cache", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // trace
-        docComment = findJavaDoc(elementUtils, null, "trace", null, classElement, true);
-        ep = new EipOption("trace", "Trace", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "trace", null, classElement, true);
+        ep = createOption("trace", "Trace", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // message history
-        docComment = findJavaDoc(elementUtils, null, "messageHistory", null, classElement, true);
-        ep = new EipOption("messageHistory", "Message History", "attribute", "java.lang.String", false, "true", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "messageHistory", null, classElement, true);
+        ep = createOption("messageHistory", "Message History", "attribute", "java.lang.String", false, "true", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // log mask
-        docComment = findJavaDoc(elementUtils, null, "logMask", null, classElement, true);
-        ep = new EipOption("logMask", "Log Mask", "attribute", "java.lang.String", false, "false", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "logMask", null, classElement, true);
+        ep = createOption("logMask", "Log Mask", "attribute", "java.lang.String", false, "false", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // delayer
-        docComment = findJavaDoc(elementUtils, null, "delayer", null, classElement, true);
-        ep = new EipOption("delayer", "Delayer", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "delayer", null, classElement, true);
+        ep = createOption("delayer", "Delayer", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // autoStartup
-        docComment = findJavaDoc(elementUtils, null, "autoStartup", null, classElement, true);
-        ep = new EipOption("autoStartup", "Auto Startup", "attribute", "java.lang.String", false, "true", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "autoStartup", null, classElement, true);
+        ep = createOption("autoStartup", "Auto Startup", "attribute", "java.lang.String", false, "true", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // startupOrder
-        docComment = findJavaDoc(elementUtils, null, "startupOrder", null, classElement, true);
-        ep = new EipOption("startupOrder", "Startup Order", "attribute", "java.lang.Integer", false, "", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "startupOrder", null, classElement, true);
+        ep = createOption("startupOrder", "Startup Order", "attribute", "java.lang.Integer", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // errorHandlerRef
-        docComment = findJavaDoc(elementUtils, null, "errorHandlerRef", null, classElement, true);
-        ep = new EipOption("errorHandlerRef", "Error Handler", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "errorHandlerRef", null, classElement, true);
+        ep = createOption("errorHandlerRef", "Error Handler", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // routePolicyRef
-        docComment = findJavaDoc(elementUtils, null, "routePolicyRef", null, classElement, true);
-        ep = new EipOption("routePolicyRef", "Route Policy", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "routePolicyRef", null, classElement, true);
+        ep = createOption("routePolicyRef", "Route Policy", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // shutdownRoute
         Set<String> enums = new LinkedHashSet<>();
         enums.add("Default");
         enums.add("Defer");
-        docComment = findJavaDoc(elementUtils, null, "shutdownRoute", "Default", classElement, true);
-        ep = new EipOption("shutdownRoute", "Shutdown Route", "attribute", "org.apache.camel.ShutdownRoute", false, "", docComment, false, null, true, enums, false, null, false);
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "shutdownRoute", "Default", classElement, true);
+        ep = createOption("shutdownRoute", "Shutdown Route", "attribute", "org.apache.camel.ShutdownRoute", false, "", docComment, false, null, true, enums, false, null, false);
         eipOptions.add(ep);
 
         // shutdownRunningTask
         enums = new LinkedHashSet<>();
         enums.add("CompleteCurrentTaskOnly");
         enums.add("CompleteAllTasks");
-        docComment = findJavaDoc(elementUtils, null, "shutdownRunningTask", "CompleteCurrentTaskOnly", classElement, true);
-        ep = new EipOption("shutdownRunningTask", "Shutdown Running Task", "attribute", "org.apache.camel.ShutdownRunningTask", false, "", docComment, false, null, true, enums,
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "shutdownRunningTask", "CompleteCurrentTaskOnly", classElement, true);
+        ep = createOption("shutdownRunningTask", "Shutdown Running Task", "attribute", "org.apache.camel.ShutdownRunningTask", false, "", docComment, false, null, true, enums,
                            false, null, false);
         eipOptions.add(ep);
 
         // input
         Set<String> oneOfTypes = new TreeSet<>();
         oneOfTypes.add("from");
-        docComment = findJavaDoc(elementUtils, null, "input", null, classElement, true);
-        ep = new EipOption("input", "Input", "element", "org.apache.camel.model.FromDefinition", true, "", docComment, false, null, false, null, true, oneOfTypes,
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "input", null, classElement, true);
+        ep = createOption("input", "Input", "element", "org.apache.camel.model.FromDefinition", true, "", docComment, false, null, false, null, true, oneOfTypes,
                            false);
         eipOptions.add(ep);
 
@@ -724,7 +681,7 @@ public class CoreEipAnnotationProcessorHelper {
         // find all classes that has that superClassName
         Set<TypeElement> children = new LinkedHashSet<>();
         for (String superclass : ONE_OF_OUTPUTS) {
-            findTypeElementChildren(processingEnv, roundEnv, children, superclass);
+            AnnotationProcessorHelper.findTypeElementChildren(processingEnv, roundEnv, children, superclass);
         }
         for (TypeElement child : children) {
             XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
@@ -737,8 +694,8 @@ public class CoreEipAnnotationProcessorHelper {
         // remove some types which are not intended as an output in eips
         oneOfTypes.remove("route");
 
-        docComment = findJavaDoc(elementUtils, null, "outputs", null, classElement, true);
-        ep = new EipOption("outputs", "Outputs", "element", "java.util.List<org.apache.camel.model.ProcessorDefinition<?>>", true, "", docComment, false, null, false, null, true,
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "outputs", null, classElement, true);
+        ep = createOption("outputs", "Outputs", "element", "java.util.List<org.apache.camel.model.ProcessorDefinition<?>>", true, "", docComment, false, null, false, null, true,
                            oneOfTypes, false);
         eipOptions.add(ep);
     }
@@ -747,26 +704,26 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for process the OptionalIdentifiedDefinition
      */
     private void processIdentified(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement,
-                                   Set<EipOption> eipOptions, String prefix) {
+                                   Set<EipOptionModel> eipOptions, String prefix) {
 
         Elements elementUtils = processingEnv.getElementUtils();
 
         // id
-        String docComment = findJavaDoc(elementUtils, null, "id", null, classElement, true);
-        EipOption ep = new EipOption("id", "Id", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+        String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "id", null, classElement, true);
+        EipOptionModel ep = createOption("id", "Id", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
         eipOptions.add(ep);
 
         // description
-        docComment = findJavaDoc(elementUtils, null, "description", null, classElement, true);
-        ep = new EipOption("description", "Description", "element", "org.apache.camel.model.DescriptionDefinition", false, "", docComment, false, null, false, null, false, null,
+        docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "description", null, classElement, true);
+        ep = createOption("description", "Description", "element", "org.apache.camel.model.DescriptionDefinition", false, "", docComment, false, null, false, null, false, null,
                            false);
         eipOptions.add(ep);
 
         // lets skip custom id as it has no value for end users to configure
         if (!skipUnwanted) {
             // custom id
-            docComment = findJavaDoc(elementUtils, null, "customId", null, classElement, true);
-            ep = new EipOption("customId", "Custom Id", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
+            docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, null, "customId", null, classElement, true);
+            ep = createOption("customId", "Custom Id", "attribute", "java.lang.String", false, "", docComment, false, null, false, null, false, null, false);
             eipOptions.add(ep);
         }
     }
@@ -775,7 +732,7 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for processing an @XmlElementRef routes field
      */
     private void processRoutes(RoundEnvironment roundEnv, TypeElement originalClassType, XmlElementRef elementRef, VariableElement fieldElement, String fieldName,
-                               Set<EipOption> eipOptions, String prefix) {
+                               Set<EipOptionModel> eipOptions, String prefix) {
         if ("routes".equals(fieldName)) {
 
             TypeMirror fieldType = fieldElement.asType();
@@ -784,7 +741,7 @@ public class CoreEipAnnotationProcessorHelper {
             Set<String> oneOfTypes = new TreeSet<>();
             oneOfTypes.add("route");
 
-            EipOption ep = new EipOption("routes", "Routes", "element", fieldTypeName, false, "", "Contains the Camel routes", false, null, false, null, true, oneOfTypes, false);
+            EipOptionModel ep = createOption("routes", "Routes", "element", fieldTypeName, false, "", "Contains the Camel routes", false, null, false, null, true, oneOfTypes, false);
             eipOptions.add(ep);
         }
     }
@@ -793,7 +750,7 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for processing an @XmlElementRef rests field
      */
     private void processRests(RoundEnvironment roundEnv, TypeElement originalClassType, XmlElementRef elementRef, VariableElement fieldElement, String fieldName,
-                              Set<EipOption> eipOptions, String prefix) {
+                              Set<EipOptionModel> eipOptions, String prefix) {
         if ("rests".equals(fieldName)) {
 
             TypeMirror fieldType = fieldElement.asType();
@@ -802,7 +759,7 @@ public class CoreEipAnnotationProcessorHelper {
             Set<String> oneOfTypes = new TreeSet<>();
             oneOfTypes.add("rest");
 
-            EipOption ep = new EipOption("rests", "Rests", "element", fieldTypeName, false, "", "Contains the rest services defined using the rest-dsl", false, null, false, null,
+            EipOptionModel ep = createOption("rests", "Rests", "element", fieldTypeName, false, "", "Contains the rest services defined using the rest-dsl", false, null, false, null,
                                          true, oneOfTypes, false);
             eipOptions.add(ep);
         }
@@ -812,11 +769,11 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for processing an @XmlElementRef outputs field
      */
     private void processOutputs(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, XmlElementRef elementRef,
-                                VariableElement fieldElement, String fieldName, Set<EipOption> eipOptions, String prefix) {
+                                VariableElement fieldElement, String fieldName, Set<EipOptionModel> eipOptions, String prefix) {
         if ("outputs".equals(fieldName) && supportOutputs(processingEnv, roundEnv, originalClassType)) {
             String kind = "element";
             String name = elementRef.name();
-            if (isNullOrEmpty(name) || "##default".equals(name)) {
+            if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
                 name = fieldName;
             }
             name = prefix + name;
@@ -828,7 +785,7 @@ public class CoreEipAnnotationProcessorHelper {
             // find all classes that has that superClassName
             Set<TypeElement> children = new LinkedHashSet<>();
             for (String superclass : ONE_OF_OUTPUTS) {
-                findTypeElementChildren(processingEnv, roundEnv, children, superclass);
+                AnnotationProcessorHelper.findTypeElementChildren(processingEnv, roundEnv, children, superclass);
             }
             for (TypeElement child : children) {
                 XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
@@ -851,7 +808,7 @@ public class CoreEipAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, true, "", "", deprecated, deprecationNote, false, null, true, oneOfTypes, false);
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, true, "", "", deprecated, deprecationNote, false, null, true, oneOfTypes, false);
             eipOptions.add(ep);
         }
     }
@@ -860,28 +817,28 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for processing an @XmlElementRef verbs field (rest-dsl)
      */
     private void processVerbs(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, XmlElementRef elementRef, VariableElement fieldElement,
-                              String fieldName, Set<EipOption> eipOptions, String prefix) {
+                              String fieldName, Set<EipOptionModel> eipOptions, String prefix) {
 
         Elements elementUtils = processingEnv.getElementUtils();
 
         if ("verbs".equals(fieldName) && supportOutputs(processingEnv, roundEnv, originalClassType)) {
             String kind = "element";
             String name = elementRef.name();
-            if (isNullOrEmpty(name) || "##default".equals(name)) {
+            if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
                 name = fieldName;
             }
             name = prefix + name;
             TypeMirror fieldType = fieldElement.asType();
             String fieldTypeName = fieldType.toString();
 
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, originalClassType, true);
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, originalClassType, true);
 
             // gather oneOf which extends any of the output base classes
             Set<String> oneOfTypes = new TreeSet<>();
             // find all classes that has that superClassName
             Set<TypeElement> children = new LinkedHashSet<>();
             for (String superclass : ONE_OF_VERBS) {
-                findTypeElementChildren(processingEnv, roundEnv, children, superclass);
+                AnnotationProcessorHelper.findTypeElementChildren(processingEnv, roundEnv, children, superclass);
             }
             for (TypeElement child : children) {
                 XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
@@ -903,7 +860,7 @@ public class CoreEipAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, true, "", docComment, deprecated, deprecationNote, false, null, true, oneOfTypes, false);
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, true, "", docComment, deprecated, deprecationNote, false, null, true, oneOfTypes, false);
             eipOptions.add(ep);
         }
     }
@@ -912,13 +869,13 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for processing an @XmlElementRef expression field
      */
     private void processRefExpression(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement,
-                                      XmlElementRef elementRef, VariableElement fieldElement, String fieldName, Set<EipOption> eipOptions, String prefix) {
+                                      XmlElementRef elementRef, VariableElement fieldElement, String fieldName, Set<EipOptionModel> eipOptions, String prefix) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         if ("expression".equals(fieldName)) {
             String kind = "expression";
             String name = elementRef.name();
-            if (isNullOrEmpty(name) || "##default".equals(name)) {
+            if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
                 name = fieldName;
             }
             name = prefix + name;
@@ -928,7 +885,7 @@ public class CoreEipAnnotationProcessorHelper {
             // find javadoc from original class as it will override the
             // setExpression method where we can provide the javadoc for the
             // given EIP
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, originalClassType, true);
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, originalClassType, true);
 
             // is it used as predicate (check field first and then fallback to
             // its class / original class)
@@ -943,11 +900,11 @@ public class CoreEipAnnotationProcessorHelper {
             // gather oneOf expression/predicates which uses language
             Set<String> oneOfTypes = new TreeSet<>();
             for (String language : ONE_OF_LANGUAGES) {
-                TypeElement languages = findTypeElement(processingEnv, roundEnv, language);
-                String superClassName = canonicalClassName(languages.toString());
+                TypeElement languages = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, language);
+                String superClassName = Strings.canonicalClassName(languages.toString());
                 // find all classes that has that superClassName
                 Set<TypeElement> children = new LinkedHashSet<>();
-                findTypeElementChildren(processingEnv, roundEnv, children, superClassName);
+                AnnotationProcessorHelper.findTypeElementChildren(processingEnv, roundEnv, children, superClassName);
                 for (TypeElement child : children) {
                     XmlRootElement rootElement = child.getAnnotation(XmlRootElement.class);
                     if (rootElement != null) {
@@ -968,7 +925,7 @@ public class CoreEipAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, true, "", docComment, deprecated, deprecationNote, false, null, true, oneOfTypes, asPredicate);
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, true, "", docComment, deprecated, deprecationNote, false, null, true, oneOfTypes, asPredicate);
             eipOptions.add(ep);
         }
     }
@@ -977,13 +934,13 @@ public class CoreEipAnnotationProcessorHelper {
      * Special for processing an @XmlElementRef when field
      */
     private void processRefWhenClauses(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, XmlElementRef elementRef,
-                                       VariableElement fieldElement, String fieldName, Set<EipOption> eipOptions, String prefix) {
+                                       VariableElement fieldElement, String fieldName, Set<EipOptionModel> eipOptions, String prefix) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         if ("whenClauses".equals(fieldName)) {
             String kind = "element";
             String name = elementRef.name();
-            if (isNullOrEmpty(name) || "##default".equals(name)) {
+            if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
                 name = fieldName;
             }
             name = prefix + name;
@@ -993,7 +950,7 @@ public class CoreEipAnnotationProcessorHelper {
             // find javadoc from original class as it will override the
             // setExpression method where we can provide the javadoc for the
             // given EIP
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, originalClassType, true);
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, originalClassType, true);
 
             // indicate that this element is one of when
             Set<String> oneOfTypes = new HashSet<>();
@@ -1013,7 +970,7 @@ public class CoreEipAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, false, "", docComment, deprecated, deprecationNote, false, null, true, oneOfTypes, asPredicate);
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, false, "", docComment, deprecated, deprecationNote, false, null, true, oneOfTypes, asPredicate);
             eipOptions.add(ep);
         }
     }
@@ -1025,7 +982,7 @@ public class CoreEipAnnotationProcessorHelper {
      * have a outputs element.
      */
     private boolean supportOutputs(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement) {
-        return implementsInterface(processingEnv, roundEnv, classElement, "org.apache.camel.model.OutputNode");
+        return AnnotationProcessorHelper.implementsInterface(processingEnv, roundEnv, classElement, "org.apache.camel.model.OutputNode");
     }
 
     private String findDefaultValue(VariableElement fieldElement, String fieldTypeName) {
@@ -1054,275 +1011,52 @@ public class CoreEipAnnotationProcessorHelper {
         return defaultValue;
     }
 
-    /**
-     * Capitializes the name as a title
-     *
-     * @param name the name
-     * @return as a title
-     */
-    private static String asTitle(String name) {
-        StringBuilder sb = new StringBuilder();
-        for (char c : name.toCharArray()) {
-            boolean upper = Character.isUpperCase(c);
-            boolean first = sb.length() == 0;
-            if (first) {
-                sb.append(Character.toUpperCase(c));
-            } else if (upper) {
-                sb.append(' ');
-                sb.append(c);
-            } else {
-                sb.append(Character.toLowerCase(c));
-            }
-        }
-        return sb.toString().trim();
-    }
-
     private boolean hasInput(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement) {
         for (String name : ONE_OF_INPUTS) {
-            if (hasSuperClass(processingEnv, roundEnv, classElement, name)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean hasOutput(EipModel model, Set<EipOption> options) {
-        // if we are route/rest then we accept output
-        if ("route".equals(model.getName()) || "rest".equals(model.getName())) {
-            return true;
-        }
-
-        // special for transacted/policy which should not have output
-        if ("policy".equals(model.getName()) || "transacted".equals(model.getName())) {
-            return false;
-        }
-
-        for (EipOption option : options) {
-            if ("outputs".equals(option.getName())) {
+            if (AnnotationProcessorHelper.hasSuperClass(processingEnv, roundEnv, classElement, name)) {
                 return true;
             }
         }
         return false;
     }
 
-    public static final class EipModel {
-
-        private String name;
-        private String title;
-        private String javaType;
-        private String label;
-        private String description;
-        private boolean deprecated;
-        private String deprecationNote;
-        private boolean input;
-        private boolean output;
-        private String firstVersion;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getTitle() {
-            return title;
-        }
-
-        public void setTitle(String title) {
-            this.title = title;
-        }
-
-        public String getJavaType() {
-            return javaType;
-        }
-
-        public void setJavaType(String javaType) {
-            this.javaType = javaType;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-        public boolean isDeprecated() {
-            return deprecated;
-        }
-
-        public void setDeprecated(boolean deprecated) {
-            this.deprecated = deprecated;
-        }
-
-        public String getDeprecationNote() {
-            return deprecationNote;
-        }
-
-        public void setDeprecationNote(String deprecationNote) {
-            this.deprecationNote = deprecationNote;
-        }
-
-        public boolean isInput() {
-            return input;
-        }
-
-        public void setInput(boolean input) {
-            this.input = input;
-        }
-
-        public String getInput() {
-            return input ? "true" : "false";
-        }
-
-        public boolean isOutput() {
-            return output;
-        }
-
-        public void setOutput(boolean output) {
-            this.output = output;
-        }
-
-        public String getOutput() {
-            return output ? "true" : "false";
-        }
-
-        public String getFirstVersion() {
-            return firstVersion;
-        }
-
-        public void setFirstVersion(String firstVersion) {
-            this.firstVersion = firstVersion;
-        }
-    }
-
-    public static final class EipOption {
-
-        private String name;
-        private String displayName;
-        private String kind;
-        private String type;
-        private boolean required;
-        private String defaultValue;
-        private String documentation;
-        private boolean deprecated;
-        private String deprecationNote;
-        private boolean enumType;
-        private Set<String> enums;
-        private boolean oneOf;
-        private Set<String> oneOfTypes;
-        private boolean asPredicate;
-
-        private EipOption(String name, String displayName, String kind, String type, boolean required, String defaultValue, String documentation, boolean deprecated,
-                          String deprecationNote, boolean enumType, Set<String> enums, boolean oneOf, Set<String> oneOfTypes, boolean asPredicate) {
-            this.name = name;
-            this.displayName = displayName;
-            this.kind = kind;
-            this.type = type;
-            this.required = required;
-            this.defaultValue = defaultValue;
-            this.documentation = documentation;
-            this.deprecated = deprecated;
-            this.deprecationNote = deprecationNote;
-            this.enumType = enumType;
-            this.enums = enums;
-            this.oneOf = oneOf;
-            this.oneOfTypes = oneOfTypes;
-            this.asPredicate = asPredicate;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        public String getKind() {
-            return kind;
-        }
-
-        public String getType() {
-            return type;
-        }
-
-        public boolean isRequired() {
-            return required;
-        }
-
-        public String getDefaultValue() {
-            return defaultValue;
-        }
-
-        public String getDocumentation() {
-            return documentation;
-        }
-
-        public boolean isDeprecated() {
-            return deprecated;
-        }
-
-        public String getDeprecationNote() {
-            return deprecationNote;
-        }
-
-        public boolean isEnumType() {
-            return enumType;
-        }
-
-        public Set<String> getEnums() {
-            return enums;
-        }
-
-        public boolean isOneOf() {
-            return oneOf;
-        }
-
-        public Set<String> getOneOfTypes() {
-            return oneOfTypes;
-        }
-
-        public boolean isAsPredicate() {
-            return asPredicate;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
+    private boolean hasOutput(EipModel model) {
+        switch (model.getName()) {
+            // if we are route/rest then we accept output
+            case "route":
+            case "rest":
                 return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            EipOption that = (EipOption)o;
-
-            if (!name.equals(that.name)) {
+            // special for transacted/policy which should not have output
+            case "policy":
+            case "transacted":
                 return false;
-            }
-
-            return true;
+            default:
+                return model.getOptions().stream()
+                        .anyMatch(option -> "outputs".equals(option.getName()));
         }
+    }
 
-        @Override
-        public int hashCode() {
-            return name.hashCode();
-        }
+    private EipOptionModel createOption(String name, String displayName, String kind, String type, boolean required, String defaultValue, String description, boolean deprecated,
+                                        String deprecationNote, boolean enumType, Set<String> enums, boolean oneOf, Set<String> oneOfs, boolean asPredicate) {
+        EipOptionModel option = new EipOptionModel();
+        option.setName(name);
+        option.setDisplayName(Strings.isNullOrEmpty(displayName) ? Strings.asTitle(name) : displayName);
+        option.setKind(kind);
+        option.setRequired(required);
+        option.setDefaultValue("java.lang.Boolean".equals(type) && !Strings.isNullOrEmpty(defaultValue )
+                ? Boolean.parseBoolean(defaultValue) : defaultValue);
+        option.setDescription(JSonSchemaHelper.sanitizeDescription(description, false));
+        option.setDeprecated(deprecated);
+        option.setDeprecationNote(Strings.isNullOrEmpty(deprecationNote) ? null : deprecationNote);
+        option.setType(JSonSchemaHelper.getType(type, enumType));
+        option.setJavaType(type);
+        option.setEnums(enums != null && !enums.isEmpty() ? new ArrayList<>(enums) : null);
+        option.setOneOfs(oneOfs != null && !oneOfs.isEmpty() ? new ArrayList<>(oneOfs) : null);
+        option.setAsPredicate(asPredicate);
+        return option;
     }
 
-    private static final class EipOptionComparator implements Comparator<EipOption> {
+    private static final class EipOptionComparator implements Comparator<EipOptionModel> {
 
         private final EipModel model;
 
@@ -1331,7 +1065,7 @@ public class CoreEipAnnotationProcessorHelper {
         }
 
         @Override
-        public int compare(EipOption o1, EipOption o2) {
+        public int compare(EipOptionModel o1, EipOptionModel o2) {
             int weight = weight(o1);
             int weight2 = weight(o2);
 
@@ -1344,7 +1078,7 @@ public class CoreEipAnnotationProcessorHelper {
             }
         }
 
-        private int weight(EipOption o) {
+        private int weight(EipOptionModel o) {
             String name = o.getName();
 
             // these should be first
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java
deleted file mode 100644
index a94b585..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/DocumentationHelper.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.tools.apt;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.util.List;
-import java.util.Map;
-
-import static org.apache.camel.tooling.util.JSonSchemaHelper.parseJsonSchema;
-
-/**
- * Helper to find documentation for inherited options when a component extends another.
- */
-public final class DocumentationHelper {
-
-    private DocumentationHelper() {
-        //utility class, never constructed
-    }
-
-    public static String findComponentJavaDoc(String scheme, String extendsScheme, String fieldName) {
-        File file = jsonFile(scheme, extendsScheme);
-        if (file != null) {
-            try {
-                String json = loadText(file);
-                List<Map<String, String>> rows = parseJsonSchema("componentProperties", json, true);
-                return getPropertyDescription(rows, fieldName);
-            } catch (Exception e) {
-                // ignore
-            }
-        }
-
-        // not found
-        return null;
-    }
-
-    public static String findEndpointJavaDoc(String scheme, String extendsScheme, String fieldName) {
-        File file = jsonFile(scheme, extendsScheme);
-        if (file != null) {
-            try {
-                String json = loadText(file);
-                List<Map<String, String>> rows = parseJsonSchema("properties", json, true);
-                return getPropertyDescription(rows, fieldName);
-            } catch (Exception e) {
-                // ignore
-            }
-        }
-
-        // not found
-        return null;
-    }
-
-    private static String getPropertyDescription(List<Map<String, String>> rows, String name) {
-        for (Map<String, String> row : rows) {
-            String description = null;
-            boolean found = false;
-            if (row.containsKey("name")) {
-                found = name.equals(row.get("name"));
-            }
-            if (row.containsKey("description")) {
-                description = row.get("description");
-            }
-            if (found) {
-                return description;
-            }
-        }
-        return null;
-    }
-
-    private static File jsonFile(String scheme, String extendsScheme) {
-        // we cannot use classloader to load external resources from other JARs during apt plugin,
-        // so load these resources using the file system
-
-        if ("file".equals(extendsScheme)) {
-            return new File("../camel-file/target/classes/org/apache/camel/component/file/file.json");
-        } else if ("ahc".equals(extendsScheme)) {
-            return new File("../camel-ahc/target/classes/org/apache/camel/component/ahc/ahc.json");
-        } else if ("atom".equals(extendsScheme)) {
-            return new File("../camel-atom/target/classes/org/apache/camel/component/atom/atom.json");
-        } else if ("ftp".equals(extendsScheme)) {
-            return new File("../camel-ftp/target/classes/org/apache/camel/component/file/remote/ftp.json");
-        } else if ("jms".equals(extendsScheme)) {
-            return new File("../camel-jms/target/classes/org/apache/camel/component/jms/jms.json");
-        } else if ("sjms".equals(extendsScheme)) {
-            return new File("../camel-sjms/target/classes/org/apache/camel/component/sjms/sjms.json");
-        } else if ("http".equals(extendsScheme)) {
-            return new File("../camel-http/target/classes/org/apache/camel/component/http/http.json");
-        } else if ("https".equals(extendsScheme)) {
-            return new File("../camel-http/target/classes/org/apache/camel/component/http/https.json");
-        } else if ("netty".equals(extendsScheme)) {
-            return new File("../camel-netty/target/classes/org/apache/camel/component/netty/netty.json");
-        } else if ("servlet".equals(extendsScheme)) {
-            return new File("../camel-servlet/target/classes/org/apache/camel/component/servlet/servlet.json");
-        }
-        // not found
-        return null;
-    }
-
-    /**
-     * Loads the entire stream into memory as a String and returns it.
-     * <p/>
-     * <b>Notice:</b> This implementation appends a <tt>\n</tt> as line
-     * terminator at the of the text.
-     * <p/>
-     * Warning, don't use for crazy big streams :)
-     */
-    private static String loadText(File file) throws IOException {
-        StringBuilder builder = new StringBuilder();
-        Files.readAllLines(file.toPath()).forEach(line -> {
-            builder.append(line);
-            builder.append("\n");
-        });
-        return builder.toString();
-    }
-
-}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
index 34055bb..137aee0 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/EndpointAnnotationProcessor.java
@@ -16,17 +16,21 @@
  */
 package org.apache.camel.tools.apt;
 
+import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.ArrayList;
+import java.io.StringReader;
+import java.io.Writer;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
+import java.util.Properties;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.lang.model.element.Element;
@@ -39,6 +43,7 @@ import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.ElementFilter;
 import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic;
 import javax.tools.Diagnostic.Kind;
 import javax.tools.FileObject;
 import javax.tools.StandardLocation;
@@ -49,27 +54,17 @@ import org.apache.camel.spi.UriParam;
 import org.apache.camel.spi.UriParams;
 import org.apache.camel.spi.UriPath;
 import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.tooling.model.BaseModel;
+import org.apache.camel.tooling.model.BaseOptionModel;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.ComponentModel.ComponentOptionModel;
+import org.apache.camel.tooling.model.ComponentModel.EndpointOptionModel;
 import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.camel.tools.apt.helper.EndpointHelper;
-import org.apache.camel.tools.apt.model.ComponentModel;
-import org.apache.camel.tools.apt.model.ComponentOption;
-import org.apache.camel.tools.apt.model.EndpointOption;
-import org.apache.camel.tools.apt.model.EndpointPath;
-import org.apache.camel.tools.apt.model.PropertyOption;
-import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
 
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findFieldElement;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findJavaDoc;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findTypeElement;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.implementsInterface;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.processFile;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.sanitizeDescription;
-import static org.apache.camel.tooling.util.Strings.canonicalClassName;
-import static org.apache.camel.tooling.util.Strings.getOrElse;
-import static org.apache.camel.tooling.util.Strings.isNullOrEmpty;
-
 /**
  * Processes all Camel {@link UriEndpoint}s and generate json schema documentation for the endpoint/component.
  */
@@ -81,7 +76,7 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
     private static final String HEADER_FILTER_STRATEGY_JAVADOC = "To use a custom HeaderFilterStrategy to filter header to and from Camel message.";
 
     @Override
-    protected void doProcess(Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) throws Exception {
+    protected void doProcess(Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
         Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(UriEndpoint.class);
         for (Element element : elements) {
             if (element instanceof TypeElement) {
@@ -98,7 +93,7 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
             String title = uriEndpoint.title();
             final String label = uriEndpoint.label();
             validateSchemaName(scheme, classElement);
-            if (!isNullOrEmpty(scheme)) {
+            if (!Strings.isNullOrEmpty(scheme)) {
                 // support multiple schemes separated by comma, which maps to the exact same component
                 // for example camel-mail has a bunch of component schema names that does that
                 String[] schemes = scheme.split(",");
@@ -116,10 +111,10 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     final String aliasTitle = aTitle;
 
                     // write json schema
-                    String name = canonicalClassName(classElement.getQualifiedName().toString());
+                    String name = Strings.canonicalClassName(classElement.getQualifiedName().toString());
                     String packageName = name.substring(0, name.lastIndexOf("."));
-                    String fileName = alias + ".json";
-                    processFile(processingEnv, packageName, fileName,
+                    String fileName = alias + PackageHelper.JSON_SUFIX;
+                    AnnotationProcessorHelper.processFile(processingEnv, packageName, fileName,
                             writer -> writeJSonSchemeAndPropertyConfigurer(writer, roundEnv, classElement, uriEndpoint, aliasTitle, alias, extendsAlias, label, schemes));
                 }
             }
@@ -136,69 +131,135 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
     protected void writeJSonSchemeAndPropertyConfigurer(PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint,
                                                         String title, String scheme, String extendsScheme, String label, String[] schemes) {
         // gather component information
-        ComponentModel componentModel = findComponentProperties(roundEnv, uriEndpoint, classElement, title, scheme, extendsScheme, label);
+        ComponentModel componentModel = findComponentProperties(roundEnv, uriEndpoint, classElement, title, scheme, extendsScheme, label, schemes);
 
         // get endpoint information which is divided into paths and options (though there should really only be one path)
-        Set<EndpointPath> endpointPaths = new LinkedHashSet<>();
-        Set<EndpointOption> endpointOptions = new LinkedHashSet<>();
-        Set<ComponentOption> componentOptions = new LinkedHashSet<>();
-
-        JsonObject parentData = null;
+        ComponentModel parentData = null;
+        String parentScheme = isNullOrEmpty(extendsScheme) ? null : extendsScheme;
         TypeMirror superclass = classElement.getSuperclass();
         if (superclass != null) {
-            String superClassName = canonicalClassName(superclass.toString());
-            TypeElement baseTypeElement = findTypeElement(processingEnv, roundEnv, superClassName);
+            String superClassName = Strings.canonicalClassName(superclass.toString());
+            TypeElement baseTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, superClassName);
             if (baseTypeElement != null && !roundEnv.getRootElements().contains(baseTypeElement)) {
                 UriEndpoint parentUriEndpoint = baseTypeElement.getAnnotation(UriEndpoint.class);
                 if (parentUriEndpoint != null) {
-                    String parentScheme = parentUriEndpoint.scheme().split(",")[0];
-                    String packageName = superClassName.substring(0, superClassName.lastIndexOf("."));
-                    String fileName = parentScheme + ".json";
-                    try {
-                        FileObject res = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, packageName, fileName);
-                        String json = res.getCharContent(false).toString();
-                        parentData = Jsoner.deserialize(json, (JsonObject) null);
-                    } catch (Exception e) {
-                        // ignore
-                        throw new RuntimeException("Error: " + e.toString(), e);
-                    }
+                    parentScheme = parentUriEndpoint.scheme().split(",")[0];
                 }
             }
         }
-
-        // remove excluded properties from parent as we dont want to inherit them again
-        if (parentData != null && parentData.get("properties") != null) {
-            Map map = (Map<String, Object>) parentData.get("properties");
-            for (String exclude : uriEndpoint.excludeProperties().split(",")) {
-                map.remove(exclude);
+        if (parentScheme != null) {
+            try {
+                FileObject res = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH,
+                        "", "META-INF/services/org/apache/camel/component/" + parentScheme);
+                String propsStr = res.getCharContent(false).toString();
+                Properties props = new Properties();
+                props.load(new StringReader(propsStr));
+                String clazzName = props.getProperty("class");
+                String packageName = clazzName.substring(0, clazzName.lastIndexOf("."));
+                res = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH,
+                        packageName, parentScheme + PackageHelper.JSON_SUFIX);
+                String json = res.getCharContent(false).toString();
+                parentData = ComponentModel.generateComponentModel(json);
+            } catch (Exception e) {
+                // ignore
+                if (!Objects.equals(parentScheme, extendsScheme)) {
+                    throw new RuntimeException("Error: " + e.toString(), e);
+                }
             }
         }
 
         // component options
-        TypeElement componentClassElement = findTypeElement(processingEnv, roundEnv, componentModel.getJavaType());
+        TypeElement componentClassElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, componentModel.getJavaType());
         if (componentClassElement != null) {
-            findComponentClassProperties(writer, roundEnv, componentModel, componentOptions, componentClassElement, "", parentData, null, null);
+            findComponentClassProperties(roundEnv, componentModel, componentClassElement, "", parentData, null, null);
         }
+
+        // endpoint options
+        findClassProperties(roundEnv, componentModel, classElement, "", uriEndpoint.excludeProperties(), parentData, null, null);
+
+        // enhance and generate
+        enhanceComponentModel(componentModel, parentData, uriEndpoint.excludeProperties());
+
         // if the component has known class name
         if (!"@@@JAVATYPE@@@".equals(componentModel.getJavaType())) {
-            generateComponentConfigurer(roundEnv, uriEndpoint, scheme, schemes, componentModel, componentOptions);
+            generateComponentConfigurer(roundEnv, uriEndpoint, scheme, schemes, componentModel);
         }
 
-        // endpoint options
-        findClassProperties(writer, roundEnv, componentModel, endpointPaths, endpointOptions, classElement, "", uriEndpoint.excludeProperties(), parentData, null, null);
-        String json = createParameterJsonSchema(componentModel, componentOptions, endpointPaths, endpointOptions, schemes, parentData);
+        String json = ComponentModel.createParameterJsonSchema(componentModel);
         writer.println(json);
-        generateEndpointConfigurer(roundEnv, classElement, uriEndpoint, scheme, schemes, componentModel, endpointOptions);
+        generateEndpointConfigurer(roundEnv, classElement, uriEndpoint, scheme, schemes, componentModel);
+    }
+
+    private void enhanceComponentModel(ComponentModel componentModel, ComponentModel parentData, String excludeProperties) {
+        componentModel.getComponentOptions().removeIf(option -> filterOutOption(componentModel, option));
+        componentModel.getComponentOptions().forEach(option -> fixDoc(option, parentData != null ? parentData.getComponentOptions() : null));
+        componentModel.getEndpointOptions().removeIf(option -> filterOutOption(componentModel, option));
+        componentModel.getEndpointOptions().forEach(option -> fixDoc(option, parentData != null ? parentData.getEndpointOptions() : null));
+        componentModel.getEndpointOptions().sort(EndpointHelper.createOverallComparator(componentModel.getSyntax()));
+        // merge with parent, removing excluded and overriden properties
+        if (parentData != null) {
+            Set<String> componentOptionNames = componentModel.getComponentOptions().stream().map(BaseOptionModel::getName).collect(Collectors.toSet());
+            Set<String> endpointOptionNames = componentModel.getEndpointOptions().stream().map(BaseOptionModel::getName).collect(Collectors.toSet());
+            Collections.addAll(endpointOptionNames, excludeProperties.split(","));
+            parentData.getComponentOptions().removeIf(option -> componentOptionNames.contains(option.getName()));
+            parentData.getEndpointOptions().removeIf(option -> endpointOptionNames.contains(option.getName()));
+            componentModel.getComponentOptions().addAll(parentData.getComponentOptions());
+            componentModel.getEndpointOptions().addAll(parentData.getEndpointOptions());
+        }
+
+    }
+
+    private void fixDoc(BaseOptionModel option, List<? extends BaseOptionModel> parentOptions) {
+        String doc = getDocumentationWithNotes(option);
+        if (Strings.isNullOrEmpty(doc) && parentOptions != null) {
+            doc = parentOptions.stream()
+                    .filter(opt -> Objects.equals(opt.getName(), option.getName()))
+                    .map(BaseOptionModel::getDescription)
+                    .findFirst().orElse(null);
+        }
+        // as its json we need to sanitize the docs
+        doc = JSonSchemaHelper.sanitizeDescription(doc, false);
+        option.setDescription(doc);
+
+        if (isNullOrEmpty(doc)) {
+            throw new IllegalStateException("Empty doc for option: " + option.getName() + ", parent options:\n" +
+                    (parentOptions != null ? Jsoner.serialize(BaseModel.asJsonObject(parentOptions)) : "<null>"));
+        }
+    }
+
+    private boolean filterOutOption(ComponentModel component, BaseOptionModel option) {
+        String label = option.getLabel();
+        if (label != null) {
+            return component.isConsumerOnly() && label.contains("producer")
+                    || component.isProducerOnly() && label.contains("consumer");
+        } else {
+            return false;
+        }
+    }
+
+    public String getDocumentationWithNotes(BaseOptionModel option) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(option.getDescription());
+
+        if (!Strings.isNullOrEmpty(option.getDefaultValueNote())) {
+            sb.append(". Default value notice: ").append(option.getDefaultValueNote());
+        }
+
+        if (!Strings.isNullOrEmpty(option.getDeprecationNote())) {
+            sb.append(". Deprecation note: ").append(option.getDeprecationNote());
+        }
+
+        return sb.toString();
     }
 
     private void generateComponentConfigurer(RoundEnvironment roundEnv, UriEndpoint uriEndpoint, String scheme, String[] schemes,
-                                             ComponentModel componentModel, Set<ComponentOption> componentOptions) {
+                                             ComponentModel componentModel) {
         TypeElement parent;
         if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
             // special for activemq and amqp scheme which should reuse jms
-            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsComponentConfigurer");
+            parent = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsComponentConfigurer");
         } else {
-            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.GeneratedPropertyConfigurer");
+            parent = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.GeneratedPropertyConfigurer");
         }
         String fqComponentClassName = componentModel.getJavaType();
         String componentClassName = fqComponentClassName.substring(fqComponentClassName.lastIndexOf('.') + 1);
@@ -207,27 +268,27 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         String fqClassName = packageName + "." + className;
 
         if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
-            PropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName);
-            PropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-component", fqClassName);
-        } else if (uriEndpoint.generateConfigurer() && !componentOptions.isEmpty()) {
+            generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName, componentModel.getScheme() + "-component");
+        } else if (uriEndpoint.generateConfigurer() && !componentModel.getComponentOptions().isEmpty()) {
             // only generate this once for the first scheme
             if (schemes == null || schemes[0].equals(scheme)) {
-                Set<PropertyOption> set = new LinkedHashSet<>();
-                set.addAll(componentOptions);
-                PropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, componentClassName, set);
-                PropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-component", fqClassName);
+                generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, componentClassName, componentModel.getScheme() + "-component", componentModel.getComponentOptions());
             }
         }
     }
 
-    private void generateEndpointConfigurer(RoundEnvironment roundEnv, TypeElement classElement, UriEndpoint uriEndpoint, String scheme, String[] schemes,
-                                            ComponentModel componentModel, Set<EndpointOption> endpointOptions) {
+    private void generateEndpointConfigurer(RoundEnvironment roundEnv,
+                                            TypeElement classElement,
+                                            UriEndpoint uriEndpoint,
+                                            String scheme,
+                                            String[] schemes,
+                                            ComponentModel componentModel) {
         TypeElement parent;
         if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
             // special for activemq and amqp scheme which should reuse jms
-            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsEndpointConfigurer");
+            parent = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, "org.apache.camel.component.jms.JmsEndpointConfigurer");
         } else {
-            parent = findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.GeneratedPropertyConfigurer");
+            parent = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, "org.apache.camel.spi.GeneratedPropertyConfigurer");
         }
         String fqEndpointClassName = classElement.getQualifiedName().toString();
         String packageName = fqEndpointClassName.substring(0, fqEndpointClassName.lastIndexOf('.'));
@@ -236,247 +297,24 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         String fqClassName = packageName + "." + className;
 
         if ("activemq".equals(scheme) || "amqp".equals(scheme)) {
-            PropertyConfigurerGenerator.generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName);
-            PropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqClassName);
-        } else if (uriEndpoint.generateConfigurer() && !endpointOptions.isEmpty()) {
+            generateExtendConfigurer(processingEnv, parent, packageName, className, fqClassName, componentModel.getScheme() + "-endpoint");
+        } else if (uriEndpoint.generateConfigurer() && !componentModel.getComponentOptions().isEmpty()) {
             // only generate this once for the first scheme
             if (schemes == null || schemes[0].equals(scheme)) {
-                Set<PropertyOption> set = new LinkedHashSet<>();
-                set.addAll(endpointOptions);
-                PropertyConfigurerGenerator.generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, endpointClassName, set);
-                PropertyConfigurerGenerator.generateMetaInfConfigurer(processingEnv, componentModel.getScheme() + "-endpoint", fqClassName);
+                generatePropertyConfigurer(processingEnv, parent, packageName, className, fqClassName, endpointClassName, componentModel.getScheme() + "-endpoint", componentModel.getEndpointParameterOptions());
             }
         }
     }
 
-    public String createParameterJsonSchema(ComponentModel componentModel, Set<ComponentOption> componentOptions,
-                                            Set<EndpointPath> endpointPaths, Set<EndpointOption> endpointOptions, String[] schemes,
-                                            Map<String, Object> parentData) {
-        StringBuilder buffer = new StringBuilder("{");
-        // component model
-        buffer.append("\n \"component\": {");
-        buffer.append("\n    \"kind\": \"").append("component").append("\",");
-        buffer.append("\n    \"scheme\": \"").append(componentModel.getScheme()).append("\",");
-        if (!Strings.isNullOrEmpty(componentModel.getExtendsScheme())) {
-            buffer.append("\n    \"extendsScheme\": \"").append(componentModel.getExtendsScheme()).append("\",");
-        }
-        // the first scheme is the regular so only output if there is alternatives
+    protected ComponentModel findComponentProperties(RoundEnvironment roundEnv, UriEndpoint uriEndpoint, TypeElement endpointClassElement,
+                                                     String title, String scheme, String extendsScheme, String label, String[] schemes) {
+        ComponentModel model = new ComponentModel();
+        model.setScheme(scheme);
+        model.setExtendsScheme(extendsScheme);
+        // alternative schemes
         if (schemes != null && schemes.length > 1) {
-            buffer.append("\n    \"alternativeSchemes\": \"").append(String.join(",", schemes)).append("\",");
-        }
-        buffer.append("\n    \"syntax\": \"").append(componentModel.getSyntax()).append("\",");
-        if (componentModel.getAlternativeSyntax() != null) {
-            buffer.append("\n    \"alternativeSyntax\": \"").append(componentModel.getAlternativeSyntax()).append("\",");
-        }
-        buffer.append("\n    \"title\": \"").append(componentModel.getTitle()).append("\",");
-        buffer.append("\n    \"description\": \"").append(componentModel.getDescription()).append("\",");
-        buffer.append("\n    \"label\": \"").append(getOrElse(componentModel.getLabel(), "")).append("\",");
-        buffer.append("\n    \"deprecated\": ").append(componentModel.isDeprecated()).append(",");
-        buffer.append("\n    \"deprecationNote\": \"").append(getOrElse(componentModel.getDeprecationNote(), "")).append("\",");
-        buffer.append("\n    \"async\": ").append(componentModel.isAsync()).append(",");
-        buffer.append("\n    \"consumerOnly\": ").append(componentModel.isConsumerOnly()).append(",");
-        buffer.append("\n    \"producerOnly\": ").append(componentModel.isProducerOnly()).append(",");
-        buffer.append("\n    \"lenientProperties\": ").append(componentModel.isLenientProperties()).append(",");
-        buffer.append("\n    \"javaType\": \"").append(componentModel.getJavaType()).append("\",");
-        if (componentModel.getFirstVersion() != null) {
-            buffer.append("\n    \"firstVersion\": \"").append(componentModel.getFirstVersion()).append("\",");
-        }
-        buffer.append("\n    \"groupId\": \"").append(componentModel.getGroupId()).append("\",");
-        buffer.append("\n    \"artifactId\": \"").append(componentModel.getArtifactId()).append("\",");
-        if (componentModel.getVerifiers() != null) {
-            buffer.append("\n    \"verifiers\": \"").append(componentModel.getVerifiers()).append("\",");
-        }
-        buffer.append("\n    \"version\": \"").append(componentModel.getVersionId()).append("\"");
-
-        buffer.append("\n  },");
-
-        // and component properties
-        Map<String, Object> parentComponentProperties;
-        if (parentData != null && parentData.get("componentProperties") != null) {
-            parentComponentProperties = (Map<String, Object>) parentData.get("componentProperties");
-        } else {
-            parentComponentProperties = new HashMap<>();
+            model.setAlternativeSchemes(String.join(",", schemes));
         }
-        buffer.append("\n  \"componentProperties\": {");
-        boolean first = true;
-        for (ComponentOption entry : componentOptions) {
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-            // either we have the documentation from this apt plugin or we need help to find it from extended component
-            String doc = entry.getDocumentationWithNotes();
-            if (Strings.isNullOrEmpty(doc)) {
-                doc = DocumentationHelper.findComponentJavaDoc(componentModel.getScheme(), componentModel.getExtendsScheme(), entry.getName());
-            }
-            // as its json we need to sanitize the docs
-            doc = sanitizeDescription(doc, false);
-            Boolean required = entry.isRequired();
-            String defaultValue = entry.getDefaultValue();
-            if (Strings.isNullOrEmpty(defaultValue) && "boolean".equals(entry.getType())) {
-                // fallback as false for boolean types
-                defaultValue = "false";
-            }
-
-            // component options do not have prefix
-            String optionalPrefix = "";
-            String prefix = "";
-            boolean multiValue = false;
-            boolean asPredicate = false;
-
-            buffer.append(JSonSchemaHelper.toJson(entry.getName(), entry.getDisplayName(), "property", required, entry.getType(), defaultValue, doc,
-                entry.isDeprecated(), entry.getDeprecationNote(), entry.isSecret(), entry.getGroup(), entry.getLabel(), entry.isEnumType(), entry.getEnums(),
-                false, null, asPredicate, optionalPrefix, prefix, multiValue, entry.getConfigurationClass(), entry.getConfigurationField()));
-
-            parentComponentProperties.remove(entry.getName());
-        }
-
-        for (Map.Entry<String, Object> prop : parentComponentProperties.entrySet()) {
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-            buffer.append(Strings.doubleQuote(prop.getKey()));
-            buffer.append(": ");
-            buffer.append(Jsoner.serialize(prop.getValue()));
-        }
-
-        buffer.append("\n  },");
-
-        Map<String, Object> parentProperties;
-        if (parentData != null && parentData.get("properties") != null) {
-            parentProperties = (Map<String, Object>) parentData.get("properties");
-        } else {
-            parentProperties = new HashMap<>();
-        }
-
-        buffer.append("\n  \"properties\": {");
-        first = true;
-
-        // sort the endpoint options in the standard order we prefer
-        List<EndpointPath> paths = new ArrayList<>();
-        paths.addAll(endpointPaths);
-        Collections.sort(paths, EndpointHelper.createPathComparator(componentModel.getSyntax()));
-
-        // include paths in the top
-        for (EndpointPath entry : paths) {
-            String label = entry.getLabel();
-            if (label != null) {
-                // skip options which are either consumer or producer labels but the component does not support them
-                if (label.contains("consumer") && componentModel.isProducerOnly()) {
-                    continue;
-                } else if (label.contains("producer") && componentModel.isConsumerOnly()) {
-                    continue;
-                }
-            }
-
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-            // either we have the documentation from this apt plugin or we need help to find it from extended component
-            String doc = entry.getDocumentation();
-            if (Strings.isNullOrEmpty(doc)) {
-                doc = DocumentationHelper.findEndpointJavaDoc(componentModel.getScheme(), componentModel.getExtendsScheme(), entry.getName());
-            }
-            // as its json we need to sanitize the docs
-            doc = sanitizeDescription(doc, false);
-            boolean required = entry.isRequired();
-            String defaultValue = entry.getDefaultValue();
-            if (Strings.isNullOrEmpty(defaultValue) && "boolean".equals(entry.getType())) {
-                // fallback as false for boolean types
-                defaultValue = "false";
-            }
-
-            // @UriPath options do not have prefix
-            String optionalPrefix = "";
-            String prefix = "";
-            boolean multiValue = false;
-            boolean asPredicate = false;
-
-            buffer.append(JSonSchemaHelper.toJson(entry.getName(), entry.getDisplayName(), "path", required, entry.getType(), defaultValue, doc,
-                entry.isDeprecated(), entry.getDeprecationNote(), entry.isSecret(), entry.getGroup(), entry.getLabel(), entry.isEnumType(), entry.getEnums(),
-                false, null, asPredicate, optionalPrefix, prefix, multiValue, null, null));
-
-            parentProperties.remove(entry.getName());
-        }
-
-        // sort the endpoint options in the standard order we prefer
-        List<EndpointOption> options = new ArrayList<>();
-        options.addAll(endpointOptions);
-        Collections.sort(options, EndpointHelper.createGroupAndLabelComparator());
-
-        // and then regular parameter options
-        for (EndpointOption entry : options) {
-            String label = entry.getLabel();
-            if (label != null) {
-                // skip options which are either consumer or producer labels but the component does not support them
-                if (label.contains("consumer") && componentModel.isProducerOnly()) {
-                    continue;
-                } else if (label.contains("producer") && componentModel.isConsumerOnly()) {
-                    continue;
-                }
-            }
-
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-            // either we have the documentation from this apt plugin or we need help to find it from extended component
-            String doc = entry.getDocumentationWithNotes();
-            if (Strings.isNullOrEmpty(doc)) {
-                doc = DocumentationHelper.findEndpointJavaDoc(componentModel.getScheme(), componentModel.getExtendsScheme(), entry.getName());
-            }
-            // as its json we need to sanitize the docs
-            doc = sanitizeDescription(doc, false);
-            Boolean required = entry.isRequired();
-            String defaultValue = entry.getDefaultValue();
-            if (Strings.isNullOrEmpty(defaultValue) && "boolean".equals(entry.getType())) {
-                // fallback as false for boolean types
-                defaultValue = "false";
-            }
-            String optionalPrefix = entry.getOptionalPrefix();
-            String prefix = entry.getPrefix();
-            boolean multiValue = entry.isMultiValue();
-            boolean asPredicate = false;
-
-            buffer.append(JSonSchemaHelper.toJson(entry.getName(), entry.getDisplayName(), "parameter", required, entry.getType(), defaultValue,
-                doc, entry.isDeprecated(), entry.getDeprecationNote(), entry.isSecret(), entry.getGroup(), entry.getLabel(), entry.isEnumType(), entry.getEnums(),
-                false, null, asPredicate, optionalPrefix, prefix, multiValue, entry.getConfigurationClass(), entry.getConfigurationField()));
-
-            parentProperties.remove(entry.getName());
-        }
-
-        for (Map.Entry<String, Object> prop : parentProperties.entrySet()) {
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-            buffer.append(Strings.doubleQuote(prop.getKey()));
-            buffer.append(": ");
-            buffer.append(Jsoner.serialize(prop.getValue()));
-        }
-
-        buffer.append("\n  }");
-
-        buffer.append("\n}\n");
-        return buffer.toString();
-    }
-
-    protected ComponentModel findComponentProperties(RoundEnvironment roundEnv, UriEndpoint uriEndpoint, TypeElement endpointClassElement,
-                                                     String title, String scheme, String extendsScheme, String label) {
-        ComponentModel model = new ComponentModel(scheme);
-
         // if the scheme is an alias then replace the scheme name from the syntax with the alias
         String syntax = scheme + ":" + Strings.after(uriEndpoint.syntax(), ":");
         // alternative syntax is optional
@@ -484,15 +322,13 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
             String alternativeSyntax = scheme + ":" + Strings.after(uriEndpoint.alternativeSyntax(), ":");
             model.setAlternativeSyntax(alternativeSyntax);
         }
-
-        model.setExtendsScheme(extendsScheme);
         model.setSyntax(syntax);
         model.setTitle(title);
         model.setLabel(label);
         model.setConsumerOnly(uriEndpoint.consumerOnly());
         model.setProducerOnly(uriEndpoint.producerOnly());
         model.setLenientProperties(uriEndpoint.lenientProperties());
-        model.setAsync(implementsInterface(processingEnv, roundEnv, endpointClassElement, "org.apache.camel.AsyncEndpoint"));
+        model.setAsync(AnnotationProcessorHelper.implementsInterface(processingEnv, roundEnv, endpointClassElement, "org.apache.camel.AsyncEndpoint"));
 
         // what is the first version this component was added to Apache Camel
         String firstVersion = uriEndpoint.firstVersion();
@@ -509,8 +345,8 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         if (elements != null) {
             for (Element e : elements) {
                 Component comp = e.getAnnotation(Component.class);
-                String[] schemes = comp.value().split(",");
-                if (Arrays.asList(schemes).contains(scheme) && e.getKind() == ElementKind.CLASS) {
+                String[] cschemes = comp.value().split(",");
+                if (Arrays.asList(cschemes).contains(scheme) && e.getKind() == ElementKind.CLASS) {
                     TypeElement te = (TypeElement) e;
                     String name = te.getQualifiedName().toString();
                     model.setJavaType(name);
@@ -535,16 +371,16 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         model.setDescription("@@@DESCRIPTION@@@");
         model.setGroupId("@@@GROUPID@@@");
         model.setArtifactId("@@@ARTIFACTID@@@");
-        model.setVersionId("@@@VERSIONID@@@");
+        model.setVersion("@@@VERSIONID@@@");
 
         // favor to use endpoint class javadoc as description
         Elements elementUtils = processingEnv.getElementUtils();
-        TypeElement typeElement = findTypeElement(processingEnv, roundEnv, endpointClassElement.getQualifiedName().toString());
+        TypeElement typeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, endpointClassElement.getQualifiedName().toString());
         if (typeElement != null) {
             String doc = elementUtils.getDocComment(typeElement);
             if (doc != null) {
                 // need to sanitize the description first (we only want a summary)
-                doc = sanitizeDescription(doc, true);
+                doc = JSonSchemaHelper.sanitizeDescription(doc, true);
                 // the javadoc may actually be empty, so only change the doc if we got something
                 if (!Strings.isNullOrEmpty(doc)) {
                     model.setDescription(doc);
@@ -555,9 +391,9 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         return model;
     }
 
-    protected void findComponentClassProperties(PrintWriter writer, RoundEnvironment roundEnv, ComponentModel componentModel,
-                                                Set<ComponentOption> componentOptions, TypeElement classElement, String prefix,
-                                                Map<String, Object> parentData, String nestedTypeName, String nestedFieldName) {
+    protected void findComponentClassProperties(RoundEnvironment roundEnv, ComponentModel componentModel,
+                                                TypeElement classElement, String prefix,
+                                                ComponentModel parentData, String nestedTypeName, String nestedFieldName) {
         Elements elementUtils = processingEnv.getElementUtils();
         while (true) {
             Metadata componentAnnotation = classElement.getAnnotation(Metadata.class);
@@ -598,7 +434,7 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                 // we usually favor putting the @Metadata annotation on the field instead of the setter, so try to use it if its there
                 String fieldName = methodName.substring(3);
                 fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
-                VariableElement field = findFieldElement(classElement, fieldName);
+                VariableElement field = AnnotationProcessorHelper.findFieldElement(classElement, fieldName);
                 if (field != null && metadata == null) {
                     metadata = field.getAnnotation(Metadata.class);
                 }
@@ -609,25 +445,28 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                 boolean required = metadata != null && metadata.required();
                 String label = metadata != null ? metadata.label() : null;
                 boolean secret = metadata != null && metadata.secret();
-                String displayName = metadata != null ? metadata.displayName() : null;
 
                 // we do not yet have default values / notes / as no annotation support yet
                 // String defaultValueNote = param.defaultValueNote();
-                String defaultValue = metadata != null ? metadata.defaultValue() : null;
+                Object defaultValue = metadata != null ? metadata.defaultValue() : "";
                 String defaultValueNote = null;
 
-                ExecutableElement setter = method;
-                String name = fieldName;
-                name = prefix + name;
-                TypeMirror fieldType = setter.getParameters().get(0).asType();
+                String name = prefix + fieldName;
+                String displayName = metadata != null ? metadata.displayName() : null;
+                // compute a display name if we don't have anything
+                if (Strings.isNullOrEmpty(displayName)) {
+                    displayName = Strings.asTitle(name);
+                }
+
+                TypeMirror fieldType = method.getParameters().get(0).asType();
                 String fieldTypeName = fieldType.toString();
-                TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+                TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
 
-                String docComment = findJavaDoc(elementUtils, method, fieldName, name, classElement, false);
-                if (isNullOrEmpty(docComment)) {
+                String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, method, fieldName, name, classElement, false);
+                if (Strings.isNullOrEmpty(docComment)) {
                     docComment = metadata != null ? metadata.description() : null;
                 }
-                if (isNullOrEmpty(docComment)) {
+                if (Strings.isNullOrEmpty(docComment)) {
                     // apt cannot grab javadoc from camel-core, only from annotations
                     if ("setHeaderFilterStrategy".equals(methodName)) {
                         docComment = HEADER_FILTER_STRATEGY_JAVADOC;
@@ -637,28 +476,23 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                 }
 
                 // gather enums
-                Set<String> enums = new LinkedHashSet<>();
+                List<String> enums = null;
 
                 boolean isEnum;
                 if (metadata != null && !Strings.isNullOrEmpty(metadata.enums())) {
                     isEnum = true;
                     String[] values = metadata.enums().split(",");
-                    for (String val : values) {
-                        enums.add(val);
-                    }
+                    enums = Stream.of(values).map(String::trim).collect(Collectors.toList());
                 } else {
                     isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
                     if (isEnum) {
-                        TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                        TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
                         if (enumClass != null) {
                             // find all the enum constants which has the possible enum value that can be used
-                            List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements());
-                            for (VariableElement var : fields) {
-                                if (var.getKind() == ElementKind.ENUM_CONSTANT) {
-                                    String val = var.toString();
-                                    enums.add(val);
-                                }
-                            }
+                            enums = ElementFilter.fieldsIn(enumClass.getEnclosedElements()).stream()
+                                    .filter(var -> var.getKind() == ElementKind.ENUM_CONSTANT)
+                                    .map(Object::toString)
+                                    .collect(Collectors.toList());
                         }
                     }
                 }
@@ -668,6 +502,13 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     fieldTypeName = metadata.javaType();
                 }
 
+                if (isNullOrEmpty(defaultValue) && "boolean".equals(fieldTypeName)) {
+                    defaultValue = false;
+                }
+                if (isNullOrEmpty(defaultValue)) {
+                    defaultValue = "";
+                }
+
                 String group = EndpointHelper.labelAsGroupName(label, componentModel.isConsumerOnly(), componentModel.isProducerOnly());
                 // filter out consumer/producer only
                 boolean accept = true;
@@ -677,9 +518,27 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     accept = false;
                 }
                 if (accept) {
-                    ComponentOption option = new ComponentOption(name, displayName, fieldTypeName, required, defaultValue, defaultValueNote,
-                            docComment.trim(), deprecated, deprecationNote, secret, group, label, isEnum, enums, nestedTypeName, nestedFieldName);
-                    componentOptions.add(option);
+                    ComponentOptionModel option = new ComponentOptionModel();
+                    option.setKind("property");
+                    option.setName(name);
+                    option.setDisplayName(displayName);
+                    option.setType(JSonSchemaHelper.getType(fieldTypeName, false));
+                    option.setJavaType(fieldTypeName);
+                    option.setRequired(required);
+                    option.setDefaultValue(defaultValue);
+                    option.setDefaultValueNote(defaultValueNote);
+                    option.setDescription(docComment.trim());
+                    option.setDeprecated(deprecated);
+                    option.setDeprecationNote(deprecationNote);
+                    option.setSecret(secret);
+                    option.setGroup(group);
+                    option.setLabel(label);
+                    option.setEnums(enums);
+                    option.setConfigurationClass(nestedTypeName);
+                    option.setConfigurationField(nestedFieldName);
+                    if (componentModel.getComponentOptions().stream().noneMatch(opt -> name.equals(opt.getName()))) {
+                        componentModel.addComponentOption(option);
+                    }
                 }
             }
 
@@ -688,8 +547,8 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
             if (parentData == null) {
                 TypeMirror superclass = classElement.getSuperclass();
                 if (superclass != null) {
-                    String superClassName = canonicalClassName(superclass.toString());
-                    baseTypeElement = findTypeElement(processingEnv, roundEnv, superClassName);
+                    String superClassName = Strings.canonicalClassName(superclass.toString());
+                    baseTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, superClassName);
                 }
             }
             if (baseTypeElement != null) {
@@ -700,10 +559,9 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         }
     }
 
-    protected void findClassProperties(PrintWriter writer, RoundEnvironment roundEnv, ComponentModel componentModel,
-                                       Set<EndpointPath> endpointPaths, Set<EndpointOption> endpointOptions,
+    protected void findClassProperties(RoundEnvironment roundEnv, ComponentModel componentModel,
                                        TypeElement classElement, String prefix, String excludeProperties,
-                                       Map<String, Object> parentData, String nestedTypeName, String nestedFieldName) {
+                                       ComponentModel parentData, String nestedTypeName, String nestedFieldName) {
         Elements elementUtils = processingEnv.getElementUtils();
         while (true) {
             List<VariableElement> fieldElements = ElementFilter.fieldsIn(classElement.getEnclosedElements());
@@ -723,19 +581,15 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                 UriPath path = fieldElement.getAnnotation(UriPath.class);
                 String fieldName = fieldElement.getSimpleName().toString();
                 if (path != null) {
-                    String name = path.name();
-                    if (isNullOrEmpty(name)) {
-                        name = fieldName;
-                    }
-                    name = prefix + name;
+                    String name = prefix + (Strings.isNullOrEmpty(path.name()) ? fieldName : path.name());
 
                     // should we exclude the name?
                     if (excludeProperty(excludeProperties, name)) {
                         continue;
                     }
 
-                    String defaultValue = path.defaultValue();
-                    if (Strings.isNullOrEmpty(defaultValue) && metadata != null) {
+                    Object defaultValue = path.defaultValue();
+                    if ("".equals(defaultValue) && metadata != null) {
                         defaultValue = metadata.defaultValue();
                     }
                     boolean required = metadata != null && metadata.required();
@@ -747,40 +601,34 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     if (Strings.isNullOrEmpty(displayName)) {
                         displayName = metadata != null ? metadata.displayName() : null;
                     }
+                    // compute a display name if we don't have anything
+                    if (Strings.isNullOrEmpty(displayName)) {
+                        displayName = Strings.asTitle(name);
+                    }
 
                     TypeMirror fieldType = fieldElement.asType();
                     String fieldTypeName = fieldType.toString();
-                    TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+                    TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
 
-                    String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, false);
-                    if (isNullOrEmpty(docComment)) {
+                    String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, false);
+                    if (Strings.isNullOrEmpty(docComment)) {
                         docComment = path.description();
                     }
 
                     // gather enums
-                    Set<String> enums = new LinkedHashSet<>();
+                    List<String> enums = null;
 
-                    boolean isEnum;
                     if (!Strings.isNullOrEmpty(path.enums())) {
-                        isEnum = true;
                         String[] values = path.enums().split(",");
-                        for (String val : values) {
-                            enums.add(val.trim());
-                        }
-                    } else {
-                        isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
-                        if (isEnum) {
-                            TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
-                            // find all the enum constants which has the possible enum value that can be used
-                            if (enumClass != null) {
-                                List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements());
-                                for (VariableElement var : fields) {
-                                    if (var.getKind() == ElementKind.ENUM_CONSTANT) {
-                                        String val = var.toString();
-                                        enums.add(val);
-                                    }
-                                }
-                            }
+                        enums = Stream.of(values).map(String::trim).collect(Collectors.toList());
+                    } else if (fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM) {
+                        TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                        // find all the enum constants which has the possible enum value that can be used
+                        if (enumClass != null) {
+                            enums = ElementFilter.fieldsIn(enumClass.getEnclosedElements()).stream()
+                                    .filter(var -> var.getKind() == ElementKind.ENUM_CONSTANT)
+                                    .map(Object::toString)
+                                    .collect(Collectors.toList());
                         }
                     }
 
@@ -788,22 +636,42 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     if (!Strings.isNullOrEmpty(path.javaType())) {
                         fieldTypeName = path.javaType();
                     }
+                    if (isNullOrEmpty(defaultValue) && "boolean".equals(fieldTypeName)) {
+                        defaultValue = false;
+                    }
+                    if (isNullOrEmpty(defaultValue)) {
+                        defaultValue = "";
+                    }
 
                     boolean isSecret = secret != null && secret || path.secret();
                     String group = EndpointHelper.labelAsGroupName(label, componentModel.isConsumerOnly(), componentModel.isProducerOnly());
-                    EndpointPath ep = new EndpointPath(name, displayName, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote,
-                        isSecret, group, label, isEnum, enums);
-                    endpointPaths.add(ep);
+                    EndpointOptionModel option = new EndpointOptionModel();
+                    option.setKind("path");
+                    option.setName(name);
+                    option.setDisplayName(displayName);
+                    option.setType(JSonSchemaHelper.getType(fieldTypeName, false));
+                    option.setJavaType(fieldTypeName);
+                    option.setRequired(required);
+                    option.setDefaultValue(defaultValue);
+//                    option.setDefaultValueNote(defaultValueNote);
+                    option.setDescription(docComment.trim());
+                    option.setDeprecated(deprecated);
+                    option.setDeprecationNote(deprecationNote);
+                    option.setSecret(isSecret);
+                    option.setGroup(group);
+                    option.setLabel(label);
+                    option.setEnums(enums);
+                    option.setConfigurationClass(nestedTypeName);
+                    option.setConfigurationField(nestedFieldName);
+                    if (componentModel.getEndpointOptions().stream().noneMatch(opt -> name.equals(opt.getName()))) {
+                        componentModel.addEndpointOption(option);
+                    }
                 }
 
                 UriParam param = fieldElement.getAnnotation(UriParam.class);
                 fieldName = fieldElement.getSimpleName().toString();
                 if (param != null) {
-                    String name = param.name();
-                    if (isNullOrEmpty(name)) {
-                        name = fieldName;
-                    }
-                    name = prefix + name;
+                    String name = prefix + (Strings.isNullOrEmpty(param.name()) ? fieldName : param.name());
 
                     // should we exclude the name?
                     if (excludeProperty(excludeProperties, name)) {
@@ -813,8 +681,8 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     String paramOptionalPrefix = param.optionalPrefix();
                     String paramPrefix = param.prefix();
                     boolean multiValue = param.multiValue();
-                    String defaultValue = param.defaultValue();
-                    if (defaultValue == null && metadata != null) {
+                    Object defaultValue = param.defaultValue();
+                    if (isNullOrEmpty(defaultValue) && metadata != null) {
                         defaultValue = metadata.defaultValue();
                     }
                     String defaultValueNote = param.defaultValueNote();
@@ -827,11 +695,15 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     if (Strings.isNullOrEmpty(displayName)) {
                         displayName = metadata != null ? metadata.displayName() : null;
                     }
+                    // compute a display name if we don't have anything
+                    if (Strings.isNullOrEmpty(displayName)) {
+                        displayName = Strings.asTitle(name);
+                    }
 
                     // if the field type is a nested parameter then iterate through its fields
                     TypeMirror fieldType = fieldElement.asType();
                     String fieldTypeName = fieldType.toString();
-                    TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+                    TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
                     UriParams fieldParams = null;
                     if (fieldTypeElement != null) {
                         fieldParams = fieldTypeElement.getAnnotation(UriParams.class);
@@ -839,47 +711,37 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                     if (fieldParams != null) {
                         String nestedPrefix = prefix;
                         String extraPrefix = fieldParams.prefix();
-                        if (!isNullOrEmpty(extraPrefix)) {
+                        if (!Strings.isNullOrEmpty(extraPrefix)) {
                             nestedPrefix += extraPrefix;
                         }
                         nestedTypeName = fieldTypeName;
                         nestedFieldName = fieldElement.getSimpleName().toString();
-                        findClassProperties(writer, roundEnv, componentModel, endpointPaths, endpointOptions, fieldTypeElement, nestedPrefix, excludeProperties, null, nestedTypeName, nestedFieldName);
+                        findClassProperties(roundEnv, componentModel, fieldTypeElement, nestedPrefix, excludeProperties, null, nestedTypeName, nestedFieldName);
                         nestedTypeName = null;
                         nestedFieldName = null;
                     } else {
-                        String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, false);
-                        if (isNullOrEmpty(docComment)) {
+                        String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, false);
+                        if (Strings.isNullOrEmpty(docComment)) {
                             docComment = param.description();
                         }
-                        if (isNullOrEmpty(docComment)) {
+                        if (Strings.isNullOrEmpty(docComment)) {
                             docComment = "";
                         }
 
                         // gather enums
-                        Set<String> enums = new LinkedHashSet<>();
+                        List<String> enums = null;
 
-                        boolean isEnum;
                         if (!Strings.isNullOrEmpty(param.enums())) {
-                            isEnum = true;
                             String[] values = param.enums().split(",");
-                            for (String val : values) {
-                                enums.add(val);
-                            }
-                        } else {
-                            isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
-                            if (isEnum) {
-                                TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
-                                if (enumClass != null) {
-                                    // find all the enum constants which has the possible enum value that can be used
-                                    List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements());
-                                    for (VariableElement var : fields) {
-                                        if (var.getKind() == ElementKind.ENUM_CONSTANT) {
-                                            String val = var.toString();
-                                            enums.add(val);
-                                        }
-                                    }
-                                }
+                            enums = Stream.of(values).map(String::trim).collect(Collectors.toList());
+                        } else if (fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM) {
+                            TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                            if (enumClass != null) {
+                                // find all the enum constants which has the possible enum value that can be used
+                                enums = ElementFilter.fieldsIn(enumClass.getEnclosedElements()).stream()
+                                        .filter(var -> var.getKind() == ElementKind.ENUM_CONSTANT)
+                                        .map(Object::toString)
+                                        .collect(Collectors.toList());
                             }
                         }
 
@@ -888,12 +750,39 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
                             fieldTypeName = param.javaType();
                         }
 
+                        if (isNullOrEmpty(defaultValue) && "boolean".equals(fieldTypeName)) {
+                            defaultValue = false;
+                        }
+                        if (isNullOrEmpty(defaultValue)) {
+                            defaultValue = "";
+                        }
+
                         boolean isSecret = secret != null && secret || param.secret();
                         String group = EndpointHelper.labelAsGroupName(label, componentModel.isConsumerOnly(), componentModel.isProducerOnly());
-                        EndpointOption option = new EndpointOption(name, displayName, fieldTypeName, required, defaultValue, defaultValueNote,
-                                docComment.trim(), paramOptionalPrefix, paramPrefix, multiValue, deprecated, deprecationNote, isSecret, group, label,
-                                isEnum, enums, nestedTypeName, nestedFieldName);
-                        endpointOptions.add(option);
+                        EndpointOptionModel option = new EndpointOptionModel();
+                        option.setKind("parameter");
+                        option.setName(name);
+                        option.setDisplayName(displayName);
+                        option.setType(JSonSchemaHelper.getType(fieldTypeName, false));
+                        option.setJavaType(fieldTypeName);
+                        option.setRequired(required);
+                        option.setDefaultValue(defaultValue);
+                        option.setDefaultValueNote(defaultValueNote);
+                        option.setDescription(docComment.trim());
+                        option.setDeprecated(deprecated);
+                        option.setDeprecationNote(deprecationNote);
+                        option.setSecret(isSecret);
+                        option.setGroup(group);
+                        option.setLabel(label);
+                        option.setEnums(enums);
+                        option.setConfigurationClass(nestedTypeName);
+                        option.setConfigurationField(nestedFieldName);
+                        option.setPrefix(paramPrefix);
+                        option.setOptionalPrefix(paramOptionalPrefix);
+                        option.setMultiValue(multiValue);
+                        if (componentModel.getEndpointOptions().stream().noneMatch(opt -> name.equals(opt.getName()))) {
+                            componentModel.addEndpointOption(option);
+                        }
                     }
                 }
             }
@@ -903,8 +792,8 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
             if (parentData == null) {
                 TypeMirror superclass = classElement.getSuperclass();
                 if (superclass != null) {
-                    String superClassName = canonicalClassName(superclass.toString());
-                    baseTypeElement = findTypeElement(processingEnv, roundEnv, superClassName);
+                    String superClassName = Strings.canonicalClassName(superclass.toString());
+                    baseTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, superClassName);
                 }
             }
             if (baseTypeElement != null) {
@@ -915,6 +804,10 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         }
     }
 
+    private static boolean isNullOrEmpty(Object value) {
+        return value == null || "".equals(value) || "null".equals(value);
+    }
+
     private static boolean excludeProperty(String excludeProperties, String name) {
         String[] excludes = excludeProperties.split(",");
         for (String exclude : excludes) {
@@ -957,4 +850,44 @@ public class EndpointAnnotationProcessor extends AbstractCamelAnnotationProcesso
         }
     }
 
+    protected void generateExtendConfigurer(ProcessingEnvironment processingEnv, TypeElement parent,
+                                            String pn, String cn, String fqn, String scheme) {
+
+        String pfqn = parent.getQualifiedName().toString();
+        String psn = pfqn.substring(pfqn.lastIndexOf('.') + 1);
+        try (Writer w = processingEnv.getFiler().createSourceFile(fqn, parent).openWriter()) {
+            PropertyConfigurerGenerator.generateExtendConfigurer(pn, cn, pfqn, psn, w);
+            generateMetaInfConfigurer(processingEnv, scheme + "-component", fqn);
+        } catch (Exception e) {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
+            AnnotationProcessorHelper.dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
+        }
+    }
+
+    protected void generatePropertyConfigurer(ProcessingEnvironment processingEnv, TypeElement parent,
+                                              String pn, String cn, String fqn, String en, String scheme,
+                                              Collection<? extends BaseOptionModel> options) {
+
+        try (Writer w = processingEnv.getFiler().createSourceFile(fqn, parent).openWriter()) {
+            PropertyConfigurerGenerator.generatePropertyConfigurer(pn, cn, en, options, w);
+            generateMetaInfConfigurer(processingEnv, scheme + "-component", fqn);
+        } catch (Exception e) {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
+            AnnotationProcessorHelper.dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
+        }
+    }
+
+    protected void generateMetaInfConfigurer(ProcessingEnvironment processingEnv, String name, String fqn) {
+        try {
+            FileObject resource = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
+                    "META-INF/services/org/apache/camel/configurer/" + name);
+            try (Writer w = resource.openWriter()) {
+                w.append("# Generated by camel annotation processor\n");
+                w.append("class=").append(fqn).append("\n");
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
 }
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java
index 4d4528d..53f4cf8 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/ModelAnnotationProcessor.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.tools.apt;
 
+import java.io.Writer;
 import java.util.Iterator;
 import java.util.Set;
 import java.util.TreeSet;
@@ -23,10 +24,12 @@ import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
 import javax.tools.Diagnostic.Kind;
 import javax.xml.bind.annotation.XmlRootElement;
 
@@ -98,7 +101,20 @@ public class ModelAnnotationProcessor extends AbstractCamelAnnotationProcessor {
 
         if (!propertyPlaceholderDefinitions.isEmpty()) {
             messager.printMessage(Kind.WARNING, String.format("Generating placeholder definitions helper for %d definitions", propertyPlaceholderDefinitions.size()));
-            PropertyPlaceholderGenerator.generatePropertyPlaceholderDefinitionsHelper(processingEnv, roundEnv, propertyPlaceholderDefinitions);
+            generatePropertyPlaceholderDefinitionsHelper(processingEnv, propertyPlaceholderDefinitions);
+        }
+    }
+
+    private void generatePropertyPlaceholderDefinitionsHelper(ProcessingEnvironment processingEnv,
+                                                              Set<String> propertyPlaceholderDefinitions) {
+
+        String fqn = "org.apache.camel.model.placeholder.DefinitionPropertiesPlaceholderProviderHelper";
+
+        try (Writer w = processingEnv.getFiler().createSourceFile(fqn).openWriter()) {
+            PropertyPlaceholderGenerator.generatePropertyPlaceholderDefinitionsHelper(propertyPlaceholderDefinitions, w);
+        } catch (Exception e) {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
+            AnnotationProcessorHelper.dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
         }
     }
 
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyConfigurerGenerator.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyConfigurerGenerator.java
index 3ab11c9..4c53193 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyConfigurerGenerator.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyConfigurerGenerator.java
@@ -18,105 +18,82 @@ package org.apache.camel.tools.apt;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.util.Set;
+import java.util.Collection;
 
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-import javax.tools.FileObject;
-import javax.tools.StandardLocation;
-
-import org.apache.camel.tools.apt.model.PropertyOption;
-
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.dumpExceptionToErrorFile;
+import org.apache.camel.tooling.model.BaseOptionModel;
 
 public final class PropertyConfigurerGenerator {
 
     private PropertyConfigurerGenerator() {
     }
 
-    public static void generateExtendConfigurer(ProcessingEnvironment processingEnv, TypeElement parent,
-                                                String pn, String cn, String fqn) {
-
-        try (Writer w = processingEnv.getFiler().createSourceFile(fqn, parent).openWriter()) {
-            w.write("/* Generated by org.apache.camel:apt */\n");
-            w.write("package " + pn + ";\n");
-            w.write("\n");
-            w.write("import " + parent.getQualifiedName().toString() + ";\n");
-            w.write("\n");
-            w.write("/**\n");
-            w.write(" * Source code generated by org.apache.camel:apt\n");
-            w.write(" */\n");
-            w.write("public class " + cn + " extends " + parent.getSimpleName().toString() + " {\n");
-            w.write("\n");
-            w.write("}\n");
-            w.write("\n");
-        } catch (Exception e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
-        }
+    public static void generateExtendConfigurer(String pn, String cn, String pfqn, String psn, Writer w) throws IOException {
+        w.write("/* Generated by org.apache.camel:apt */\n");
+        w.write("package " + pn + ";\n");
+        w.write("\n");
+        w.write("import " + pfqn + ";\n");
+        w.write("\n");
+        w.write("/**\n");
+        w.write(" * Source code generated by org.apache.camel:apt\n");
+        w.write(" */\n");
+        w.write("public class " + cn + " extends " + psn + " {\n");
+        w.write("\n");
+        w.write("}\n");
+        w.write("\n");
     }
 
-    public static void generatePropertyConfigurer(ProcessingEnvironment processingEnv, TypeElement parent,
-                                                  String pn, String cn, String fqn, String en,
-                                                  Set<PropertyOption> options) {
-
-        try (Writer w = processingEnv.getFiler().createSourceFile(fqn, parent).openWriter()) {
-            w.write("/* Generated by org.apache.camel:apt */\n");
-            w.write("package " + pn + ";\n");
-            w.write("\n");
-            w.write("import java.util.HashMap;\n");
-            w.write("import java.util.Map;\n");
-            w.write("\n");
-            w.write("import org.apache.camel.CamelContext;\n");
-            w.write("import org.apache.camel.spi.GeneratedPropertyConfigurer;\n");
-            w.write("import org.apache.camel.support.component.PropertyConfigurerSupport;\n");
-            w.write("\n");
-            w.write("/**\n");
-            w.write(" * Source code generated by org.apache.camel:apt\n");
-            w.write(" */\n");
-            w.write("@SuppressWarnings(\"unchecked\")\n");
-            w.write("public class " + cn + " extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer {\n");
-            w.write("\n");
-            w.write("    @Override\n");
-            w.write("    public boolean configure(CamelContext camelContext, Object target, String name, Object value, boolean ignoreCase) {\n");
-            w.write("        if (ignoreCase) {\n");
-            w.write("            return doConfigureIgnoreCase(camelContext, target, name, value);\n");
-            w.write("        } else {\n");
-            w.write("            return doConfigure(camelContext, target, name, value);\n");
-            w.write("        }\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("    private static boolean doConfigure(CamelContext camelContext, Object target, String name, Object value) {\n");
-            w.write("        switch (name) {\n");
-            for (PropertyOption option : options) {
-                String getOrSet = option.getName();
-                getOrSet = Character.toUpperCase(getOrSet.charAt(0)) + getOrSet.substring(1);
-                String setterLambda = setterLambda(en, getOrSet, option.getType(), option.getConfigurationField());
-                w.write(String.format("        case \"%s\": %s; return true;\n", option.getName(), setterLambda));
-            }
-            w.write("            default: return false;\n");
-            w.write("        }\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("    private static boolean doConfigureIgnoreCase(CamelContext camelContext, Object target, String name, Object value) {\n");
-            w.write("        switch (name.toLowerCase()) {\n");
-            for (PropertyOption option : options) {
-                String getOrSet = option.getName();
-                getOrSet = Character.toUpperCase(getOrSet.charAt(0)) + getOrSet.substring(1);
-                String setterLambda = setterLambda(en, getOrSet, option.getType(), option.getConfigurationField());
-                w.write(String.format("        case \"%s\": %s; return true;\n", option.getName().toLowerCase(), setterLambda));
-            }
-            w.write("            default: return false;\n");
-            w.write("        }\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("}\n");
-            w.write("\n");
-        } catch (Exception e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
+    public static void generatePropertyConfigurer(String pn, String cn, String en, Collection<? extends BaseOptionModel> options, Writer w) throws IOException {
+        w.write("/* Generated by org.apache.camel:apt */\n");
+        w.write("package " + pn + ";\n");
+        w.write("\n");
+        w.write("import java.util.HashMap;\n");
+        w.write("import java.util.Map;\n");
+        w.write("\n");
+        w.write("import org.apache.camel.CamelContext;\n");
+        w.write("import org.apache.camel.spi.GeneratedPropertyConfigurer;\n");
+        w.write("import org.apache.camel.support.component.PropertyConfigurerSupport;\n");
+        w.write("\n");
+        w.write("/**\n");
+        w.write(" * Source code generated by org.apache.camel:apt\n");
+        w.write(" */\n");
+        w.write("@SuppressWarnings(\"unchecked\")\n");
+        w.write("public class " + cn + " extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer {\n");
+        w.write("\n");
+        w.write("    @Override\n");
+        w.write("    public boolean configure(CamelContext camelContext, Object target, String name, Object value, boolean ignoreCase) {\n");
+        w.write("        if (ignoreCase) {\n");
+        w.write("            return doConfigureIgnoreCase(camelContext, target, name, value);\n");
+        w.write("        } else {\n");
+        w.write("            return doConfigure(camelContext, target, name, value);\n");
+        w.write("        }\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("    private static boolean doConfigure(CamelContext camelContext, Object target, String name, Object value) {\n");
+        w.write("        switch (name) {\n");
+        for (BaseOptionModel option : options) {
+            String getOrSet = option.getName();
+            getOrSet = Character.toUpperCase(getOrSet.charAt(0)) + getOrSet.substring(1);
+            String setterLambda = setterLambda(en, getOrSet, option.getJavaType(), option.getConfigurationField());
+            w.write(String.format("        case \"%s\": %s; return true;\n", option.getName(), setterLambda));
         }
+        w.write("            default: return false;\n");
+        w.write("        }\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("    private static boolean doConfigureIgnoreCase(CamelContext camelContext, Object target, String name, Object value) {\n");
+        w.write("        switch (name.toLowerCase()) {\n");
+        for (BaseOptionModel option : options) {
+            String getOrSet = option.getName();
+            getOrSet = Character.toUpperCase(getOrSet.charAt(0)) + getOrSet.substring(1);
+            String setterLambda = setterLambda(en, getOrSet, option.getJavaType(), option.getConfigurationField());
+            w.write(String.format("        case \"%s\": %s; return true;\n", option.getName().toLowerCase(), setterLambda));
+        }
+        w.write("            default: return false;\n");
+        w.write("        }\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("}\n");
+        w.write("\n");
     }
 
     private static String setterLambda(String en, String getOrSet, String type, String configurationField) {
@@ -134,17 +111,4 @@ public final class PropertyConfigurerGenerator {
         return String.format("((%s) target).%s(property(camelContext, %s.class, value))", en, getOrSet, type);
     }
 
-    public static void generateMetaInfConfigurer(ProcessingEnvironment processingEnv, String name, String fqn) {
-        try {
-            FileObject resource = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
-                    "META-INF/services/org/apache/camel/configurer/" + name);
-            try (Writer w = resource.openWriter()) {
-                w.append("# Generated by camel annotation processor\n");
-                w.append("class=").append(fqn).append("\n");
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
 }
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyPlaceholderGenerator.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyPlaceholderGenerator.java
index 556c5de..5776158 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyPlaceholderGenerator.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/PropertyPlaceholderGenerator.java
@@ -16,143 +16,123 @@
  */
 package org.apache.camel.tools.apt;
 
+import java.io.IOException;
 import java.io.Writer;
+import java.util.Collection;
 import java.util.Set;
 
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.dumpExceptionToErrorFile;
+import org.apache.camel.tooling.model.BaseOptionModel;
 
 public final class PropertyPlaceholderGenerator {
 
     private PropertyPlaceholderGenerator() {
     }
 
-    public static void generatePropertyPlaceholderProviderSource(ProcessingEnvironment processingEnv, TypeElement parent,
-                                                                 String def, String fqnDef, String cn, String fqn,
-                                                                 Set<CoreEipAnnotationProcessorHelper.EipOption> options) {
-
-        try (Writer w = processingEnv.getFiler().createSourceFile(fqn, parent).openWriter()) {
-            w.write("/* Generated by org.apache.camel:apt */\n");
-            w.write("package org.apache.camel.model.placeholder;\n");
-            w.write("\n");
-            w.write("import java.util.HashMap;\n");
-            w.write("import java.util.Map;\n");
-            w.write("import java.util.function.Consumer;\n");
-            w.write("import java.util.function.Supplier;\n");
-            w.write("\n");
-            w.write("import org.apache.camel.CamelContext;\n");
-            w.write("import " + fqnDef + ";\n");
-            w.write("import org.apache.camel.spi.PropertyPlaceholderConfigurer;\n");
-            w.write("\n");
-            w.write("/**\n");
-            w.write(" * Source code generated by org.apache.camel:apt\n");
-            w.write(" */\n");
-            w.write("public class " + cn + " implements PropertyPlaceholderConfigurer {\n");
-            w.write("\n");
-            w.write("    private final Map<String, Supplier<String>> readPlaceholders = new HashMap<>();\n");
-            w.write("    private final Map<String, Consumer<String>> writePlaceholders = new HashMap<>();\n");
-            w.write("\n");
-
-            // add constructor
-            w.write("    public " + cn + "(Object obj) {\n");
-            w.write("        " + def + " definition = (" + def + ") obj;\n");
-            w.write("\n");
-
-            // only include string types as they are the only ones we can use for property placeholders
-            boolean found = false;
-            for (CoreEipAnnotationProcessorHelper.EipOption option : options) {
-                if ("java.lang.String".equals(option.getType())) {
-                    found = true;
-                    String getOrSet = sanitizePropertyPlaceholderOptionName(def, option);
-                    getOrSet = Character.toUpperCase(getOrSet.charAt(0)) + getOrSet.substring(1);
-                    w.write("        readPlaceholders.put(\"" + option.getName() + "\", definition::get" + getOrSet + ");\n");
-                    w.write("        writePlaceholders.put(\"" + option.getName() + "\", definition::set" + getOrSet + ");\n");
-                }
+    public static void generatePropertyPlaceholderProviderSource(String def, String fqnDef, String cn, Collection<? extends BaseOptionModel> options, Writer w) throws IOException {
+        w.write("/* Generated by org.apache.camel:apt */\n");
+        w.write("package org.apache.camel.model.placeholder;\n");
+        w.write("\n");
+        w.write("import java.util.HashMap;\n");
+        w.write("import java.util.Map;\n");
+        w.write("import java.util.function.Consumer;\n");
+        w.write("import java.util.function.Supplier;\n");
+        w.write("\n");
+        w.write("import org.apache.camel.CamelContext;\n");
+        w.write("import " + fqnDef + ";\n");
+        w.write("import org.apache.camel.spi.PropertyPlaceholderConfigurer;\n");
+        w.write("\n");
+        w.write("/**\n");
+        w.write(" * Source code generated by org.apache.camel:apt\n");
+        w.write(" */\n");
+        w.write("public class " + cn + " implements PropertyPlaceholderConfigurer {\n");
+        w.write("\n");
+        w.write("    private final Map<String, Supplier<String>> readPlaceholders = new HashMap<>();\n");
+        w.write("    private final Map<String, Consumer<String>> writePlaceholders = new HashMap<>();\n");
+        w.write("\n");
+
+        // add constructor
+        w.write("    public " + cn + "(Object obj) {\n");
+        w.write("        " + def + " definition = (" + def + ") obj;\n");
+        w.write("\n");
+
+        // only include string types as they are the only ones we can use for property placeholders
+        boolean found = false;
+        for (BaseOptionModel option : options) {
+            if ("java.lang.String".equals(option.getJavaType())) {
+                found = true;
+                String getOrSet = sanitizePropertyPlaceholderOptionName(def, option);
+                getOrSet = Character.toUpperCase(getOrSet.charAt(0)) + getOrSet.substring(1);
+                w.write("        readPlaceholders.put(\"" + option.getName() + "\", definition::get" + getOrSet + ");\n");
+                w.write("        writePlaceholders.put(\"" + option.getName() + "\", definition::set" + getOrSet + ");\n");
             }
-            if (!found) {
-                w.write("\n");
-            }
-
-            w.write("    }\n");
-            w.write("\n");
-            w.write("    @Override\n");
-            w.write("    public Map<String, Supplier<String>> getReadPropertyPlaceholderOptions(CamelContext camelContext) {\n");
-            w.write("        return readPlaceholders;\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("    @Override\n");
-            w.write("    public Map<String, Consumer<String>> getWritePropertyPlaceholderOptions(CamelContext camelContext) {\n");
-            w.write("        return writePlaceholders;\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("}\n");
+        }
+        if (!found) {
             w.write("\n");
-        } catch (Exception e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
         }
-    }
 
-    public static void generatePropertyPlaceholderDefinitionsHelper(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv,
-                                                                    Set<String> propertyPlaceholderDefinitions) {
-
-        String fqn = "org.apache.camel.model.placeholder.DefinitionPropertiesPlaceholderProviderHelper";
+        w.write("    }\n");
+        w.write("\n");
+        w.write("    @Override\n");
+        w.write("    public Map<String, Supplier<String>> getReadPropertyPlaceholderOptions(CamelContext camelContext) {\n");
+        w.write("        return readPlaceholders;\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("    @Override\n");
+        w.write("    public Map<String, Consumer<String>> getWritePropertyPlaceholderOptions(CamelContext camelContext) {\n");
+        w.write("        return writePlaceholders;\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("}\n");
+        w.write("\n");
+    }
 
-        try (Writer w = processingEnv.getFiler().createSourceFile(fqn).openWriter()) {
-            w.write("/* Generated by camel-apt */\n");
-            w.write("package org.apache.camel.model.placeholder;\n");
-            w.write("\n");
-            w.write("import java.util.HashMap;\n");
-            w.write("import java.util.Map;\n");
-            w.write("import java.util.Optional;\n");
-            w.write("import java.util.function.Function;\n");
-            w.write("import java.util.function.Supplier;\n");
-            w.write("\n");
-            w.write("import org.apache.camel.spi.PropertyPlaceholderConfigurer;\n");
-            for (String def : propertyPlaceholderDefinitions) {
-                w.write("import " + def + ";\n");
-            }
-            w.write("\n");
-            w.write("/**\n");
-            w.write(" * Source code generated by org.apache.camel:apt\n");
-            w.write(" */\n");
-            w.write("public class DefinitionPropertiesPlaceholderProviderHelper {\n");
-            w.write("\n");
-            w.write("    private static final Map<Class, Function<Object, PropertyPlaceholderConfigurer>> MAP;\n");
-            w.write("    static {\n");
-            w.write("        Map<Class, Function<Object, PropertyPlaceholderConfigurer>> map = new HashMap<>(" + propertyPlaceholderDefinitions.size() + ");\n");
-            for (String def : propertyPlaceholderDefinitions) {
-                String cn = def.substring(def.lastIndexOf('.') + 1);
-                w.write("        map.put(" + cn + ".class, " + cn + "PropertyPlaceholderProvider::new);\n");
-            }
-            w.write("        MAP = map;\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("    public static Optional<PropertyPlaceholderConfigurer> provider(Object definition) {\n");
-            w.write("        Function<Object, PropertyPlaceholderConfigurer> func = MAP.get(definition.getClass());\n");
-            w.write("        if (func != null) {\n");
-            w.write("            return Optional.of(func.apply(definition));\n");
-            w.write("        }\n");
-            w.write("        return Optional.empty();\n");
-            w.write("    }\n");
-            w.write("\n");
-            w.write("}\n");
-            w.write("\n");
-        } catch (Exception e) {
-            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate source code file: " + fqn + ": " + e.getMessage());
-            dumpExceptionToErrorFile("camel-apt-error.log", "Unable to generate source code file: " + fqn, e);
+    public static void generatePropertyPlaceholderDefinitionsHelper(Set<String> propertyPlaceholderDefinitions, Writer w) throws IOException {
+        w.write("/* Generated by camel-apt */\n");
+        w.write("package org.apache.camel.model.placeholder;\n");
+        w.write("\n");
+        w.write("import java.util.HashMap;\n");
+        w.write("import java.util.Map;\n");
+        w.write("import java.util.Optional;\n");
+        w.write("import java.util.function.Function;\n");
+        w.write("import java.util.function.Supplier;\n");
+        w.write("\n");
+        w.write("import org.apache.camel.spi.PropertyPlaceholderConfigurer;\n");
+        for (String def : propertyPlaceholderDefinitions) {
+            w.write("import " + def + ";\n");
+        }
+        w.write("\n");
+        w.write("/**\n");
+        w.write(" * Source code generated by org.apache.camel:apt\n");
+        w.write(" */\n");
+        w.write("public class DefinitionPropertiesPlaceholderProviderHelper {\n");
+        w.write("\n");
+        w.write("    private static final Map<Class, Function<Object, PropertyPlaceholderConfigurer>> MAP;\n");
+        w.write("    static {\n");
+        w.write("        Map<Class, Function<Object, PropertyPlaceholderConfigurer>> map = new HashMap<>(" + propertyPlaceholderDefinitions.size() + ");\n");
+        for (String def : propertyPlaceholderDefinitions) {
+            String cn = def.substring(def.lastIndexOf('.') + 1);
+            w.write("        map.put(" + cn + ".class, " + cn + "PropertyPlaceholderProvider::new);\n");
         }
+        w.write("        MAP = map;\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("    public static Optional<PropertyPlaceholderConfigurer> provider(Object definition) {\n");
+        w.write("        Function<Object, PropertyPlaceholderConfigurer> func = MAP.get(definition.getClass());\n");
+        w.write("        if (func != null) {\n");
+        w.write("            return Optional.of(func.apply(definition));\n");
+        w.write("        }\n");
+        w.write("        return Optional.empty();\n");
+        w.write("    }\n");
+        w.write("\n");
+        w.write("}\n");
+        w.write("\n");
     }
 
     /**
      * Some models have different setter/getter names vs the xml name (eg as defined in @XmlAttribute).
      * So we need to correct this using this method.
      */
-    private static String sanitizePropertyPlaceholderOptionName(String def, CoreEipAnnotationProcessorHelper.EipOption option) {
+    private static String sanitizePropertyPlaceholderOptionName(String def, BaseOptionModel option) {
         if ("SimpleExpression".equals(def) || "JsonPathExpression".equals(def)) {
             if ("resultType".equals(option.getName())) {
                 return "resultTypeName";
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java
index e925af8..55ce384 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/SpringAnnotationProcessorHelper.java
@@ -17,6 +17,7 @@
 package org.apache.camel.tools.apt;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -41,15 +42,10 @@ import javax.xml.bind.annotation.XmlType;
 
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
-
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findJavaDoc;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.findTypeElement;
-import static org.apache.camel.tools.apt.AnnotationProcessorHelper.processFile;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.sanitizeDescription;
-import static org.apache.camel.tooling.util.Strings.canonicalClassName;
-import static org.apache.camel.tooling.util.Strings.isNullOrEmpty;
-import static org.apache.camel.tooling.util.Strings.safeNull;
+import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.tooling.model.EipModel.EipOptionModel;
 
 /**
  * Process camel-spring's <camelContext> and generate json schema documentation
@@ -58,7 +54,7 @@ public class SpringAnnotationProcessorHelper {
 
     protected void processModelClass(final ProcessingEnvironment processingEnv, final RoundEnvironment roundEnv,
                                      final TypeElement classElement) {
-        final String javaTypeName = canonicalClassName(classElement.getQualifiedName().toString());
+        final String javaTypeName = Strings.canonicalClassName(classElement.getQualifiedName().toString());
         String packageName = javaTypeName.substring(0, javaTypeName.lastIndexOf("."));
 
         // skip abstract classes
@@ -72,7 +68,7 @@ public class SpringAnnotationProcessorHelper {
         }
 
         String aName = rootElement.name();
-        if (isNullOrEmpty(aName) || "##default".equals(aName)) {
+        if (Strings.isNullOrEmpty(aName) || "##default".equals(aName)) {
             XmlType typeElement = classElement.getAnnotation(XmlType.class);
             aName = typeElement.name();
         }
@@ -80,14 +76,14 @@ public class SpringAnnotationProcessorHelper {
 
         // lets use the xsd name as the file name
         String fileName;
-        if (isNullOrEmpty(name) || "##default".equals(name)) {
-            fileName = classElement.getSimpleName().toString() + ".json";
+        if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
+            fileName = classElement.getSimpleName().toString() + PackageHelper.JSON_SUFIX;
         } else {
-            fileName = name + ".json";
+            fileName = name + PackageHelper.JSON_SUFIX;
         }
 
         // write json schema
-        processFile(processingEnv, packageName, fileName, writer -> writeJSonSchemeDocumentation(processingEnv, writer, roundEnv, classElement, rootElement, javaTypeName, name));
+        AnnotationProcessorHelper.processFile(processingEnv, packageName, fileName, writer -> writeJSonSchemeDocumentation(processingEnv, writer, roundEnv, classElement, rootElement, javaTypeName, name));
     }
 
     protected void writeJSonSchemeDocumentation(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv, TypeElement classElement,
@@ -96,57 +92,14 @@ public class SpringAnnotationProcessorHelper {
         EipModel eipModel = findEipModelProperties(processingEnv, roundEnv, classElement, javaTypeName, modelName);
 
         // collect eip information
-        Set<EipOption> eipOptions = new TreeSet<>(new EipOptionComparator(eipModel));
+        Set<EipOptionModel> eipOptions = new TreeSet<>(new EipOptionComparator(eipModel));
         findClassProperties(processingEnv, writer, roundEnv, eipOptions, classElement, classElement, "", modelName);
 
-        String json = createParameterJsonSchema(eipModel, eipOptions);
+        eipOptions.forEach(eipModel::addOption);
+        String json = EipModel.createParameterJsonSchema(eipModel);
         writer.println(json);
     }
 
-    public String createParameterJsonSchema(EipModel eipModel, Set<EipOption> options) {
-        StringBuilder buffer = new StringBuilder("{");
-        // eip model
-        buffer.append("\n \"model\": {");
-        buffer.append("\n    \"kind\": \"").append("model").append("\",");
-        buffer.append("\n    \"name\": \"").append(eipModel.getName()).append("\",");
-        if (eipModel.getTitle() != null) {
-            buffer.append("\n    \"title\": \"").append(eipModel.getTitle()).append("\",");
-        } else {
-            // fallback and use name as title
-            buffer.append("\n    \"title\": \"").append(Strings.asTitle(eipModel.getName())).append("\",");
-        }
-        buffer.append("\n    \"description\": \"").append(safeNull(eipModel.getDescription())).append("\",");
-        buffer.append("\n    \"javaType\": \"").append(eipModel.getJavaType()).append("\",");
-        buffer.append("\n    \"label\": \"").append(safeNull(eipModel.getLabel())).append("\",");
-        buffer.append("\n    \"deprecated\": false,");
-        buffer.append("\n    \"input\": false,");
-        buffer.append("\n    \"output\": false");
-        buffer.append("\n  },");
-
-        buffer.append("\n  \"properties\": {");
-        boolean first = true;
-        for (EipOption entry : options) {
-            if (first) {
-                first = false;
-            } else {
-                buffer.append(",");
-            }
-            buffer.append("\n    ");
-
-            // as its json we need to sanitize the docs
-            String doc = entry.getDocumentation();
-            doc = sanitizeDescription(doc, false);
-
-            buffer.append(JSonSchemaHelper.toJson(entry.getName(), entry.getDisplayName(), entry.getKind(), entry.isRequired(), entry.getType(), entry.getDefaultValue(), doc,
-                                                  entry.isDeprecated(), entry.getDeprecationNote(), false, null, null, entry.isEnumType(), entry.getEnums(), entry.isOneOf(),
-                                                  entry.getOneOfTypes(), entry.isAsPredicate(), null, null, false, null, null));
-        }
-        buffer.append("\n  }");
-
-        buffer.append("\n}\n");
-        return buffer.toString();
-    }
-
     protected EipModel findEipModelProperties(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement, String javaTypeName, String name) {
         EipModel model = new EipModel();
         model.setJavaType(javaTypeName);
@@ -162,16 +115,20 @@ public class SpringAnnotationProcessorHelper {
             }
         }
 
+        if (Strings.isNullOrEmpty(model.getTitle())) {
+            model.setTitle(Strings.asTitle(model.getName()));
+        }
+
         // favor to use class javadoc of component as description
         if (model.getJavaType() != null) {
             Elements elementUtils = processingEnv.getElementUtils();
-            TypeElement typeElement = findTypeElement(processingEnv, roundEnv, model.getJavaType());
+            TypeElement typeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, model.getJavaType());
             if (typeElement != null) {
                 String doc = elementUtils.getDocComment(typeElement);
                 if (doc != null) {
                     // need to sanitize the description first (we only want a
                     // summary)
-                    doc = sanitizeDescription(doc, true);
+                    doc = JSonSchemaHelper.sanitizeDescription(doc, true);
                     // the javadoc may actually be empty, so only change the doc
                     // if we got something
                     if (!Strings.isNullOrEmpty(doc)) {
@@ -184,7 +141,7 @@ public class SpringAnnotationProcessorHelper {
         return model;
     }
 
-    protected void findClassProperties(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv, Set<EipOption> eipOptions, TypeElement originalClassType,
+    protected void findClassProperties(ProcessingEnvironment processingEnv, PrintWriter writer, RoundEnvironment roundEnv, Set<EipOptionModel> eipOptions, TypeElement originalClassType,
                                        TypeElement classElement, String prefix, String modelName) {
         while (true) {
             List<VariableElement> fieldElements = ElementFilter.fieldsIn(classElement.getEnclosedElements());
@@ -226,8 +183,8 @@ public class SpringAnnotationProcessorHelper {
             TypeElement baseTypeElement = null;
             TypeMirror superclass = classElement.getSuperclass();
             if (superclass != null) {
-                String superClassName = canonicalClassName(superclass.toString());
-                baseTypeElement = findTypeElement(processingEnv, roundEnv, superClassName);
+                String superClassName = Strings.canonicalClassName(superclass.toString());
+                baseTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, superClassName);
             }
             if (baseTypeElement != null) {
                 classElement = baseTypeElement;
@@ -238,27 +195,27 @@ public class SpringAnnotationProcessorHelper {
     }
 
     private boolean processAttribute(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement originalClassType, TypeElement classElement,
-                                     VariableElement fieldElement, String fieldName, XmlAttribute attribute, Set<EipOption> eipOptions, String prefix, String modelName) {
+                                     VariableElement fieldElement, String fieldName, XmlAttribute attribute, Set<EipOptionModel> eipOptions, String prefix, String modelName) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         String name = attribute.name();
-        if (isNullOrEmpty(name) || "##default".equals(name)) {
+        if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
             name = fieldName;
         }
 
         name = prefix + name;
         TypeMirror fieldType = fieldElement.asType();
         String fieldTypeName = fieldType.toString();
-        TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+        TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
 
         String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
         String docComment;
         if ("mdcLoggingKeysPattern".equals(fieldName)) {
-            docComment = findJavaDoc(elementUtils, fieldElement, "MDCLoggingKeysPattern", name, classElement, true);
+            docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, "MDCLoggingKeysPattern", name, classElement, true);
         } else {
-            docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+            docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
         }
-        if (isNullOrEmpty(docComment)) {
+        if (Strings.isNullOrEmpty(docComment)) {
             Metadata metadata = fieldElement.getAnnotation(Metadata.class);
             docComment = metadata != null ? metadata.description() : null;
         }
@@ -270,7 +227,7 @@ public class SpringAnnotationProcessorHelper {
         Set<String> enums = new TreeSet<>();
         boolean isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
         if (isEnum) {
-            TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+            TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
             // find all the enum constants which has the possible enum value
             // that can be used
             List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements());
@@ -294,7 +251,7 @@ public class SpringAnnotationProcessorHelper {
         }
 
         // special for id as its inherited from camel-core
-        if ("id".equals(name) && isNullOrEmpty(docComment)) {
+        if ("id".equals(name) && Strings.isNullOrEmpty(docComment)) {
             if ("CamelContextFactoryBean".equals(originalClassType.getSimpleName().toString())) {
                 docComment = "Sets the id (name) of this CamelContext";
             } else {
@@ -302,7 +259,7 @@ public class SpringAnnotationProcessorHelper {
             }
         }
 
-        EipOption ep = new EipOption(name, displayName, "attribute", fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, false, null,
+        EipOptionModel ep = createOption(name, displayName, "attribute", fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, false, null,
                                      false);
         eipOptions.add(ep);
 
@@ -313,7 +270,7 @@ public class SpringAnnotationProcessorHelper {
      * Special for processing an @XmlElement routes field
      */
     private void processRoutes(RoundEnvironment roundEnv, TypeElement originalClassType, XmlElement element, VariableElement fieldElement, String fieldName,
-                               Set<EipOption> eipOptions, String prefix) {
+                               Set<EipOptionModel> eipOptions, String prefix) {
 
         TypeMirror fieldType = fieldElement.asType();
         String fieldTypeName = fieldType.toString();
@@ -321,7 +278,7 @@ public class SpringAnnotationProcessorHelper {
         Set<String> oneOfTypes = new TreeSet<>();
         oneOfTypes.add("route");
 
-        EipOption ep = new EipOption("route", "Route", "element", fieldTypeName, false, "", "Contains the Camel routes", false, null, false, null, true, oneOfTypes, false);
+        EipOptionModel ep = createOption("route", "Route", "element", fieldTypeName, false, "", "Contains the Camel routes", false, null, false, null, true, oneOfTypes, false);
         eipOptions.add(ep);
     }
 
@@ -329,7 +286,7 @@ public class SpringAnnotationProcessorHelper {
      * Special for processing an @XmlElement rests field
      */
     private void processRests(RoundEnvironment roundEnv, TypeElement originalClassType, XmlElement element, VariableElement fieldElement, String fieldName,
-                              Set<EipOption> eipOptions, String prefix) {
+                              Set<EipOptionModel> eipOptions, String prefix) {
 
         TypeMirror fieldType = fieldElement.asType();
         String fieldTypeName = fieldType.toString();
@@ -337,13 +294,13 @@ public class SpringAnnotationProcessorHelper {
         Set<String> oneOfTypes = new TreeSet<>();
         oneOfTypes.add("rest");
 
-        EipOption ep = new EipOption("rest", "Rest", "element", fieldTypeName, false, "", "Contains the rest services defined using the rest-dsl", false, null, false, null, true,
+        EipOptionModel ep = createOption("rest", "Rest", "element", fieldTypeName, false, "", "Contains the rest services defined using the rest-dsl", false, null, false, null, true,
                                      oneOfTypes, false);
         eipOptions.add(ep);
     }
 
     private void processElement(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement, XmlElement element, XmlElementRef elementRef,
-                                VariableElement fieldElement, Set<EipOption> eipOptions, String prefix) {
+                                VariableElement fieldElement, Set<EipOptionModel> eipOptions, String prefix) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         String fieldName;
@@ -352,17 +309,17 @@ public class SpringAnnotationProcessorHelper {
 
             String kind = "element";
             String name = element != null ? element.name() : elementRef.name();
-            if (isNullOrEmpty(name) || "##default".equals(name)) {
+            if (Strings.isNullOrEmpty(name) || "##default".equals(name)) {
                 name = fieldName;
             }
             name = prefix + name;
             TypeMirror fieldType = fieldElement.asType();
             String fieldTypeName = fieldType.toString();
-            TypeElement fieldTypeElement = findTypeElement(processingEnv, roundEnv, fieldTypeName);
+            TypeElement fieldTypeElement = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeName);
 
             String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
-            if (isNullOrEmpty(docComment)) {
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+            if (Strings.isNullOrEmpty(docComment)) {
                 Metadata metadata = fieldElement.getAnnotation(Metadata.class);
                 docComment = metadata != null ? metadata.description() : null;
             }
@@ -374,7 +331,7 @@ public class SpringAnnotationProcessorHelper {
             Set<String> enums = new LinkedHashSet<>();
             boolean isEnum = fieldTypeElement != null && fieldTypeElement.getKind() == ElementKind.ENUM;
             if (isEnum) {
-                TypeElement enumClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                TypeElement enumClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
                 // find all the enum constants which has the possible enum value
                 // that can be used
                 List<VariableElement> fields = ElementFilter.fieldsIn(enumClass.getEnclosedElements());
@@ -389,7 +346,7 @@ public class SpringAnnotationProcessorHelper {
             // is it a definition/factory-bean type then its a oneOf
             TreeSet oneOfTypes = new TreeSet<String>();
             if (fieldTypeName.endsWith("Definition") || fieldTypeName.endsWith("FactoryBean")) {
-                TypeElement definitionClass = findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
+                TypeElement definitionClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, fieldTypeElement.asType().toString());
                 if (definitionClass != null) {
                     XmlRootElement rootElement = definitionClass.getAnnotation(XmlRootElement.class);
                     if (rootElement != null) {
@@ -402,7 +359,7 @@ public class SpringAnnotationProcessorHelper {
             } else if (fieldTypeName.endsWith("Definition>") || fieldTypeName.endsWith("FactoryBean>")) {
                 // its a list so we need to load the generic type
                 String typeName = Strings.between(fieldTypeName, "<", ">");
-                TypeElement definitionClass = findTypeElement(processingEnv, roundEnv, typeName);
+                TypeElement definitionClass = AnnotationProcessorHelper.findTypeElement(processingEnv, roundEnv, typeName);
                 if (definitionClass != null) {
                     XmlRootElement rootElement = definitionClass.getAnnotation(XmlRootElement.class);
                     if (rootElement != null) {
@@ -427,14 +384,14 @@ public class SpringAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, displayName, kind, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, oneOf, oneOfTypes,
+            EipOptionModel ep = createOption(name, displayName, kind, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, isEnum, enums, oneOf, oneOfTypes,
                                          asPredicate);
             eipOptions.add(ep);
         }
     }
 
     private void processElements(ProcessingEnvironment processingEnv, RoundEnvironment roundEnv, TypeElement classElement, XmlElements elements, VariableElement fieldElement,
-                                 Set<EipOption> eipOptions, String prefix) {
+                                 Set<EipOptionModel> eipOptions, String prefix) {
         Elements elementUtils = processingEnv.getElementUtils();
 
         String fieldName;
@@ -448,8 +405,8 @@ public class SpringAnnotationProcessorHelper {
             String fieldTypeName = fieldType.toString();
 
             String defaultValue = findDefaultValue(fieldElement, fieldTypeName);
-            String docComment = findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
-            if (isNullOrEmpty(docComment)) {
+            String docComment = AnnotationProcessorHelper.findJavaDoc(elementUtils, fieldElement, fieldName, name, classElement, true);
+            if (Strings.isNullOrEmpty(docComment)) {
                 Metadata metadata = fieldElement.getAnnotation(Metadata.class);
                 docComment = metadata != null ? metadata.description() : null;
             }
@@ -473,7 +430,7 @@ public class SpringAnnotationProcessorHelper {
                 deprecationNote = metadata.deprecationNote();
             }
 
-            EipOption ep = new EipOption(name, kind, displayName, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, false, null, true, oneOfTypes,
+            EipOptionModel ep = createOption(name, kind, displayName, fieldTypeName, required, defaultValue, docComment, deprecated, deprecationNote, false, null, true, oneOfTypes,
                                          false);
             eipOptions.add(ep);
         }
@@ -505,172 +462,26 @@ public class SpringAnnotationProcessorHelper {
         return defaultValue;
     }
 
-    private static final class EipModel {
-
-        private String name;
-        private String title;
-        private String javaType;
-        private String label;
-        private String description;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getTitle() {
-            return title;
-        }
-
-        public void setTitle(String title) {
-            this.title = title;
-        }
-
-        public String getJavaType() {
-            return javaType;
-        }
-
-        public void setJavaType(String javaType) {
-            this.javaType = javaType;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-    }
-
-    private static final class EipOption {
-
-        private String name;
-        private String displayName;
-        private String kind;
-        private String type;
-        private boolean required;
-        private String defaultValue;
-        private String documentation;
-        private boolean deprecated;
-        private String deprecationNote;
-        private boolean enumType;
-        private Set<String> enums;
-        private boolean oneOf;
-        private Set<String> oneOfTypes;
-        private boolean asPredicate;
-
-        private EipOption(String name, String displayName, String kind, String type, boolean required, String defaultValue, String documentation, boolean deprecated,
-                          String deprecationNote, boolean enumType, Set<String> enums, boolean oneOf, Set<String> oneOfTypes, boolean asPredicate) {
-            this.name = name;
-            this.displayName = displayName;
-            this.kind = kind;
-            this.type = type;
-            this.required = required;
-            this.defaultValue = defaultValue;
-            this.documentation = documentation;
-            this.deprecated = deprecated;
-            this.deprecationNote = deprecationNote;
-            this.enumType = enumType;
-            this.enums = enums;
-            this.oneOf = oneOf;
-            this.oneOfTypes = oneOfTypes;
-            this.asPredicate = asPredicate;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        public String getKind() {
-            return kind;
-        }
-
-        public String getType() {
-            return type;
-        }
-
-        public boolean isRequired() {
-            return required;
-        }
-
-        public String getDefaultValue() {
-            return defaultValue;
-        }
-
-        public String getDocumentation() {
-            return documentation;
-        }
-
-        public boolean isDeprecated() {
-            return deprecated;
-        }
-
-        public String getDeprecationNote() {
-            return deprecationNote;
-        }
-
-        public boolean isEnumType() {
-            return enumType;
-        }
-
-        public Set<String> getEnums() {
-            return enums;
-        }
-
-        public boolean isOneOf() {
-            return oneOf;
-        }
-
-        public Set<String> getOneOfTypes() {
-            return oneOfTypes;
-        }
-
-        public boolean isAsPredicate() {
-            return asPredicate;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            EipOption that = (EipOption)o;
-
-            if (!name.equals(that.name)) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            return name.hashCode();
-        }
+    private EipOptionModel createOption(String name, String displayName, String kind, String type, boolean required, String defaultValue, String description, boolean deprecated,
+                                        String deprecationNote, boolean enumType, Set<String> enums, boolean oneOf, Set<String> oneOfs, boolean asPredicate) {
+        EipOptionModel option = new EipOptionModel();
+        option.setName(name);
+        option.setDisplayName(Strings.isNullOrEmpty(displayName) ? Strings.asTitle(name) : displayName);
+        option.setKind(kind);
+        option.setRequired(required);
+        option.setDefaultValue(defaultValue);
+        option.setDescription(JSonSchemaHelper.sanitizeDescription(description, false));
+        option.setDeprecated(deprecated);
+        option.setDeprecationNote(deprecationNote);
+        option.setType(JSonSchemaHelper.getType(type, enumType));
+        option.setJavaType(type);
+        option.setEnums(enums != null && !enums.isEmpty() ? new ArrayList<>(enums) : null);
+        option.setOneOfs(oneOfs != null && !oneOfs.isEmpty() ? new ArrayList<>(oneOfs) : null);
+        option.setAsPredicate(asPredicate);
+        return option;
     }
 
-    private static final class EipOptionComparator implements Comparator<EipOption> {
+    private static final class EipOptionComparator implements Comparator<EipOptionModel> {
 
         private final EipModel model;
 
@@ -679,7 +490,7 @@ public class SpringAnnotationProcessorHelper {
         }
 
         @Override
-        public int compare(EipOption o1, EipOption o2) {
+        public int compare(EipOptionModel o1, EipOptionModel o2) {
             int weigth = weigth(o1);
             int weigth2 = weigth(o2);
 
@@ -692,7 +503,7 @@ public class SpringAnnotationProcessorHelper {
             }
         }
 
-        private int weigth(EipOption o) {
+        private int weigth(EipOptionModel o) {
             String name = o.getName();
 
             // these should be first
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/helper/EndpointHelper.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/helper/EndpointHelper.java
index a51fc92..a769972 100644
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/helper/EndpointHelper.java
+++ b/tooling/apt/src/main/java/org/apache/camel/tools/apt/helper/EndpointHelper.java
@@ -17,10 +17,10 @@
 package org.apache.camel.tools.apt.helper;
 
 import java.util.Comparator;
+import java.util.Objects;
 
+import org.apache.camel.tooling.model.BaseOptionModel;
 import org.apache.camel.tooling.util.Strings;
-import org.apache.camel.tools.apt.model.EndpointOption;
-import org.apache.camel.tools.apt.model.EndpointPath;
 
 public final class EndpointHelper {
 
@@ -70,7 +70,7 @@ public final class EndpointHelper {
     /**
      * A comparator to sort the endpoint/component options according to group and label.
      */
-    public static EndpointOptionGroupAndLabelComparator createGroupAndLabelComparator() {
+    public static Comparator<BaseOptionModel> createGroupAndLabelComparator() {
         return new EndpointOptionGroupAndLabelComparator();
     }
 
@@ -79,11 +79,37 @@ public final class EndpointHelper {
      *
      * @param syntax the endpoint uri syntax
      */
-    public static EndpointPathComparator createPathComparator(String syntax) {
+    public static Comparator<BaseOptionModel> createPathComparator(String syntax) {
         return new EndpointPathComparator(syntax);
     }
 
     /**
+     * A comparator to sort endpoints options: first paths according to the path comparator,
+     * then parameters according to their group and label.
+     *
+     * @param syntax the endpoint uri syntax
+     */
+    public static Comparator<BaseOptionModel> createOverallComparator(String syntax) {
+        Comparator<BaseOptionModel> pathComparator = createPathComparator(syntax);
+        Comparator<BaseOptionModel> groupAndLabelComparator = createGroupAndLabelComparator();
+        return (o1, o2) -> {
+            if (Objects.equals("path", o1.getKind())) {
+                if (Objects.equals("path", o2.getKind())) {
+                    return pathComparator.compare(o1, o2);
+                } else {
+                    return -1;
+                }
+            } else {
+                if (Objects.equals("path", o2.getKind())) {
+                    return +1;
+                } else {
+                    return groupAndLabelComparator.compare(o1, o2);
+                }
+            }
+        };
+    }
+
+    /**
      * To sort the component/endpoint options in human friendly order.
      * <p/>
      * The order is to include options grouped by
@@ -96,10 +122,10 @@ public final class EndpointHelper {
      *     <li>... and the rest sorted by a..z</li>
      * </ul>
      */
-    private static final class EndpointOptionGroupAndLabelComparator implements Comparator<EndpointOption> {
+    private static final class EndpointOptionGroupAndLabelComparator implements Comparator<BaseOptionModel> {
 
         @Override
-        public int compare(EndpointOption o1, EndpointOption o2) {
+        public int compare(BaseOptionModel o1, BaseOptionModel o2) {
             String name1 = o1.getName();
             String name2 = o2.getName();
             String label1 = o1.getLabel() != null ? o1.getLabel() : "common";
@@ -131,24 +157,18 @@ public final class EndpointHelper {
     }
 
     private static int groupScore(String group) {
-        if ("common".equals(group)) {
-            return 1;
-        } else if ("common (advanced)".equals(group)) {
-            return 2;
-        } else if ("consumer".equals(group)) {
-            return 3;
-        } else if ("consumer (advanced)".equals(group)) {
-            return 4;
-        } else if ("producer".equals(group)) {
-            return 5;
-        } else if ("producer (advanced)".equals(group)) {
-            return 6;
-        } else {
-            return 9;
+        switch (group) {
+            case "common":              return 1;
+            case "common (advanced)":   return 2;
+            case "consumer":            return 3;
+            case "consumer (advanced)": return 4;
+            case "producer":            return 5;
+            case "producer (advanced)": return 6;
+            default:                    return 9;
         }
     }
 
-    private static final class EndpointPathComparator implements Comparator<EndpointPath> {
+    private static final class EndpointPathComparator implements Comparator<BaseOptionModel> {
 
         private final String syntax;
 
@@ -157,7 +177,7 @@ public final class EndpointHelper {
         }
 
         @Override
-        public int compare(EndpointPath path1, EndpointPath path2) {
+        public int compare(BaseOptionModel path1, BaseOptionModel path2) {
             int pos1 = syntax != null ? syntax.indexOf(path1.getName()) : -1;
             int pos2 = syntax != null ? syntax.indexOf(path2.getName()) : -1;
 
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/ComponentModel.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/ComponentModel.java
deleted file mode 100644
index 8ae3677..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/ComponentModel.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.tools.apt.model;
-
-public final class ComponentModel {
-
-    private String scheme;
-    private String extendsScheme;
-    private String syntax;
-    private String alternativeSyntax;
-    private String javaType;
-    private String title;
-    private String description;
-    private String firstVersion;
-    private String groupId;
-    private String artifactId;
-    private String versionId;
-    private String label;
-    private String verifiers;
-    private boolean consumerOnly;
-    private boolean producerOnly;
-    private boolean deprecated;
-    private String deprecationNote;
-    private boolean lenientProperties;
-    private boolean async;
-
-    public ComponentModel(String scheme) {
-        this.scheme = scheme;
-    }
-
-    public String getScheme() {
-        return scheme;
-    }
-
-    public String getExtendsScheme() {
-        return extendsScheme;
-    }
-
-    public void setExtendsScheme(String extendsScheme) {
-        this.extendsScheme = extendsScheme;
-    }
-
-    public String getSyntax() {
-        return syntax;
-    }
-
-    public void setSyntax(String syntax) {
-        this.syntax = syntax;
-    }
-
-    public String getAlternativeSyntax() {
-        return alternativeSyntax;
-    }
-
-    public void setAlternativeSyntax(String alternativeSyntax) {
-        this.alternativeSyntax = alternativeSyntax;
-    }
-
-    public String getJavaType() {
-        return javaType;
-    }
-
-    public void setJavaType(String javaType) {
-        this.javaType = javaType;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getFirstVersion() {
-        return firstVersion;
-    }
-
-    public void setFirstVersion(String firstVersion) {
-        this.firstVersion = firstVersion;
-    }
-
-    public String getGroupId() {
-        return groupId;
-    }
-
-    public void setGroupId(String groupId) {
-        this.groupId = groupId;
-    }
-
-    public String getArtifactId() {
-        return artifactId;
-    }
-
-    public void setArtifactId(String artifactId) {
-        this.artifactId = artifactId;
-    }
-
-    public String getVersionId() {
-        return versionId;
-    }
-
-    public void setVersionId(String versionId) {
-        this.versionId = versionId;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    public String getVerifiers() {
-        return verifiers;
-    }
-
-    public void setVerifiers(String verifiers) {
-        this.verifiers = verifiers;
-    }
-
-    public boolean isConsumerOnly() {
-        return consumerOnly;
-    }
-
-    public void setConsumerOnly(boolean consumerOnly) {
-        this.consumerOnly = consumerOnly;
-    }
-
-    public boolean isProducerOnly() {
-        return producerOnly;
-    }
-
-    public void setProducerOnly(boolean producerOnly) {
-        this.producerOnly = producerOnly;
-    }
-
-    public boolean isDeprecated() {
-        return deprecated;
-    }
-
-    public void setDeprecated(boolean deprecated) {
-        this.deprecated = deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public void setDeprecationNote(String deprecationNote) {
-        this.deprecationNote = deprecationNote;
-    }
-
-    public boolean isLenientProperties() {
-        return lenientProperties;
-    }
-
-    public void setLenientProperties(boolean lenientProperties) {
-        this.lenientProperties = lenientProperties;
-    }
-
-    public boolean isAsync() {
-        return async;
-    }
-
-    public void setAsync(boolean async) {
-        this.async = async;
-    }
-}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/ComponentOption.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/ComponentOption.java
deleted file mode 100644
index efec802..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/ComponentOption.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.tools.apt.model;
-
-import java.util.Set;
-
-import static org.apache.camel.tooling.util.Strings.isNullOrEmpty;
-
-public final class ComponentOption implements PropertyOption {
-
-    private String name;
-    private String displayName;
-    private String type;
-    private boolean required;
-    private String defaultValue;
-    private String defaultValueNote;
-    private String documentation;
-    private boolean deprecated;
-    private String deprecationNote;
-    private boolean secret;
-    private String group;
-    private String label;
-    private boolean enumType;
-    private Set<String> enums;
-    private String configurationClass;
-    private String configurationField;
-
-    public ComponentOption(String name, String displayName, String type, boolean required, String defaultValue, String defaultValueNote,
-                           String documentation, boolean deprecated, String deprecationNote, boolean secret, String group, String label,
-                           boolean enumType, Set<String> enums, String configurationClass, String nestedFieldName) {
-        this.name = name;
-        this.displayName = displayName;
-        this.type = type;
-        this.required = required;
-        this.defaultValue = defaultValue;
-        this.defaultValueNote = defaultValueNote;
-        this.documentation = documentation;
-        this.deprecated = deprecated;
-        this.deprecationNote = deprecationNote;
-        this.secret = secret;
-        this.group = group;
-        this.label = label;
-        this.enumType = enumType;
-        this.enums = enums;
-        this.configurationClass = configurationClass;
-        this.configurationField = nestedFieldName;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public boolean isRequired() {
-        return required;
-    }
-
-    public String getDefaultValue() {
-        return defaultValue;
-    }
-
-    public String getDocumentation() {
-        return documentation;
-    }
-
-    public boolean isDeprecated() {
-        return deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public boolean isSecret() {
-        return secret;
-    }
-
-    public String getDocumentationWithNotes() {
-        StringBuilder sb = new StringBuilder();
-        sb.append(documentation);
-
-        if (!isNullOrEmpty(defaultValueNote)) {
-            sb.append(". Default value notice: ").append(defaultValueNote);
-        }
-
-        if (!isNullOrEmpty(deprecationNote)) {
-            sb.append(". Deprecation note: ").append(deprecationNote);
-        }
-
-        return sb.toString();
-    }
-
-    public boolean isEnumType() {
-        return enumType;
-    }
-
-    public Set<String> getEnums() {
-        return enums;
-    }
-
-    public String getConfigurationClass() {
-        return configurationClass;
-    }
-
-    public String getConfigurationField() {
-        return configurationField;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        ComponentOption that = (ComponentOption) o;
-
-        if (!name.equals(that.name)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return name.hashCode();
-    }
-}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/EndpointOption.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/EndpointOption.java
deleted file mode 100644
index ff1a4c9..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/EndpointOption.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.tools.apt.model;
-
-import java.util.Set;
-
-import static org.apache.camel.tooling.util.Strings.isNullOrEmpty;
-
-public final class EndpointOption implements PropertyOption {
-
-    private String name;
-    private String displayName;
-    private String type;
-    private boolean required;
-    private String defaultValue;
-    private String defaultValueNote;
-    private String documentation;
-    private String optionalPrefix;
-    private String prefix;
-    private boolean multiValue;
-    private boolean deprecated;
-    private String deprecationNote;
-    private boolean secret;
-    private String group;
-    private String label;
-    private boolean enumType;
-    private Set<String> enums;
-    private String configurationClass;
-    private String configurationField;
-
-    public EndpointOption(String name, String displayName, String type, boolean required, String defaultValue, String defaultValueNote,
-                          String documentation, String optionalPrefix, String prefix, boolean multiValue,
-                          boolean deprecated, String deprecationNote, boolean secret, String group, String label,
-                          boolean enumType, Set<String> enums, String configurationClass, String nestedFieldName) {
-        this.name = name;
-        this.displayName = displayName;
-        this.type = type;
-        this.required = required;
-        this.defaultValue = defaultValue;
-        this.defaultValueNote = defaultValueNote;
-        this.documentation = documentation;
-        this.optionalPrefix = optionalPrefix;
-        this.prefix = prefix;
-        this.multiValue = multiValue;
-        this.deprecated = deprecated;
-        this.deprecationNote = deprecationNote;
-        this.secret = secret;
-        this.group = group;
-        this.label = label;
-        this.enumType = enumType;
-        this.enums = enums;
-        this.configurationClass = configurationClass;
-        this.configurationField = nestedFieldName;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public boolean isRequired() {
-        return required;
-    }
-
-    public String getDefaultValue() {
-        return defaultValue;
-    }
-
-    public String getDocumentation() {
-        return documentation;
-    }
-
-    public String getOptionalPrefix() {
-        return optionalPrefix;
-    }
-
-    public String getPrefix() {
-        return prefix;
-    }
-
-    public boolean isMultiValue() {
-        return multiValue;
-    }
-
-    public boolean isDeprecated() {
-        return deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public boolean isSecret() {
-        return secret;
-    }
-
-    public String getDocumentationWithNotes() {
-        StringBuilder sb = new StringBuilder();
-        sb.append(documentation);
-
-        if (!isNullOrEmpty(defaultValueNote)) {
-            sb.append(". Default value notice: ").append(defaultValueNote);
-        }
-        if (!isNullOrEmpty(deprecationNote)) {
-            sb.append(". Deprecation note: ").append(deprecationNote);
-        }
-
-        return sb.toString();
-    }
-
-    public boolean isEnumType() {
-        return enumType;
-    }
-
-    public Set<String> getEnums() {
-        return enums;
-    }
-
-    public String getConfigurationClass() {
-        return configurationClass;
-    }
-
-    public String getConfigurationField() {
-        return configurationField;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        EndpointOption that = (EndpointOption) o;
-
-        if (!name.equals(that.name)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return name.hashCode();
-    }
-}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/EndpointPath.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/EndpointPath.java
deleted file mode 100644
index 04b720b..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/EndpointPath.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.tools.apt.model;
-
-import java.util.Set;
-
-public final class EndpointPath {
-
-    private String name;
-    private String displayName;
-    private String type;
-    private boolean required;
-    private String defaultValue;
-    private String documentation;
-    private boolean deprecated;
-    private String deprecationNote;
-    private boolean secret;
-    private String group;
-    private String label;
-    private boolean enumType;
-    private Set<String> enums;
-
-    public EndpointPath(String name, String displayName, String type, boolean required, String defaultValue, String documentation,
-                        boolean deprecated, String deprecationNote, boolean secret, String group, String label,
-                        boolean enumType, Set<String> enums) {
-        this.name = name;
-        this.displayName = displayName;
-        this.type = type;
-        this.required = required;
-        this.defaultValue = defaultValue;
-        this.documentation = documentation;
-        this.deprecated = deprecated;
-        this.deprecationNote = deprecationNote;
-        this.secret = secret;
-        this.group = group;
-        this.label = label;
-        this.enumType = enumType;
-        this.enums = enums;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public boolean isRequired() {
-        return required;
-    }
-
-    public String getDefaultValue() {
-        return defaultValue;
-    }
-
-    public String getDocumentation() {
-        return documentation;
-    }
-
-    public boolean isDeprecated() {
-        return deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public boolean isSecret() {
-        return secret;
-    }
-
-    public boolean isEnumType() {
-        return enumType;
-    }
-
-    public Set<String> getEnums() {
-        return enums;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        EndpointPath that = (EndpointPath) o;
-
-        if (!name.equals(that.name)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        return name.hashCode();
-    }
-}
diff --git a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/PropertyOption.java b/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/PropertyOption.java
deleted file mode 100644
index 3bcfc77..0000000
--- a/tooling/apt/src/main/java/org/apache/camel/tools/apt/model/PropertyOption.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.tools.apt.model;
-
-public interface PropertyOption {
-
-    String getName();
-
-    String getType();
-
-    String getConfigurationField();
-
-}
diff --git a/tooling/apt/src/test/java/org/apache/camel/tools/apt/EndpointOptionComparatorTest.java b/tooling/apt/src/test/java/org/apache/camel/tools/apt/EndpointOptionComparatorTest.java
index adcf03b..ebb28fe 100644
--- a/tooling/apt/src/test/java/org/apache/camel/tools/apt/EndpointOptionComparatorTest.java
+++ b/tooling/apt/src/test/java/org/apache/camel/tools/apt/EndpointOptionComparatorTest.java
@@ -17,11 +17,10 @@
 package org.apache.camel.tools.apt;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
+import org.apache.camel.tooling.model.BaseOptionModel;
 import org.apache.camel.tools.apt.helper.EndpointHelper;
-import org.apache.camel.tools.apt.model.EndpointOption;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -39,27 +38,54 @@ public class EndpointOptionComparatorTest {
         String group3 = EndpointHelper.labelAsGroupName(label3, false, false);
         String group4 = EndpointHelper.labelAsGroupName(label4, false, false);
 
-        EndpointOption op1 = new EndpointOption("first", "First", "string", true, "", "", "blah", null, null, false,
-            false, null, false, group1, label1, false, null, null, null);
-        EndpointOption op2 = new EndpointOption("synchronous", "Synchronous", "string", true, "", "", "blah", null, null, false,
-            false, null, false, group2, label2, false, null, null, null);
-        EndpointOption op3 = new EndpointOption("second", "Second", "string", true, "", "", "blah", null, null, false,
-            false, null, false, group3, label3, false, null, null, null);
-        EndpointOption op4 = new EndpointOption("country", "Country", "string", true, "", "", "blah", null, null, false,
-            false, null, false, group4, label4, false, null, null, null);
+        EndpointOptionModel op1 = new EndpointOptionModel("first", "First", "string", true, "", "", "blah", null, false,
+            false, null, false, group1, label1, null, null, null);
+        EndpointOptionModel op2 = new EndpointOptionModel("synchronous", "Synchronous", "string", true, "", "blah", null, null, false,
+            false, null, false, group2, label2, null, null, null);
+        EndpointOptionModel op3 = new EndpointOptionModel("second", "Second", "string", true, "", "blah", null, null, false,
+            false, null, false, group3, label3, null, null, null);
+        EndpointOptionModel op4 = new EndpointOptionModel("country", "Country", "string", true, "", "blah", null, null, false,
+            false, null, false, group4, label4, null, null, null);
 
-        List<EndpointOption> list = new ArrayList<>();
+        List<EndpointOptionModel> list = new ArrayList<>();
         list.add(op1);
         list.add(op2);
         list.add(op3);
         list.add(op4);
 
         // then by label into the groups
-        Collections.sort(list, EndpointHelper.createGroupAndLabelComparator());
+        list.sort(EndpointHelper.createGroupAndLabelComparator());
 
         assertEquals("first", list.get(0).getName()); // common
         assertEquals("second", list.get(1).getName()); // common
         assertEquals("synchronous", list.get(2).getName()); // advanced
         assertEquals("country", list.get(3).getName()); // filter
     }
+
+    public static class EndpointOptionModel extends BaseOptionModel {
+        public EndpointOptionModel(String name, String displayName, String type, boolean required, String defaultValue,
+                                   String description, String optionalPrefix, String prefix,
+                                   boolean multiValue, boolean deprecated, String deprecationNote, boolean secret,
+                                   String group, String label, List<String> enums,
+                                   String configurationClass, String nestedFieldName) {
+            this.name = name;
+            this.displayName = displayName;
+            this.type = type;
+            this.required = required;
+            this.defaultValue = defaultValue;
+            this.description = description;
+            this.optionalPrefix = optionalPrefix;
+            this.prefix = prefix;
+            this.multiValue = multiValue;
+            this.deprecated = deprecated;
+            this.deprecationNote = deprecationNote;
+            this.secret = secret;
+            this.group = group;
+            this.label = label;
+            this.enums = enums;
+            this.configurationClass = configurationClass;
+            this.configurationField = nestedFieldName;
+        }
+
+    }
 }
diff --git a/tooling/apt/pom.xml b/tooling/camel-tooling-model/pom.xml
similarity index 52%
copy from tooling/apt/pom.xml
copy to tooling/camel-tooling-model/pom.xml
index 2a81ce2..ae38ecc 100644
--- a/tooling/apt/pom.xml
+++ b/tooling/camel-tooling-model/pom.xml
@@ -26,29 +26,12 @@
         <version>3.1.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>apt</artifactId>
+    <artifactId>camel-tooling-model</artifactId>
 
-    <name>Camel :: Annotation Processor</name>
-    <description>Processes Camel endpoint source code</description>
-
-    <properties>
-        <camel.osgi.import.pkg>
-            *
-        </camel.osgi.import.pkg>
-        <camel.osgi.export.pkg>
-            !*
-        </camel.osgi.export.pkg>
-    </properties>
+    <name>Camel :: Tooling :: Model</name>
+    <description>Tooling Model</description>
 
     <dependencies>
-
-        <!-- camel annotations -->
-        <dependency>
-            <groupId>org.apache.camel</groupId>
-            <artifactId>spi-annotations</artifactId>
-        </dependency>
-
-        <!-- use our patched ordered json-simple parser -->
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-util-json</artifactId>
@@ -58,49 +41,28 @@
             <artifactId>camel-tooling-util</artifactId>
         </dependency>
 
-        <!-- logging -->
-        <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-api</artifactId>
-            <scope>test</scope>
-        </dependency>
+        <!-- testing -->
         <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-core</artifactId>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit-jupiter-version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-slf4j-impl</artifactId>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit-jupiter-version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit-jupiter-version}</version>
             <scope>test</scope>
         </dependency>
-
-        <!-- testing -->
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
         </dependency>
-
     </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <configuration>
-                    <source>${jdk.version}</source>
-                    <target>${jdk.version}</target>
-                    <!-- Disable annotation processing for ourselves -->
-                    <proc>none</proc>
-                    <verbose>false</verbose>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java
new file mode 100644
index 0000000..2c4ce51
--- /dev/null
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseModel.java
@@ -0,0 +1,218 @@
+/*
+ * 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.tooling.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.camel.tooling.util.Strings;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public abstract class BaseModel<O extends BaseOptionModel> {
+
+    protected String kind;
+    protected String name;
+    protected String title;
+    protected String description;
+    protected String firstVersion;
+    protected String javaType;
+    protected String label;
+    protected boolean deprecated;
+    protected String deprecationNote;
+    protected final List<O> options = new ArrayList<>();
+
+    public static Comparator<BaseModel<?>> compareTitle() {
+        return (m1, m2) -> m1.getTitle().compareToIgnoreCase(m2.getTitle());
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getFirstVersion() {
+        return firstVersion;
+    }
+
+    public void setFirstVersion(String firstVersion) {
+        this.firstVersion = firstVersion;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public boolean isDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(boolean deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public String getDeprecationNote() {
+        return deprecationNote;
+    }
+
+    public void setDeprecationNote(String deprecationNote) {
+        this.deprecationNote = deprecationNote;
+    }
+
+    public String getJavaType() {
+        return javaType;
+    }
+
+    public void setJavaType(String javaType) {
+        this.javaType = javaType;
+    }
+
+    public List<O> getOptions() {
+        return options;
+    }
+
+    public void addOption(O option) {
+        options.add(option);
+    }
+
+    public String getShortJavaType() {
+        return Strings.getClassShortName(javaType);
+    }
+
+    public String getFirstVersionShort() {
+        return Strings.cutLastZeroDigit(firstVersion);
+    }
+
+    public static void parseModel(JsonObject mobj, BaseModel<?> model) {
+        model.setTitle(mobj.getString("title"));
+        model.setName(mobj.getString("name"));
+        model.setDescription(mobj.getString("description"));
+        model.setFirstVersion(mobj.getString("firstVersion"));
+        model.setLabel(mobj.getString("label"));
+        model.setDeprecated(mobj.getBooleanOrDefault("deprecated", false));
+        model.setDeprecationNote(mobj.getString("label"));
+        model.setJavaType(mobj.getString("javaType"));
+    }
+
+    public static void parseOption(JsonObject mp, BaseOptionModel option, String name) {
+        option.setName(name);
+        option.setKind(mp.getString("kind"));
+        option.setDisplayName(mp.getString("displayName"));
+        option.setGroup(mp.getString("group"));
+        option.setLabel(mp.getString("label"));
+        option.setRequired(mp.getBooleanOrDefault("required", false));
+        option.setType(mp.getString("type"));
+        option.setJavaType(mp.getString("javaType"));
+        option.setEnums(asStringList(mp.getCollection("enum")));
+        option.setOneOfs(asStringList(mp.getCollection("oneOf")));
+        option.setPrefix(mp.getString("prefix"));
+        option.setOptionalPrefix(mp.getString("optionalPrefix"));
+        option.setMultiValue(mp.getBooleanOrDefault("multiValue", false));
+        option.setDeprecated(mp.getBooleanOrDefault("deprecated", false));
+        option.setDeprecationNote(mp.getString("deprecationNote"));
+        option.setSecret(mp.getBooleanOrDefault("secret", false));
+        option.setDefaultValue(mp.get("defaultValue"));
+        option.setAsPredicate(mp.getBooleanOrDefault("asPredicate", false));
+        option.setConfigurationClass(mp.getString("configurationClass"));
+        option.setConfigurationField(mp.getString("configurationField"));
+        option.setDescription(mp.getString("description"));
+    }
+
+    public static JsonObject asJsonObject(List<? extends BaseOptionModel> options) {
+        JsonObject json = new JsonObject();
+        options.forEach(option -> json.put(option.getName(), asJsonObject(option)));
+        return json;
+    }
+
+    public static JsonObject asJsonObject(BaseOptionModel option) {
+        JsonObject prop = new JsonObject();
+        prop.put("kind", option.getKind());
+        prop.put("displayName", option.getDisplayName());
+        prop.put("group", option.getGroup());
+        prop.put("label", option.getLabel());
+        prop.put("required", option.isRequired());
+        prop.put("type", option.getType());
+        prop.put("javaType", option.getJavaType());
+        prop.put("enums", option.getEnums());
+        prop.put("oneOfs", option.getOneOfs());
+        prop.put("prefix", option.getPrefix());
+        prop.put("optionalPrefix", option.getOptionalPrefix());
+        prop.put("multiValue", option.isMultiValue());
+        prop.put("deprecated", option.isDeprecated());
+        prop.put("deprecationNote", option.getDeprecationNote());
+        prop.put("secret", option.isSecret());
+        prop.put("defaultValue", option.getDefaultValue());
+        prop.put("asPredicate", option.isAsPredicate());
+        prop.put("configurationClass", option.getConfigurationClass());
+        prop.put("configurationField", option.getConfigurationField());
+        prop.put("description", option.getDescription());
+        prop.entrySet().removeIf(e -> e.getValue() == null);
+        return prop;
+    }
+
+    protected static JsonObject deserialize(String json) {
+        try {
+            return (JsonObject) Jsoner.deserialize(json);
+        } catch (Exception e) {
+            // wrap parsing exceptions as runtime
+            throw new RuntimeException("Cannot parse json", e);
+        }
+    }
+
+    protected static List<String> asStringList(Collection<?> col) {
+        if (col != null) {
+            return col.stream().map(Object::toString).collect(Collectors.toList());
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java
similarity index 58%
rename from tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
rename to tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java
index 7ccd9bb..2ec4745 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java
@@ -14,34 +14,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.maven.packaging.model;
+package org.apache.camel.tooling.model;
+
+import java.util.List;
 
 import org.apache.camel.tooling.util.Strings;
 
-import static org.apache.camel.tooling.util.Strings.wrapCamelCaseWords;
-
-public class EndpointOptionModel {
-
-    private String name;
-    private String displayName;
-    private String kind;
-    private String group;
-    private String label;
-    private String required;
-    private String type;
-    private String javaType;
-    private String enums;
-    private String prefix;
-    private String multiValue;
-    private String deprecated;
-    private String deprecationNote;
-    private String secret;
-    private String defaultValue;
-    private String description;
-    private String enumValues;
-
-    // special for documentation rendering
-    private boolean newGroup;
+@SuppressWarnings("unused")
+public abstract class BaseOptionModel {
+
+    protected String name;
+
+    protected String kind;
+    protected String displayName;
+    protected String group;
+    protected String label;
+    protected boolean required;
+    protected String type;
+    protected String javaType;
+    protected List<String> enums;
+    protected List<String> oneOfs;
+    protected String prefix;
+    protected String optionalPrefix;
+    protected boolean multiValue;
+    protected boolean deprecated;
+    protected String deprecationNote;
+    protected boolean secret;
+    protected Object defaultValue;
+    protected String defaultValueNote;
+    protected boolean asPredicate;
+    protected String configurationClass;
+    protected String configurationField;
+    protected String description;
+
+    // todo: move this as a helper method
+    protected boolean newGroup; // special for documentation rendering
 
     public String getName() {
         return name;
@@ -51,14 +58,6 @@ public class EndpointOptionModel {
         this.name = name;
     }
 
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public void setDisplayName(String displayName) {
-        this.displayName = displayName;
-    }
-
     public String getKind() {
         return kind;
     }
@@ -67,6 +66,14 @@ public class EndpointOptionModel {
         this.kind = kind;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
     public String getGroup() {
         return group;
     }
@@ -83,11 +90,11 @@ public class EndpointOptionModel {
         this.label = label;
     }
 
-    public String getRequired() {
+    public boolean isRequired() {
         return required;
     }
 
-    public void setRequired(String required) {
+    public void setRequired(boolean required) {
         this.required = required;
     }
 
@@ -107,14 +114,22 @@ public class EndpointOptionModel {
         this.javaType = javaType;
     }
 
-    public String getEnums() {
+    public List<String> getEnums() {
         return enums;
     }
 
-    public void setEnums(String enums) {
+    public void setEnums(List<String> enums) {
         this.enums = enums;
     }
 
+    public List<String> getOneOfs() {
+        return oneOfs;
+    }
+
+    public void setOneOfs(List<String> oneOfs) {
+        this.oneOfs = oneOfs;
+    }
+
     public String getPrefix() {
         return prefix;
     }
@@ -123,23 +138,27 @@ public class EndpointOptionModel {
         this.prefix = prefix;
     }
 
-    public String getMultiValue() {
+    public String getOptionalPrefix() {
+        return optionalPrefix;
+    }
+
+    public void setOptionalPrefix(String optionalPrefix) {
+        this.optionalPrefix = optionalPrefix;
+    }
+
+    public boolean isMultiValue() {
         return multiValue;
     }
 
-    public void setMultiValue(String multiValue) {
+    public void setMultiValue(boolean multiValue) {
         this.multiValue = multiValue;
     }
 
     public boolean isDeprecated() {
-        return "true".equals(deprecated);
-    }
-
-    public String getDeprecated() {
         return deprecated;
     }
 
-    public void setDeprecated(String deprecated) {
+    public void setDeprecated(boolean deprecated) {
         this.deprecated = deprecated;
     }
 
@@ -151,36 +170,60 @@ public class EndpointOptionModel {
         this.deprecationNote = deprecationNote;
     }
 
-    public String getSecret() {
+    public boolean isSecret() {
         return secret;
     }
 
-    public void setSecret(String secret) {
+    public void setSecret(boolean secret) {
         this.secret = secret;
     }
 
-    public String getDefaultValue() {
+    public Object getDefaultValue() {
         return defaultValue;
     }
 
-    public void setDefaultValue(String defaultValue) {
+    public void setDefaultValue(Object defaultValue) {
         this.defaultValue = defaultValue;
     }
 
-    public String getDescription() {
-        return description;
+    public String getDefaultValueNote() {
+        return defaultValueNote;
     }
 
-    public void setDescription(String description) {
-        this.description = description;
+    public void setDefaultValueNote(String defaultValueNote) {
+        this.defaultValueNote = defaultValueNote;
+    }
+
+    public boolean isAsPredicate() {
+        return asPredicate;
     }
 
-    public String getEnumValues() {
-        return enumValues;
+    public void setAsPredicate(boolean asPredicate) {
+        this.asPredicate = asPredicate;
     }
 
-    public void setEnumValues(String enumValues) {
-        this.enumValues = enumValues;
+    public String getConfigurationClass() {
+        return configurationClass;
+    }
+
+    public void setConfigurationClass(String configurationClass) {
+        this.configurationClass = configurationClass;
+    }
+
+    public String getConfigurationField() {
+        return configurationField;
+    }
+
+    public void setConfigurationField(String configurationField) {
+        this.configurationField = configurationField;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
     }
 
     public boolean isNewGroup() {
@@ -191,45 +234,39 @@ public class EndpointOptionModel {
         this.newGroup = newGroup;
     }
 
+    public String getShortGroup() {
+        if (group.endsWith(" (advanced)")) {
+            return group.substring(0, group.length() - 11);
+        }
+        return group;
+    }
+
     public String getShortJavaType() {
         return Strings.getClassShortName(javaType);
     }
 
     @Deprecated
     public String getShortJavaType(int watermark) {
-
-        String text = Strings.getClassShortName(javaType);
-
+        String text = Strings.getClassShortName(type);
         // if its some kind of java object then lets wrap it as its long
         if ("object".equals(type)) {
-            text = wrapCamelCaseWords(text, watermark, " ");
+            text = Strings.wrapCamelCaseWords(text, watermark, " ");
         }
         return text;
     }
 
-    public String getShortGroup() {
-        if (group.endsWith(" (advanced)")) {
-            return group.substring(0, group.length() - 11);
-        }
-        return group;
-    }
-
     public String getShortDefaultValue(int watermark) {
-        if (defaultValue.isEmpty()) {
-            return "";
-        }
-        String text = defaultValue;
+        String text = defaultValue != null ? defaultValue.toString() : "";
         if (text.endsWith("<T>")) {
             text = text.substring(0, text.length() - 3);
         } else if (text.endsWith("<T>>")) {
             text = text.substring(0, text.length() - 4);
         }
-
         return text;
     }
 
     public String getShortName(int watermark) {
-        String text = wrapCamelCaseWords(name, watermark, " ");
+        String text = Strings.wrapCamelCaseWords(name, watermark, " ");
         // ensure the option name starts with lower-case
         return Character.toLowerCase(text.charAt(0)) + text.substring(1);
     }
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
new file mode 100644
index 0000000..5d137fc
--- /dev/null
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ComponentModel.java
@@ -0,0 +1,254 @@
+/*
+ * 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.tooling.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public class ComponentModel extends BaseModel<ComponentModel.ComponentOptionModel> {
+
+    protected String scheme;
+    protected String extendsScheme;
+    protected String alternativeSchemes;
+    protected String syntax;
+    protected String alternativeSyntax;
+    protected boolean async;
+    protected boolean consumerOnly;
+    protected boolean producerOnly;
+    protected boolean lenientProperties;
+    protected String verifiers;
+    protected String groupId;
+    protected String artifactId;
+    protected String version;
+
+    protected final List<EndpointOptionModel> endpointOptions = new ArrayList<>();
+
+    public static ComponentModel generateComponentModel(String json) {
+        JsonObject obj = deserialize(json);
+        JsonObject mobj = (JsonObject) obj.get("component");
+        ComponentModel model = new ComponentModel();
+        parseModel(mobj, model);
+        model.setScheme(mobj.getString("scheme"));
+        model.setExtendsScheme(mobj.getString("extendsScheme"));
+        model.setAlternativeSchemes(mobj.getString("alternativeSchemes"));
+        model.setSyntax(mobj.getString("syntax"));
+        model.setAlternativeSyntax(mobj.getString("alternativeSyntax"));
+        model.setAsync(mobj.getBooleanOrDefault("async", false));
+        model.setConsumerOnly(mobj.getBooleanOrDefault("consumerOnly", false));
+        model.setProducerOnly(mobj.getBooleanOrDefault("producerOnly", false));
+        model.setLenientProperties(mobj.getBooleanOrDefault("lenientProperties", false));
+        model.setGroupId(mobj.getString("groupId"));
+        model.setArtifactId(mobj.getString("artifactId"));
+        model.setVersion(mobj.getString("version"));
+        JsonObject mcprp = (JsonObject) obj.get("componentProperties");
+        for (Map.Entry<String, Object> entry : mcprp.entrySet()) {
+            JsonObject mp = (JsonObject) entry.getValue();
+            ComponentOptionModel option = new ComponentOptionModel();
+            parseOption(mp, option, entry.getKey());
+            model.addComponentOption(option);
+        }
+        JsonObject mprp = (JsonObject) obj.get("properties");
+        for (Map.Entry<String, Object> entry : mprp.entrySet()) {
+            JsonObject mp = (JsonObject) entry.getValue();
+            EndpointOptionModel option = new EndpointOptionModel();
+            parseOption(mp, option, entry.getKey());
+            model.addEndpointOption(option);
+        }
+        return model;
+    }
+
+    public static String createParameterJsonSchema(ComponentModel model) {
+        JsonObject obj = new JsonObject();
+        obj.put("kind", model.getKind());
+        obj.put("name", model.getName());
+        obj.put("scheme", model.getScheme());
+        obj.put("extendsScheme", model.getExtendsScheme());
+        obj.put("alternativeSchemes", model.getAlternativeSchemes());
+        obj.put("syntax", model.getSyntax());
+        obj.put("alternativeSyntax", model.getAlternativeSyntax());
+        obj.put("title", model.getTitle());
+        obj.put("description", model.getDescription());
+        obj.put("label", model.getLabel());
+        obj.put("deprecated", model.isDeprecated());
+        obj.put("deprecationNote", model.getDeprecationNote());
+        obj.put("async", model.isAsync());
+        obj.put("consumerOnly", model.isConsumerOnly());
+        obj.put("producerOnly", model.isProducerOnly());
+        obj.put("lenientProperties", model.isLenientProperties());
+        obj.put("javaType", model.getJavaType());
+        obj.put("firstVersion", model.getFirstVersion());
+        obj.put("verifiers", model.getVerifiers());
+        obj.put("groupId", model.getGroupId());
+        obj.put("artifactId", model.getArtifactId());
+        obj.put("version", model.getVersion());
+        obj.entrySet().removeIf(e -> e.getValue() == null);
+        JsonObject wrapper = new JsonObject();
+        wrapper.put("component", obj);
+        wrapper.put("componentProperties", asJsonObject(model.getComponentOptions()));
+        wrapper.put("properties", asJsonObject(model.getEndpointOptions()));
+        return Jsoner.prettyPrint(Jsoner.serialize(wrapper), 2, 2);
+    }
+
+    public ComponentModel() {
+        setKind("component");
+    }
+
+    public String getScheme() {
+        return scheme;
+    }
+
+    public void setScheme(String scheme) {
+        this.scheme = scheme;
+    }
+
+    public String getExtendsScheme() {
+        return extendsScheme;
+    }
+
+    public void setExtendsScheme(String extendsScheme) {
+        this.extendsScheme = extendsScheme;
+    }
+
+    public String getAlternativeSchemes() {
+        return alternativeSchemes;
+    }
+
+    public void setAlternativeSchemes(String alternativeSchemes) {
+        this.alternativeSchemes = alternativeSchemes;
+    }
+
+    public String getSyntax() {
+        return syntax;
+    }
+
+    public void setSyntax(String syntax) {
+        this.syntax = syntax;
+    }
+
+    public String getAlternativeSyntax() {
+        return alternativeSyntax;
+    }
+
+    public void setAlternativeSyntax(String alternativeSyntax) {
+        this.alternativeSyntax = alternativeSyntax;
+    }
+
+    public boolean isAsync() {
+        return async;
+    }
+
+    public void setAsync(boolean async) {
+        this.async = async;
+    }
+
+    public boolean isConsumerOnly() {
+        return consumerOnly;
+    }
+
+    public void setConsumerOnly(boolean consumerOnly) {
+        this.consumerOnly = consumerOnly;
+    }
+
+    public boolean isProducerOnly() {
+        return producerOnly;
+    }
+
+    public void setProducerOnly(boolean producerOnly) {
+        this.producerOnly = producerOnly;
+    }
+
+    public boolean isLenientProperties() {
+        return lenientProperties;
+    }
+
+    public void setLenientProperties(boolean lenientProperties) {
+        this.lenientProperties = lenientProperties;
+    }
+
+    public String getVerifiers() {
+        return verifiers;
+    }
+
+    public void setVerifiers(String verifiers) {
+        this.verifiers = verifiers;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public List<ComponentOptionModel> getComponentOptions() {
+        return super.getOptions();
+    }
+
+    public void addComponentOption(ComponentOptionModel option) {
+        super.addOption(option);
+    }
+
+    public List<EndpointOptionModel> getEndpointOptions() {
+        return endpointOptions;
+    }
+
+    public void addEndpointOption(EndpointOptionModel option) {
+        endpointOptions.add(option);
+    }
+
+    public List<EndpointOptionModel> getEndpointParameterOptions() {
+        return endpointOptions.stream()
+                .filter(o -> "parameter".equals(o.getKind()))
+                .collect(Collectors.toList());
+    }
+
+    public List<EndpointOptionModel> getEndpointPathOptions() {
+        return endpointOptions.stream()
+                .filter(o -> "path".equals(o.getKind()))
+                .collect(Collectors.toList());
+    }
+
+    public static class ComponentOptionModel extends BaseOptionModel {
+
+    }
+
+    public static class EndpointOptionModel extends BaseOptionModel {
+
+    }
+}
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DataFormatModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DataFormatModel.java
new file mode 100644
index 0000000..cb50eaa
--- /dev/null
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/DataFormatModel.java
@@ -0,0 +1,122 @@
+/*
+ * 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.tooling.model;
+
+import java.util.Map;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public class DataFormatModel extends BaseModel<DataFormatModel.DataFormatOptionModel> {
+
+    protected String modelName;
+    protected String modelJavaType;
+    protected String groupId;
+    protected String artifactId;
+    protected String version;
+
+    public static DataFormatModel generateDataFormatModel(String json) {
+        JsonObject obj = deserialize(json);
+        JsonObject mobj = (JsonObject) obj.get("dataformat");
+        DataFormatModel model = new DataFormatModel();
+        parseModel(mobj, model);
+        model.setModelName(mobj.getString("modelName"));
+        model.setModelJavaType(mobj.getString("modelJavaType"));
+        model.setGroupId(mobj.getString("groupId"));
+        model.setArtifactId(mobj.getString("artifactId"));
+        model.setVersion(mobj.getString("version"));
+        JsonObject mprp = (JsonObject) obj.get("properties");
+        for (Map.Entry<String, Object> entry : mprp.entrySet()) {
+            JsonObject mp = (JsonObject) entry.getValue();
+            DataFormatOptionModel option = new DataFormatOptionModel();
+            parseOption(mp, option, entry.getKey());
+            model.addOption(option);
+        }
+        return model;
+    }
+
+    public static String createParameterJsonSchema(DataFormatModel model) {
+        JsonObject obj = new JsonObject();
+        obj.put("kind", model.getKind());
+        obj.put("name", model.getName());
+        obj.put("modelName", model.getModelName());
+        obj.put("title", model.getTitle());
+        obj.put("description", model.getDescription());
+        obj.put("deprecated", model.isDeprecated());
+        obj.put("deprecationNote", model.getDeprecationNote());
+        obj.put("firstVersion", model.getFirstVersion());
+        obj.put("label", model.getLabel());
+        obj.put("javaType", model.getJavaType());
+        obj.put("modelJavaType", model.getModelJavaType());
+        obj.put("groupId", model.getGroupId());
+        obj.put("artifactId", model.getArtifactId());
+        obj.put("version", model.getVersion());
+        obj.entrySet().removeIf(e -> e.getValue() == null);
+        JsonObject wrapper = new JsonObject();
+        wrapper.put("dataformat", obj);
+        wrapper.put("properties", asJsonObject(model.getOptions()));
+        return Jsoner.prettyPrint(Jsoner.serialize(wrapper), 2, 2);
+    }
+
+    public static class DataFormatOptionModel extends BaseOptionModel {
+
+    }
+
+    public DataFormatModel() {
+        setKind("dataformat");
+    }
+
+    public String getModelName() {
+        return modelName;
+    }
+
+    public void setModelName(String modelName) {
+        this.modelName = modelName;
+    }
+
+    public String getModelJavaType() {
+        return modelJavaType;
+    }
+
+    public void setModelJavaType(String modelJavaType) {
+        this.modelJavaType = modelJavaType;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+}
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java
new file mode 100644
index 0000000..ebdf0f6
--- /dev/null
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/EipModel.java
@@ -0,0 +1,127 @@
+/*
+ * 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.tooling.model;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public class EipModel extends BaseModel<EipModel.EipOptionModel> {
+
+    protected boolean input;          // used in models from camel-core-engine
+    protected boolean output;         // used in models from camel-core-engine
+
+    public static EipModel generateEipModel(String json) {
+        JsonObject obj = deserialize(json);
+        JsonObject mobj = (JsonObject) obj.get("model");
+        EipModel model = new EipModel();
+        parseModel(mobj, model);
+        model.setInput(mobj.getBooleanOrDefault("input", false));
+        model.setOutput(mobj.getBooleanOrDefault("output", false));
+        JsonObject mprp = (JsonObject) obj.get("properties");
+        for (Map.Entry<String, Object> entry : mprp.entrySet()) {
+            JsonObject mp = (JsonObject) entry.getValue();
+            EipOptionModel option = new EipOptionModel();
+            parseOption(mp, option, entry.getKey());
+            model.addOption(option);
+        }
+        return model;
+    }
+
+    public static String createParameterJsonSchema(EipModel model) {
+        JsonObject obj = new JsonObject();
+        obj.put("kind", model.getKind());
+        obj.put("name", model.getName());
+        obj.put("title", model.getTitle());
+        obj.put("description", model.getDescription());
+        obj.put("firstVersion", model.getFirstVersion());
+        obj.put("javaType", model.getJavaType());
+        obj.put("label", model.getLabel());
+        obj.put("deprecated", model.isDeprecated());
+        obj.put("deprecationNote", model.getDeprecationNote());
+        obj.put("input", model.isInput());
+        obj.put("output", model.isOutput());
+        obj.entrySet().removeIf(e -> e.getValue() == null);
+        JsonObject wrapper = new JsonObject();
+        wrapper.put("model", obj);
+        wrapper.put("properties", asJsonObject(model.getOptions()));
+        return Jsoner.prettyPrint(Jsoner.serialize(wrapper), 2, 2);
+    }
+
+    public static JsonObject asJsonObject(List<? extends BaseOptionModel> options) {
+        JsonObject json = new JsonObject();
+        options.forEach(option -> json.put(option.getName(), asJsonObject(option)));
+        return json;
+    }
+
+    public static JsonObject asJsonObject(BaseOptionModel option) {
+        JsonObject prop = new JsonObject();
+        prop.put("kind", option.getKind());
+        prop.put("displayName", option.getDisplayName());
+        prop.put("group", option.getGroup());
+        prop.put("label", option.getLabel());
+        prop.put("required", option.isRequired());
+        prop.put("type", option.getType());
+        prop.put("javaType", option.getJavaType());
+        prop.put("enums", option.getEnums());
+        prop.put("oneOfs", option.getOneOfs());
+        prop.put("prefix", option.getPrefix());
+        prop.put("optionalPrefix", option.getOptionalPrefix());
+        prop.put("multiValue", option.isMultiValue() ? Boolean.TRUE : null);
+        prop.put("deprecated", option.isDeprecated());
+        prop.put("deprecationNote", option.getDeprecationNote());
+        prop.put("secret", option.isSecret());
+        prop.put("defaultValue", option.getDefaultValue());
+        prop.put("asPredicate", option.isAsPredicate() ? Boolean.TRUE : null);
+        prop.put("configurationClass", option.getConfigurationClass());
+        prop.put("configurationField", option.getConfigurationField());
+        prop.put("description", option.getDescription());
+        prop.entrySet().removeIf(e -> e.getValue() == null);
+        return prop;
+    }
+
+    public EipModel() {
+        setKind("model");
+    }
+
+    public boolean isInput() {
+        return input;
+    }
+
+    public void setInput(boolean input) {
+        this.input = input;
+    }
+
+    public boolean isOutput() {
+        return output;
+    }
+
+    public void setOutput(boolean output) {
+        this.output = output;
+    }
+
+    public String getDocLink() {
+        // lets store EIP docs in a sub-folder as we have many EIPs
+        return "src/main/docs/eips/";
+    }
+
+    public static class EipOptionModel extends BaseOptionModel {
+
+    }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ExampleModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ExampleModel.java
similarity index 88%
rename from tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ExampleModel.java
rename to tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ExampleModel.java
index 3e81a84..dde2c14 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ExampleModel.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/ExampleModel.java
@@ -14,16 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.maven.packaging.model;
+package org.apache.camel.tooling.model;
 
 public class ExampleModel {
 
-    private String fileName;
-    private String title;
-    private String category = "";
-    private String description = "";
-    private String readmeFileName = "readme.md";
-    private String deprecated;
+    protected String fileName;
+    protected String title;
+    protected String category = "";
+    protected String description = "";
+    protected String readmeFileName = "readme.md";
+    protected String deprecated;
 
     public String getFileName() {
         return fileName;
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/LanguageModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/LanguageModel.java
new file mode 100644
index 0000000..068ecca
--- /dev/null
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/LanguageModel.java
@@ -0,0 +1,122 @@
+/*
+ * 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.tooling.model;
+
+import java.util.Map;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public class LanguageModel extends BaseModel<LanguageModel.LanguageOptionModel> {
+
+    protected String modelName;
+    protected String modelJavaType;
+    protected String groupId;
+    protected String artifactId;
+    protected String version;
+
+    public static LanguageModel generateLanguageModel(String json) {
+        JsonObject obj = deserialize(json);
+        JsonObject mobj = (JsonObject) obj.get("language");
+        LanguageModel model = new LanguageModel();
+        parseModel(mobj, model);
+        model.setModelName(mobj.getString("modelName"));
+        model.setModelJavaType(mobj.getString("modelJavaType"));
+        model.setGroupId(mobj.getString("groupId"));
+        model.setArtifactId(mobj.getString("artifactId"));
+        model.setVersion(mobj.getString("version"));
+        JsonObject mprp = (JsonObject) obj.get("properties");
+        for (Map.Entry<String, Object> entry : mprp.entrySet()) {
+            JsonObject mp = (JsonObject) entry.getValue();
+            LanguageOptionModel option = new LanguageOptionModel();
+            parseOption(mp, option, entry.getKey());
+            model.addOption(option);
+        }
+        return model;
+    }
+
+    public static String createParameterJsonSchema(LanguageModel model) {
+        JsonObject obj = new JsonObject();
+        obj.put("kind", model.getKind());
+        obj.put("name", model.getName());
+        obj.put("modelName", model.getModelName());
+        obj.put("title", model.getTitle());
+        obj.put("description", model.getDescription());
+        obj.put("deprecated", model.isDeprecated());
+        obj.put("deprecationNote", model.getDeprecationNote());
+        obj.put("firstVersion", model.getFirstVersion());
+        obj.put("label", model.getLabel());
+        obj.put("javaType", model.getJavaType());
+        obj.put("modelJavaType", model.getModelJavaType());
+        obj.put("groupId", model.getGroupId());
+        obj.put("artifactId", model.getArtifactId());
+        obj.put("version", model.getVersion());
+        obj.entrySet().removeIf(e -> e.getValue() == null);
+        JsonObject wrapper = new JsonObject();
+        wrapper.put("language", obj);
+        wrapper.put("properties", asJsonObject(model.getOptions()));
+        return Jsoner.prettyPrint(Jsoner.serialize(wrapper), 2, 2);
+    }
+
+    public static class LanguageOptionModel extends BaseOptionModel {
+
+    }
+
+    public LanguageModel() {
+        setKind("language");
+    }
+
+    public String getModelName() {
+        return modelName;
+    }
+
+    public void setModelName(String modelName) {
+        this.modelName = modelName;
+    }
+
+    public String getModelJavaType() {
+        return modelJavaType;
+    }
+
+    public void setModelJavaType(String modelJavaType) {
+        this.modelJavaType = modelJavaType;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+}
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/OtherModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/OtherModel.java
new file mode 100644
index 0000000..db3398f
--- /dev/null
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/OtherModel.java
@@ -0,0 +1,86 @@
+/*
+ * 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.tooling.model;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+
+public class OtherModel extends BaseModel<BaseOptionModel> {
+
+    protected String groupId;
+    protected String artifactId;
+    protected String version;
+
+    public static OtherModel generateOtherModel(String json) {
+        JsonObject obj = deserialize(json);
+        JsonObject mobj = (JsonObject) obj.get("other");
+        OtherModel model = new OtherModel();
+        parseModel(mobj, model);
+        model.setGroupId(mobj.getString("groupId"));
+        model.setArtifactId(mobj.getString("artifactId"));
+        model.setVersion(mobj.getString("version"));
+        return model;
+    }
+
+    public static String createJsonSchema(OtherModel model) {
+        JsonObject obj = new JsonObject();
+        obj.put("kind", "other");
+        obj.put("name", model.getName());
+        obj.put("title", model.getTitle());
+        obj.put("description", model.getDescription());
+        obj.put("deprecated", model.isDeprecated());
+        obj.put("deprecationNote", model.getDeprecationNote());
+        obj.put("firstVersion", model.getFirstVersion());
+        obj.put("label", model.getLabel());
+        obj.put("groupId", model.getGroupId());
+        obj.put("artifactId", model.getArtifactId());
+        obj.put("version", model.getVersion());
+        obj.entrySet().removeIf(e -> e.getValue() == null);
+        JsonObject wrapper = new JsonObject();
+        wrapper.put("other", obj);
+        return Jsoner.prettyPrint(Jsoner.serialize(wrapper), 2, 2);
+    }
+
+    public OtherModel() {
+        setKind("other");
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/model/ComponentOptionModelTest.java b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/ComponentOptionModelTest.java
similarity index 83%
rename from tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/model/ComponentOptionModelTest.java
rename to tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/ComponentOptionModelTest.java
index e045b41..9f0addb9 100644
--- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/model/ComponentOptionModelTest.java
+++ b/tooling/camel-tooling-model/src/test/java/org/apache/camel/tooling/model/ComponentOptionModelTest.java
@@ -14,19 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.maven.packaging.model;
+package org.apache.camel.tooling.model;
 
+import org.apache.camel.tooling.model.ComponentModel;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 public class ComponentOptionModelTest {
 
-    ComponentOptionModel componentOptionModelUnderTest;
+    ComponentModel.ComponentOptionModel componentOptionModelUnderTest;
 
     @BeforeEach
     public void setup() {
-        componentOptionModelUnderTest = new ComponentOptionModel();
+        componentOptionModelUnderTest = new ComponentModel.ComponentOptionModel();
     }
 
     @Test
diff --git a/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/PackageHelper.java b/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/PackageHelper.java
index 2443b62..19345dd 100644
--- a/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/PackageHelper.java
+++ b/tooling/camel-tooling-util/src/main/java/org/apache/camel/tooling/util/PackageHelper.java
@@ -164,4 +164,20 @@ public final class PackageHelper {
     }
 
 
+    /**
+     * Extract the model kind from a given json schema
+     */
+    public static String getSchemaKind(String json) {
+        int i = json.indexOf("\"kind\"");
+        if (i >= 0) {
+            int s = json.indexOf("\"", i + 6);
+            if (s >= 0) {
+                int e = json.indexOf("\"", s + 1);
+                if (e >= 0) {
+                    return json.substring(s + 1, e);
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/tooling/camel-tooling-util/src/test/java/org/apache/camel/tooling/util/PackageHelperTest.java b/tooling/camel-tooling-util/src/test/java/org/apache/camel/tooling/util/PackageHelperTest.java
index 870bf66..24c3e3a 100644
--- a/tooling/camel-tooling-util/src/test/java/org/apache/camel/tooling/util/PackageHelperTest.java
+++ b/tooling/camel-tooling-util/src/test/java/org/apache/camel/tooling/util/PackageHelperTest.java
@@ -45,4 +45,10 @@ public class PackageHelperTest {
         assertTrue(jsonFiles.containsKey("b"), "Files b.json must be found");
         assertFalse(jsonFiles.containsKey("c"), "File c.txt must not be found");
     }
+
+    @Test
+    public void testGetSchemaKind() throws Exception {
+        String json = PackageHelper.loadText(ResourceUtils.getResourceAsFile("json/aop.json"));
+        assertEquals("model", PackageHelper.getSchemaKind(json));
+    }
 }
diff --git a/tooling/maven/camel-package-maven-plugin/pom.xml b/tooling/maven/camel-package-maven-plugin/pom.xml
index f951028..2fd7625 100644
--- a/tooling/maven/camel-package-maven-plugin/pom.xml
+++ b/tooling/maven/camel-package-maven-plugin/pom.xml
@@ -54,6 +54,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-tooling-util</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-tooling-model</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.mvel</groupId>
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
index c9ce963..41e2226 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
@@ -39,14 +39,12 @@ import java.util.stream.Collectors;
 import javax.annotation.Generated;
 
 import org.apache.camel.maven.packaging.generics.GenericsUtil;
-import org.apache.camel.maven.packaging.model.ComponentModel;
-import org.apache.camel.maven.packaging.model.ComponentOptionModel;
-import org.apache.camel.maven.packaging.model.EndpointOptionModel;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 import org.apache.camel.spi.UriParams;
 import org.apache.camel.spi.UriPath;
-import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.ComponentModel.EndpointOptionModel;
 import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.camel.tooling.util.srcgen.GenericType;
@@ -62,11 +60,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
 
-import static org.apache.camel.maven.packaging.AbstractGeneratorMojo.updateResource;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.getSafeValue;
-import static org.apache.camel.tooling.util.PackageHelper.findCamelDirectory;
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-
 /**
  * Generate Endpoint DSL source files for Components.
  */
@@ -147,7 +140,7 @@ public class EndpointDslMojo extends AbstractMojo {
         }
 
         if (outputDir == null) {
-            outputDir = findCamelDirectory(baseDir, "core/camel-endpointdsl/src/main/java");
+            outputDir = PackageHelper.findCamelDirectory(baseDir, "core/camel-endpointdsl/src/main/java");
         }
 
         Map<File, Supplier<String>> files;
@@ -185,7 +178,7 @@ public class EndpointDslMojo extends AbstractMojo {
 
     private static String loadJson(File file) {
         try {
-            return loadText(file);
+            return PackageHelper.loadText(file);
         } catch (IOException e) {
             throw new IOError(e);
         }
@@ -218,7 +211,7 @@ public class EndpointDslMojo extends AbstractMojo {
             for (String componentName : componentNames) {
                 String json = loadComponentJson(jsonFiles, componentName);
                 if (json != null) {
-                    ComponentModel model = generateComponentModel(componentName, json);
+                    ComponentModel model = ComponentModel.generateComponentModel(json);
                     allModels.add(model);
                 }
             }
@@ -261,7 +254,7 @@ public class EndpointDslMojo extends AbstractMojo {
 
         boolean hasAdvanced = false;
         for (EndpointOptionModel option : model.getEndpointOptions()) {
-            if (option.getLabel().contains("advanced")) {
+            if (option.getLabel() != null && option.getLabel().contains("advanced")) {
                 hasAdvanced = true;
                 break;
             }
@@ -385,21 +378,22 @@ public class EndpointDslMojo extends AbstractMojo {
             }
 
             List<JavaClass> targets = new ArrayList<>();
-            if (option.getLabel() != null) {
-                if (option.getLabel().contains("producer")) {
-                    if (option.getLabel().contains("advanced")) {
+            String label = option.getLabel() != null ? option.getLabel() : "";
+            if (label != null) {
+                if (label.contains("producer")) {
+                    if (label.contains("advanced")) {
                         targets.add(advancedProducerClass != null ? advancedProducerClass : advancedBuilderClass);
                     } else {
                         targets.add(producerClass != null ? producerClass : builderClass);
                     }
-                } else if (option.getLabel().contains("consumer")) {
-                    if (option.getLabel().contains("advanced")) {
+                } else if (label.contains("consumer")) {
+                    if (label.contains("advanced")) {
                         targets.add(advancedConsumerClass != null ? advancedConsumerClass : advancedBuilderClass);
                     } else {
                         targets.add(consumerClass != null ? consumerClass : builderClass);
                     }
                 } else {
-                    if (option.getLabel().contains("advanced")) {
+                    if (label.contains("advanced")) {
                         targets.add(advancedConsumerClass);
                         targets.add(advancedProducerClass);
                         targets.add(advancedBuilderClass);
@@ -434,7 +428,7 @@ public class EndpointDslMojo extends AbstractMojo {
                             "doSetProperty(\"" + option.getName() + "\", " + option.getName() + ");",
                             "return this;\n");
 
-                if ("true".equals(option.getDeprecated())) {
+                if (option.isDeprecated()) {
                     fluent.addAnnotation(Deprecated.class);
                 }
                 if (!Strings.isEmpty(option.getDescription())) {
@@ -449,11 +443,11 @@ public class EndpointDslMojo extends AbstractMojo {
                     // context-path and not as individual options
                     // so lets only mark query parameters that are required as
                     // required
-                    if ("parameter".equals(option.getKind()) && "true".equals(option.getRequired())) {
+                    if ("parameter".equals(option.getKind()) && option.isRequired()) {
                         desc += "\nRequired: true";
                     }
                     // include default value (if any)
-                    if (!option.getDefaultValue().isEmpty()) {
+                    if (option.getDefaultValue() != null) {
                         desc += "\nDefault: " + option.getDefaultValue();
                     }
                     desc += "\nGroup: " + option.getGroup();
@@ -470,7 +464,7 @@ public class EndpointDslMojo extends AbstractMojo {
                             "doSetProperty(\"" + option.getName() + "\", " + option.getName() + ");",
                             "return this;\n");
 
-                    if ("true".equals(option.getDeprecated())) {
+                    if (option.isDeprecated()) {
                         fluent.addAnnotation(Deprecated.class);
                     }
                     if (!Strings.isEmpty(option.getDescription())) {
@@ -485,11 +479,11 @@ public class EndpointDslMojo extends AbstractMojo {
                         // entire context-path and not as individual options
                         // so lets only mark query parameters that are required
                         // as required
-                        if ("parameter".equals(option.getKind()) && "true".equals(option.getRequired())) {
+                        if ("parameter".equals(option.getKind()) && option.isRequired()) {
                             desc += "\nRequired: true";
                         }
                         // include default value (if any)
-                        if (!option.getDefaultValue().isEmpty()) {
+                        if (option.getDefaultValue() != null) {
                             desc += "\nDefault: " + option.getDefaultValue();
                         }
                         desc += "\nGroup: " + option.getGroup();
@@ -700,27 +694,28 @@ public class EndpointDslMojo extends AbstractMojo {
             if ("path".equals(option.getKind())) {
                 desc += "\n";
                 desc += "\nPath parameter: " + option.getName();
-                if ("true".equals(option.getRequired())) {
+                if (option.isRequired()) {
                     desc += " (required)";
                 }
-                if ("true".equals(option.getDeprecated())) {
+                if (option.isDeprecated()) {
                     desc += " <strong>deprecated</strong>";
                 }
                 desc += "\n" + option.getDescription();
-                if (!Strings.isEmpty(option.getDefaultValue())) {
+                if (option.getDefaultValue() != null) {
                     desc += "\nDefault value: " + option.getDefaultValue();
                 }
-                if (!Strings.isEmpty(option.getEnumValues())) {
-                    desc += "\nThe value can be one of: " + wrapEnumValues(option.getEnumValues());
+                // TODO: default value note ?
+                if (option.getEnums() != null && !option.getEnums().isEmpty()) {
+                    desc += "\nThe value can be one of: " + wrapEnumValues(option.getEnums());
                 }
             }
         }
         return desc;
     }
 
-    private String wrapEnumValues(String enumValues) {
+    private String wrapEnumValues(List<String> enumValues) {
         // comma to space so we can wrap words (which uses space)
-        return enumValues.replace(",", ", ");
+        return String.join(", ", enumValues);
     }
 
     private String getComponentNameFromType(String type) {
@@ -822,7 +817,7 @@ public class EndpointDslMojo extends AbstractMojo {
         return optionClass;
     }
 
-    private GenericType getType(JavaClass javaClass, Map<String, JavaClass> enumClasses, String enums, String type) {
+    private GenericType getType(JavaClass javaClass, Map<String, JavaClass> enumClasses, List<String> enums, String type) {
         type = type.trim();
         // Check if this is an array
         if (type.endsWith("[]")) {
@@ -938,9 +933,9 @@ public class EndpointDslMojo extends AbstractMojo {
 
     private static String loadJsonOfType(Map<File, Supplier<String>> jsonFiles, String modelName, String type) {
         for (Map.Entry<File, Supplier<String>> entry : jsonFiles.entrySet()) {
-            if (entry.getKey().getName().equals(modelName + ".json")) {
+            if (entry.getKey().getName().equals(modelName + PackageHelper.JSON_SUFIX)) {
                 String json = entry.getValue().get();
-                if (json.contains("\"kind\": \"" + type + "\"")) {
+                if ("type".equals(PackageHelper.getSchemaKind(json))) {
                     return json;
                 }
             }
@@ -948,67 +943,6 @@ public class EndpointDslMojo extends AbstractMojo {
         return null;
     }
 
-    private static ComponentModel generateComponentModel(String componentName, String json) {
-        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
-
-        ComponentModel component = new ComponentModel();
-        component.setScheme(getSafeValue("scheme", rows));
-        component.setSyntax(getSafeValue("syntax", rows));
-        component.setAlternativeSyntax(getSafeValue("alternativeSyntax", rows));
-        component.setTitle(getSafeValue("title", rows));
-        component.setDescription(getSafeValue("description", rows));
-        component.setFirstVersion(JSonSchemaHelper.getSafeValue("firstVersion", rows));
-        component.setLabel(getSafeValue("label", rows));
-        component.setDeprecated(getSafeValue("deprecated", rows));
-        component.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        component.setConsumerOnly(getSafeValue("consumerOnly", rows));
-        component.setProducerOnly(getSafeValue("producerOnly", rows));
-        component.setJavaType(getSafeValue("javaType", rows));
-        component.setGroupId(getSafeValue("groupId", rows));
-        component.setArtifactId(getSafeValue("artifactId", rows));
-        component.setVersion(getSafeValue("version", rows));
-
-        rows = JSonSchemaHelper.parseJsonSchema("componentProperties", json, true);
-        for (Map<String, String> row : rows) {
-            ComponentOptionModel option = new ComponentOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setKind(getSafeValue("kind", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setDeprecated(getSafeValue("deprecated", row));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setDescription(getSafeValue("description", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setEnums(getSafeValue("enum", row));
-            component.addComponentOption(option);
-        }
-
-        rows = JSonSchemaHelper.parseJsonSchema("properties", json, true);
-        for (Map<String, String> row : rows) {
-            EndpointOptionModel option = new EndpointOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setKind(getSafeValue("kind", row));
-            option.setGroup(getSafeValue("group", row));
-            option.setLabel(getSafeValue("label", row));
-            option.setRequired(getSafeValue("required", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setEnums(getSafeValue("enum", row));
-            option.setPrefix(getSafeValue("prefix", row));
-            option.setMultiValue(getSafeValue("multiValue", row));
-            option.setDeprecated(getSafeValue("deprecated", row));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setDescription(getSafeValue("description", row));
-            option.setEnumValues(getSafeValue("enum", row));
-            component.addEndpointOption(option);
-        }
-
-        return component;
-    }
-
     private void findComponentNames(File dir, Set<String> componentNames) {
         File f = new File(dir, "classes/META-INF/services/org/apache/camel/component");
 
@@ -1040,12 +974,12 @@ public class EndpointDslMojo extends AbstractMojo {
         try {
             String header;
             try (InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt")) {
-                header = loadText(is);
+                header = PackageHelper.loadText(is);
             }
             String code = header + source;
             getLog().debug("Source code generated:\n" + code);
 
-            updateResource(null, target, code);
+            AbstractGeneratorMojo.updateResource(null, target, code);
         } catch (Exception e) {
             throw new MojoFailureException("IOError with file " + target, e);
         }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageArchetypeCatalogMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageArchetypeCatalogMojo.java
index fa55c4f..9cfcbb3 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageArchetypeCatalogMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageArchetypeCatalogMojo.java
@@ -24,6 +24,8 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.tooling.util.Strings;
 import org.apache.commons.io.FileUtils;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -35,9 +37,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 
-import static org.apache.camel.tooling.util.Strings.between;
-import static org.apache.camel.tooling.util.PackageHelper.findCamelDirectory;
-
 /**
  * Creates the Maven catalog for the Camel archetypes
  */
@@ -84,7 +83,7 @@ public class PackageArchetypeCatalogMojo extends AbstractMojo {
 
     public static void generateArchetypeCatalog(Log log, MavenProject project, MavenProjectHelper projectHelper, File outDir) throws MojoExecutionException, IOException {
 
-        File archetypes = findCamelDirectory(project.getBasedir(), "archetypes");
+        File archetypes = PackageHelper.findCamelDirectory(project.getBasedir(), "archetypes");
         if (archetypes == null || !archetypes.exists()) {
             throw new MojoExecutionException("Cannot find directory: archetypes");
         }
@@ -126,16 +125,16 @@ public class PackageArchetypeCatalogMojo extends AbstractMojo {
                 }
                 if (parent) {
                     // grab version from parent
-                    String version = between(line, "<version>", "</version>");
+                    String version = Strings.between(line, "<version>", "</version>");
                     if (version != null) {
                         model.setVersion(version);
                     }
                     continue;
                 }
 
-                String groupId = between(line, "<groupId>", "</groupId>");
-                String artifactId = between(line, "<artifactId>", "</artifactId>");
-                String description = between(line, "<description>", "</description>");
+                String groupId = Strings.between(line, "<groupId>", "</groupId>");
+                String artifactId = Strings.between(line, "<artifactId>", "</artifactId>");
+                String description = Strings.between(line, "<description>", "</description>");
 
                 if (groupId != null && model.getGroupId() == null) {
                     model.setGroupId(groupId);
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageComponentMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageComponentMojo.java
index 2e1c626..23d4b5c 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageComponentMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageComponentMojo.java
@@ -21,7 +21,6 @@ import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.camel.tooling.util.PackageHelper;
@@ -34,8 +33,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-
 /**
  * Analyses the Camel plugins in a project and generates extra descriptor
  * information for easier auto-discovery in Camel.
@@ -140,7 +137,7 @@ public class PackageComponentMojo extends AbstractGeneratorMojo {
             if (components.contains(name)) {
                 log.debug("Enriching component: " + name);
                 try {
-                    String text = loadText(file);
+                    String text = PackageHelper.loadText(file);
                     text = text.replace("@@@DESCRIPTION@@@", project.getDescription());
                     text = text.replace("@@@GROUPID@@@", project.getGroupId());
                     text = text.replace("@@@ARTIFACTID@@@", project.getArtifactId());
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java
index 069a0c0..e5bca21 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageDataFormatMojo.java
@@ -18,13 +18,19 @@ package org.apache.camel.maven.packaging;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.StringWriter;
 import java.nio.file.Path;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-
-import org.apache.camel.tooling.util.JSonSchemaHelper;
+import java.util.Set;
+
+import org.apache.camel.tooling.model.DataFormatModel;
+import org.apache.camel.tooling.model.DataFormatModel.DataFormatOptionModel;
+import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.tooling.model.EipModel.EipOptionModel;
+import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.logging.Log;
@@ -34,11 +40,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.tooling.util.PackageHelper.after;
-import static org.apache.camel.tooling.util.PackageHelper.findCamelCoreDirectory;
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.parseAsMap;
-
 /**
  * Analyses the Camel plugins in a project and generates extra descriptor
  * information for easier auto-discovery in Camel.
@@ -55,6 +56,12 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
     /**
      * The output directory for generated dataformats file
      */
+    @Parameter(defaultValue = "${project.build.directory}/src/main/java")
+    protected File configurerOutDir;
+
+    /**
+     * The output directory for generated dataformats file
+     */
     @Parameter(defaultValue = "${project.build.directory}/classes")
     protected File schemaOutDir;
 
@@ -68,10 +75,11 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
      */
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
-        prepareDataFormat(getLog(), project, projectHelper, dataFormatOutDir, schemaOutDir, buildContext);
+        prepareDataFormat(getLog(), project, projectHelper, dataFormatOutDir, configurerOutDir, schemaOutDir, buildContext);
     }
 
-    public static int prepareDataFormat(Log log, MavenProject project, MavenProjectHelper projectHelper, File dataFormatOutDir, File schemaOutDir, BuildContext buildContext)
+    public static int prepareDataFormat(Log log, MavenProject project, MavenProjectHelper projectHelper,
+                                        File dataFormatOutDir, File configurerOutDir, File schemaOutDir, BuildContext buildContext)
         throws MojoExecutionException {
 
         File camelMetaDir = new File(dataFormatOutDir, "META-INF/services/org/apache/camel/");
@@ -118,39 +126,43 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
         // and create json schema model file for this data format
         try {
             if (apacheCamel && count > 0) {
-                File core = findCamelCoreDirectory(project.getBasedir());
+                File core = PackageHelper.findCamelCoreDirectory(project.getBasedir());
                 if (core != null) {
                     for (Map.Entry<String, String> entry : javaTypes.entrySet()) {
                         String name = entry.getKey();
                         String javaType = entry.getValue();
                         String modelName = asModelName(name);
 
-                        String json = loadText(new File(core, "target/classes/org/apache/camel/model/dataformat/" + modelName + ".json"));
+                        String json = PackageHelper.loadText(new File(core, "target/classes/org/apache/camel/model/dataformat/"
+                                + modelName + PackageHelper.JSON_SUFIX));
 
-                        DataFormatModel dataFormatModel = extractDataFormatModel(project, json, modelName, name, javaType);
+                        DataFormatModel dataFormatModel = extractDataFormatModel(project, json, name, javaType);
                         if (log.isDebugEnabled()) {
                             log.debug("Model: " + dataFormatModel);
                         }
-
-                        // build json schema for the data format
-                        String properties = after(json, "  \"properties\": {");
-
-                        // special prepare for bindy/json properties
-                        properties = prepareBindyProperties(name, properties);
-                        properties = prepareJsonProperties(name, properties);
-
-                        String schema = createParameterJsonSchema(dataFormatModel, properties);
+                        String schema = DataFormatModel.createParameterJsonSchema(dataFormatModel);
                         if (log.isDebugEnabled()) {
                             log.debug("JSon schema:\n" + schema);
                         }
 
                         // write this to the directory
-                        Path out = schemaOutDir.toPath().resolve(schemaSubDirectory(dataFormatModel.getJavaType())).resolve(name + ".json");
+                        Path out = schemaOutDir.toPath().resolve(schemaSubDirectory(dataFormatModel.getJavaType())).resolve(name + PackageHelper.JSON_SUFIX);
                         updateResource(buildContext, out, schema);
 
                         if (log.isDebugEnabled()) {
                             log.debug("Generated " + out + " containing JSon schema for " + name + " data format");
                         }
+
+
+                        String type = dataFormatModel.getJavaType();
+                        String cn = type.substring(type.lastIndexOf('.') + 1);
+                        String pn = type.substring(0, type.length() - cn.length() - 1);
+                        Path outCfg = configurerOutDir.toPath()
+                                .resolve(pn.replace('.', '/'))
+                                .resolve(cn + "Configurer.java");
+//                        updateResource(buildContext, outCfg,
+//                                generatePropertyConfigurer(pn, cn + "Configurer", cn,
+//                                        dataFormatModel.get));
                     }
                 } else {
                     throw new MojoExecutionException("Error finding core/camel-core/target/camel-core-engine-" + project.getVersion()
@@ -174,105 +186,68 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
         return count;
     }
 
-    private static DataFormatModel extractDataFormatModel(MavenProject project, String json, String modelName, String name, String javaType) {
-        DataFormatModel dataFormatModel = new DataFormatModel();
-        dataFormatModel.setName(name);
-        dataFormatModel.setTitle("");
-        dataFormatModel.setModelName(modelName);
-        dataFormatModel.setLabel("");
-        dataFormatModel.setDescription(project.getDescription());
-        dataFormatModel.setJavaType(javaType);
-        dataFormatModel.setGroupId(project.getGroupId());
-        dataFormatModel.setArtifactId(project.getArtifactId());
-        dataFormatModel.setVersion(project.getVersion());
-
-        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("model", json, false);
-        for (Map<String, String> row : rows) {
-            if (row.containsKey("title")) {
-                String title = row.get("title");
-                dataFormatModel.setTitle(asModelTitle(name, title));
-            }
-            if (row.containsKey("label")) {
-                dataFormatModel.setLabel(row.get("label"));
-            }
-            if (row.containsKey("deprecated")) {
-                dataFormatModel.setDeprecated(row.get("deprecated"));
-            }
-            if (row.containsKey("deprecationNote")) {
-                dataFormatModel.setDeprecationNote(row.get("deprecationNote"));
-            }
-            if (row.containsKey("javaType")) {
-                dataFormatModel.setModelJavaType(row.get("javaType"));
-            }
-            if (row.containsKey("firstVersion")) {
-                dataFormatModel.setFirstVersion(row.get("firstVersion"));
+    private static DataFormatModel extractDataFormatModel(MavenProject project, String json, String name, String javaType) {
+        EipModel def = EipModel.generateEipModel(json);
+        DataFormatModel model = new DataFormatModel();
+        model.setKind("dataformat");
+        model.setName(name);
+        model.setTitle(asModelTitle(name, def.getTitle()));
+        model.setDescription(def.getDescription());
+        model.setFirstVersion(asModelFirstVersion(name, def.getFirstVersion()));
+        model.setLabel(def.getLabel());
+        model.setDeprecated(def.isDeprecated());
+        model.setDeprecationNote(def.getDeprecationNote());
+        model.setJavaType(javaType);
+        model.setModelName(def.getName());
+        model.setModelJavaType(def.getJavaType());
+        model.setGroupId(project.getGroupId());
+        model.setArtifactId(project.getArtifactId());
+        model.setVersion(project.getVersion());
+
+        for (EipOptionModel opt : def.getOptions()) {
+            DataFormatOptionModel option = new DataFormatOptionModel();
+            option.setName(opt.getName());
+            option.setKind(opt.getKind());
+            option.setDisplayName(opt.getDisplayName());
+            option.setGroup(opt.getGroup());
+            option.setLabel(opt.getLabel());
+            option.setRequired(opt.isRequired());
+            option.setType(opt.getType());
+            option.setJavaType(opt.getJavaType());
+            option.setEnums(opt.getEnums());
+            option.setOneOfs(opt.getOneOfs());
+            option.setPrefix(opt.getPrefix());
+            option.setOptionalPrefix(opt.getOptionalPrefix());
+            option.setMultiValue(opt.isMultiValue());
+            option.setDeprecated(opt.isDeprecated());
+            option.setDeprecationNote(opt.getDeprecationNote());
+            option.setSecret(opt.isSecret());
+            option.setDefaultValue(opt.getDefaultValue());
+            option.setDefaultValueNote(opt.getDefaultValueNote());
+            option.setAsPredicate(opt.isAsPredicate());
+            option.setConfigurationClass(opt.getConfigurationClass());
+            option.setConfigurationField(opt.getConfigurationField());
+            option.setDescription(opt.getDescription());
+
+            if ("type".equals(option.getName()) && "bindy".equals(model.getModelName())) {
+                switch (name) {
+                    case "bindy-csv": option.setDefaultValue("Csv"); break;
+                    case "bindy-fixed": option.setDefaultValue("Fixed"); break;
+                    case "bindy-kvp": option.setDefaultValue("KeyValue"); break;
+                }
             }
-            // favor description from the model schema
-            if (row.containsKey("description")) {
-                dataFormatModel.setDescription(row.get("description"));
+            if ("library".equals(option.getName()) && "json".equals(model.getModelName())) {
+                switch (name) {
+                    case "json-gson": option.setDefaultValue("Gson"); break;
+                    case "json-jackson": option.setDefaultValue("Jackson"); break;
+                    case "json-johnzon": option.setDefaultValue("Johnzon"); break;
+                    case "json-fastson": option.setDefaultValue("Fastjson"); break;
+                    case "json-xstream": option.setDefaultValue("XStream"); break;
+                }
             }
+            model.addOption(option);
         }
-
-        // first version special for json
-        String firstVersion = prepareJsonFirstVersion(name);
-        if (firstVersion != null) {
-            dataFormatModel.setFirstVersion(firstVersion);
-        }
-
-        return dataFormatModel;
-    }
-
-    private static String prepareBindyProperties(String name, String properties) {
-        String bindy = "\"enum\": [ \"Csv\", \"Fixed\", \"KeyValue\" ], \"deprecated\": \"false\", \"secret\": \"false\"";
-        String bindyCsv = "\"enum\": [ \"Csv\", \"Fixed\", \"KeyValue\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"Csv\"";
-        String bindyFixed = "\"enum\": [ \"Csv\", \"Fixed\", \"KeyValue\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"Fixed\"";
-        String bindyKvp = "\"enum\": [ \"Csv\", \"Fixed\", \"KeyValue\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"KeyValue\"";
-
-        if ("bindy-csv".equals(name)) {
-            properties = properties.replace(bindy, bindyCsv);
-        } else if ("bindy-fixed".equals(name)) {
-            properties = properties.replace(bindy, bindyFixed);
-        } else if ("bindy-kvp".equals(name)) {
-            properties = properties.replace(bindy, bindyKvp);
-        }
-
-        return properties;
-    }
-
-    private static String prepareJsonProperties(String name, String properties) {
-        String json = "\"enum\": [ \"Gson\", \"Jackson\", \"Johnzon\", \"XStream\", \"Fastjson\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"XStream\"";
-        String jsonGson = "\"enum\": [ \"Gson\", \"Jackson\", \"Johnzon\", \"XStream\", \"Fastjson\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"Gson\"";
-        String jsonJackson = "\"enum\": [ \"Gson\", \"Jackson\", \"Johnzon\", \"XStream\", \"Fastjson\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"Jackson\"";
-        String jsonJohnzon = "\"enum\": [ \"Gson\", \"Jackson\", \"Johnzon\", \"XStream\", \"Fastjson\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"Johnzon\"";
-        String jsonFastjson = "\"enum\": [ \"Gson\", \"Jackson\", \"Johnzon\", \"XStream\", \"Fastjson\" ], \"deprecated\": \"false\", \"secret\": \"false\", \"defaultValue\": \"Fastjson\"";
-
-        if ("json-gson".equals(name)) {
-            properties = properties.replace(json, jsonGson);
-        } else if ("json-jackson".equals(name)) {
-            properties = properties.replace(json, jsonJackson);
-        } else if ("json-johnzon".equals(name)) {
-            properties = properties.replace(json, jsonJohnzon);
-        } else if ("json-fastjson".equals(name)) {
-            properties = properties.replace(json, jsonFastjson);
-        }
-
-        return properties;
-    }
-
-    private static String prepareJsonFirstVersion(String name) {
-        if ("json-gson".equals(name)) {
-            return "2.10.0";
-        } else if ("json-jackson".equals(name)) {
-            return "2.0.0";
-        } else if ("json-johnzon".equals(name)) {
-            return "2.18.0";
-        } else if ("json-xstream".equals(name)) {
-            return "2.0.0";
-        } else if ("json-fastjson".equals(name)) {
-            return "2.20.0";
-        }
-
-        return null;
+        return model;
     }
 
     private static String readClassFromCamelResource(File file, StringBuilder buffer, BuildContext buildContext) throws MojoExecutionException {
@@ -298,8 +273,8 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
 
         // find out the javaType for each data format
         try {
-            String text = loadText(file);
-            Map<String, String> map = parseAsMap(text);
+            String text = PackageHelper.loadText(file);
+            Map<String, String> map = PackageHelper.parseAsMap(text);
             return map.get("class");
         } catch (IOException e) {
             throw new MojoExecutionException("Failed to read file " + file + ". Reason: " + e, e);
@@ -318,6 +293,18 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
         return name;
     }
 
+    private static String asModelFirstVersion(String name, String firstVersion) {
+        switch (name) {
+            case "json-gson": return "2.10.0";
+            case "json-jackson": return "2.0.0";
+            case "json-johnzon": return "2.18.0";
+            case "json-xstream": return "2.0.0";
+            case "json-fastjson": return "2.20.0";
+            default: return firstVersion;
+        }
+    }
+
+    // TODO: split json / bindy into multiple jsons descriptors
     private static String asModelTitle(String name, String title) {
         // special for some data formats
         if ("json-gson".equals(name)) {
@@ -348,164 +335,53 @@ public class PackageDataFormatMojo extends AbstractGeneratorMojo {
         return pckName.replace('.', '/');
     }
 
-    private static String createParameterJsonSchema(DataFormatModel dataFormatModel, String schema) {
-        StringBuilder buffer = new StringBuilder("{");
-        // dataformat model
-        buffer.append("\n \"dataformat\": {");
-        buffer.append("\n    \"name\": \"").append(dataFormatModel.getName()).append("\",");
-        buffer.append("\n    \"kind\": \"").append("dataformat").append("\",");
-        buffer.append("\n    \"modelName\": \"").append(dataFormatModel.getModelName()).append("\",");
-        if (dataFormatModel.getTitle() != null) {
-            buffer.append("\n    \"title\": \"").append(dataFormatModel.getTitle()).append("\",");
-        }
-        if (dataFormatModel.getDescription() != null) {
-            buffer.append("\n    \"description\": \"").append(dataFormatModel.getDescription()).append("\",");
-        }
-        boolean deprecated = "true".equals(dataFormatModel.getDeprecated());
-        buffer.append("\n    \"deprecated\": ").append(deprecated).append(",");
-        if (dataFormatModel.getFirstVersion() != null) {
-            buffer.append("\n    \"firstVersion\": \"").append(dataFormatModel.getFirstVersion()).append("\",");
-        }
-        buffer.append("\n    \"label\": \"").append(dataFormatModel.getLabel()).append("\",");
-        buffer.append("\n    \"javaType\": \"").append(dataFormatModel.getJavaType()).append("\",");
-        if (dataFormatModel.getModelJavaType() != null) {
-            buffer.append("\n    \"modelJavaType\": \"").append(dataFormatModel.getModelJavaType()).append("\",");
+    public static String generatePropertyConfigurer(
+            String pn, String cn, String en,
+            Set<DataFormatOptionModel> options) throws IOException {
+
+        try (StringWriter w = new StringWriter()) {
+            w.write("/* Generated by camel-package-maven-plugin - do not edit this file! */\n");
+            w.write("package " + pn + ";\n");
+            w.write("\n");
+            w.write("import java.util.HashMap;\n");
+            w.write("import java.util.Map;\n");
+            w.write("\n");
+            w.write("import org.apache.camel.CamelContext;\n");
+            w.write("import org.apache.camel.spi.GeneratedPropertyConfigurer;\n");
+            w.write("import org.apache.camel.support.component.PropertyConfigurerSupport;\n");
+            w.write("\n");
+            w.write("/**\n");
+            w.write(" * Source code generated by camel-package-maven-plugin - do not edit this file!\n");
+            w.write(" */\n");
+            w.write("@SuppressWarnings(\"unchecked\")\n");
+            w.write("public class " + cn + " extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer {\n");
+            w.write("\n");
+            w.write("    @Override\n");
+            w.write("    public boolean configure(CamelContext camelContext, Object target, String name, Object value, boolean ignoreCase) {\n");
+            w.write("        switch (ignoreCase ? name.toLowerCase() : name) {\n");
+            for (DataFormatOptionModel option : options) {
+                String name = option.getName();
+                String setter = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+                String type = Strings.canonicalClassName(option.getType());
+                w.write(String.format("        case \"%s\": ((%s) target).%s(property(camelContext, %s.class, value)); return true;\n",
+                        name, en, setter, type));
+                if (!name.toLowerCase().equals(name)) {
+                    w.write(String.format("        case \"%s\": ((%s) target).%s(property(camelContext, %s.class, value)); return true;\n",
+                            name.toLowerCase(), en, setter, type));
+                }
+            }
+            w.write("            default: return false;\n");
+            w.write("        }\n");
+            w.write("    }\n");
+            w.write("\n");
+            w.write("}\n");
+            w.write("\n");
+            return w.toString();
         }
-        buffer.append("\n    \"groupId\": \"").append(dataFormatModel.getGroupId()).append("\",");
-        buffer.append("\n    \"artifactId\": \"").append(dataFormatModel.getArtifactId()).append("\",");
-        buffer.append("\n    \"version\": \"").append(dataFormatModel.getVersion()).append("\"");
-        buffer.append("\n  },");
-
-        buffer.append("\n  \"properties\": {");
-        buffer.append(schema);
-        return buffer.toString();
     }
 
-    private static class DataFormatModel {
-        private String name;
-        private String title;
-        private String modelName;
-        private String description;
-        private String firstVersion;
-        private String label;
-        private String deprecated;
-        private String deprecationNote;
-        private String javaType;
-        private String modelJavaType;
-        private String groupId;
-        private String artifactId;
-        private String version;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getTitle() {
-            return title;
-        }
-
-        public void setTitle(String title) {
-            this.title = title;
-        }
-
-        public String getModelName() {
-            return modelName;
-        }
-
-        public void setModelName(String modelName) {
-            this.modelName = modelName;
-        }
-
-        public String getModelJavaType() {
-            return modelJavaType;
-        }
-
-        public void setModelJavaType(String modelJavaType) {
-            this.modelJavaType = modelJavaType;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-        public String getFirstVersion() {
-            return firstVersion;
-        }
-
-        public void setFirstVersion(String firstVersion) {
-            this.firstVersion = firstVersion;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getDeprecated() {
-            return deprecated;
-        }
-
-        public void setDeprecated(String deprecated) {
-            this.deprecated = deprecated;
-        }
-
-        public String getDeprecationNote() {
-            return deprecationNote;
-        }
-
-        public void setDeprecationNote(String deprecationNote) {
-            this.deprecationNote = deprecationNote;
-        }
-
-        public String getJavaType() {
-            return javaType;
-        }
-
-        public void setJavaType(String javaType) {
-            this.javaType = javaType;
-        }
-
-        public String getGroupId() {
-            return groupId;
-        }
-
-        public void setGroupId(String groupId) {
-            this.groupId = groupId;
-        }
-
-        public String getArtifactId() {
-            return artifactId;
-        }
-
-        public void setArtifactId(String artifactId) {
-            this.artifactId = artifactId;
-        }
-
-        public String getVersion() {
-            return version;
-        }
-
-        public void setVersion(String version) {
-            this.version = version;
-        }
-
-        @Override
-        public String toString() {
-            return "DataFormatModel[" + "name='" + name + '\'' + ", title='" + title + '\'' + ", modelName='" + modelName + '\'' + ", description='" + description + '\''
-                   + ", label='" + label + '\'' + ", deprecated='" + deprecated + '\'' + ", javaType='" + javaType + '\'' + ", modelJavaType='" + modelJavaType + '\''
-                   + ", groupId='" + groupId + '\'' + ", artifactId='" + artifactId + '\'' + ", version='" + version + '\'' + ']';
-        }
+    public static String generateMetaInfConfigurer(String name, String fqn) {
+        return "# Generated by camel annotation processor\n" + fqn + "\n";
     }
 
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java
index a408633..2343814 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageLanguageMojo.java
@@ -21,10 +21,12 @@ import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
-import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.tooling.model.EipModel.EipOptionModel;
+import org.apache.camel.tooling.model.LanguageModel;
+import org.apache.camel.tooling.model.LanguageModel.LanguageOptionModel;
 import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -35,11 +37,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.tooling.util.PackageHelper.after;
-import static org.apache.camel.tooling.util.PackageHelper.findCamelCoreDirectory;
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.parseAsMap;
-
 /**
  * Analyses the Camel plugins in a project and generates extra descriptor
  * information for easier auto-discovery in Camel.
@@ -126,68 +123,28 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo {
         // and create json schema model file for this language
         try {
             if (apacheCamel && count > 0) {
-                File core = findCamelCoreDirectory(project.getBasedir());
+                File core = PackageHelper.findCamelCoreDirectory(project.getBasedir());
                 if (core != null) {
                     for (Map.Entry<String, String> entry : javaTypes.entrySet()) {
                         String name = entry.getKey();
                         String javaType = entry.getValue();
                         String modelName = asModelName(name);
 
-                        LanguageModel languageModel = new LanguageModel();
-                        languageModel.setName(name);
-                        languageModel.setTitle("");
-                        languageModel.setModelName(modelName);
-                        languageModel.setLabel("");
-                        languageModel.setDescription("");
-                        languageModel.setJavaType(javaType);
-                        languageModel.setGroupId(project.getGroupId());
-                        languageModel.setArtifactId(project.getArtifactId());
-                        languageModel.setVersion(project.getVersion());
-
-                        String json = loadText(new File(core, "src/main/schema/" + modelName + ".json"));
-                        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("model", json, false);
-                        for (Map<String, String> row : rows) {
-                            if (row.containsKey("title")) {
-                                // title may be special for some
-                                // languages
-                                String title = asTitle(name, row.get("title"));
-                                languageModel.setTitle(title);
-                            }
-                            if (row.containsKey("description")) {
-                                // description may be special for some
-                                // languages
-                                String desc = asDescription(name, row.get("description"));
-                                languageModel.setDescription(desc);
-                            }
-                            if (row.containsKey("label")) {
-                                languageModel.setLabel(row.get("label"));
-                            }
-                            if (row.containsKey("deprecated")) {
-                                languageModel.setDeprecated(row.get("deprecated"));
-                            }
-                            if (row.containsKey("deprecationNote")) {
-                                languageModel.setDeprecationNote(row.get("deprecationNote"));
-                            }
-                            if (row.containsKey("javaType")) {
-                                languageModel.setModelJavaType(row.get("javaType"));
-                            }
-                            if (row.containsKey("firstVersion")) {
-                                languageModel.setFirstVersion(row.get("firstVersion"));
-                            }
-                        }
+                        String json = PackageHelper.loadText(new File(core, "src/main/schema/" + modelName + PackageHelper.JSON_SUFIX));
+
+                        LanguageModel languageModel = extractLanguageModel(project, json, name, javaType);
                         if (log.isDebugEnabled()) {
                             log.debug("Model: " + languageModel);
                         }
 
                         // build json schema for the data format
-                        String properties = after(json, "  \"properties\": {");
-                        String schema = createParameterJsonSchema(languageModel, properties);
+                        String schema = LanguageModel.createParameterJsonSchema(languageModel);
                         if (log.isDebugEnabled()) {
                             log.debug("JSon schema\n" + schema);
                         }
 
                         // write this to the directory
-                        Path out = schemaOutDir.toPath().resolve(schemaSubDirectory(languageModel.getJavaType())).resolve(name + ".json");
+                        Path out = schemaOutDir.toPath().resolve(schemaSubDirectory(languageModel.getJavaType())).resolve(name + PackageHelper.JSON_SUFIX);
                         updateResource(buildContext, out, schema);
 
                         if (log.isDebugEnabled()) {
@@ -216,6 +173,53 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo {
         return count;
     }
 
+    protected static LanguageModel extractLanguageModel(MavenProject project, String json, String name, String javaType) {
+        EipModel def = EipModel.generateEipModel(json);
+        LanguageModel model = new LanguageModel();
+        model.setKind("language");
+        model.setName(name);
+        model.setTitle(asTitle(name, def.getTitle()));
+        model.setDescription(asDescription(name, def.getDescription()));
+        model.setFirstVersion(def.getFirstVersion());
+        model.setLabel(def.getLabel());
+        model.setDeprecated(def.isDeprecated());
+        model.setDeprecationNote(def.getDeprecationNote());
+        model.setJavaType(javaType);
+        model.setModelName(def.getName());
+        model.setModelJavaType(def.getJavaType());
+        model.setGroupId(project.getGroupId());
+        model.setArtifactId(project.getArtifactId());
+        model.setVersion(project.getVersion());
+
+        for (EipOptionModel opt : def.getOptions()) {
+            LanguageOptionModel option = new LanguageOptionModel();
+            option.setName(opt.getName());
+            option.setKind(opt.getKind());
+            option.setDisplayName(opt.getDisplayName());
+            option.setGroup(opt.getGroup());
+            option.setLabel(opt.getLabel());
+            option.setRequired(opt.isRequired());
+            option.setType(opt.getType());
+            option.setJavaType(opt.getJavaType());
+            option.setEnums(opt.getEnums());
+            option.setOneOfs(opt.getOneOfs());
+            option.setPrefix(opt.getPrefix());
+            option.setOptionalPrefix(opt.getOptionalPrefix());
+            option.setMultiValue(opt.isMultiValue());
+            option.setDeprecated(opt.isDeprecated());
+            option.setDeprecationNote(opt.getDeprecationNote());
+            option.setSecret(opt.isSecret());
+            option.setDefaultValue(opt.getDefaultValue());
+            option.setDefaultValueNote(opt.getDefaultValueNote());
+            option.setAsPredicate(opt.isAsPredicate());
+            option.setConfigurationClass(opt.getConfigurationClass());
+            option.setConfigurationField(opt.getConfigurationField());
+            option.setDescription(opt.getDescription());
+            model.addOption(option);
+        }
+        return model;
+    }
+
     private static String readClassFromCamelResource(File file, StringBuilder buffer, BuildContext buildContext) throws MojoExecutionException {
         // skip directories as there may be a sub .resolver directory such as in
         // camel-script
@@ -240,8 +244,8 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo {
 
         // find out the javaType for each data format
         try {
-            String text = loadText(file);
-            Map<String, String> map = parseAsMap(text);
+            String text = PackageHelper.loadText(file);
+            Map<String, String> map = PackageHelper.parseAsMap(text);
             return map.get("class");
         } catch (IOException e) {
             throw new MojoExecutionException("Failed to read file " + file + ". Reason: " + e, e);
@@ -280,164 +284,4 @@ public class PackageLanguageMojo extends AbstractGeneratorMojo {
         return pckName.replace('.', '/');
     }
 
-    private static String createParameterJsonSchema(LanguageModel languageModel, String schema) {
-        StringBuilder buffer = new StringBuilder("{");
-        // language model
-        buffer.append("\n \"language\": {");
-        buffer.append("\n    \"name\": \"").append(languageModel.getName()).append("\",");
-        buffer.append("\n    \"kind\": \"").append("language").append("\",");
-        buffer.append("\n    \"modelName\": \"").append(languageModel.getModelName()).append("\",");
-        if (languageModel.getTitle() != null) {
-            buffer.append("\n    \"title\": \"").append(languageModel.getTitle()).append("\",");
-        }
-        if (languageModel.getDescription() != null) {
-            buffer.append("\n    \"description\": \"").append(languageModel.getDescription()).append("\",");
-        }
-        boolean deprecated = "true".equals(languageModel.getDeprecated());
-        buffer.append("\n    \"deprecated\": ").append(deprecated).append(",");
-        if (languageModel.getFirstVersion() != null) {
-            buffer.append("\n    \"firstVersion\": \"").append(languageModel.getFirstVersion()).append("\",");
-        }
-        buffer.append("\n    \"label\": \"").append(languageModel.getLabel()).append("\",");
-        buffer.append("\n    \"javaType\": \"").append(languageModel.getJavaType()).append("\",");
-        if (languageModel.getModelJavaType() != null) {
-            buffer.append("\n    \"modelJavaType\": \"").append(languageModel.getModelJavaType()).append("\",");
-        }
-        buffer.append("\n    \"groupId\": \"").append(languageModel.getGroupId()).append("\",");
-        buffer.append("\n    \"artifactId\": \"").append(languageModel.getArtifactId()).append("\",");
-        buffer.append("\n    \"version\": \"").append(languageModel.getVersion()).append("\"");
-        buffer.append("\n  },");
-
-        buffer.append("\n  \"properties\": {");
-        buffer.append(schema);
-        return buffer.toString();
-    }
-
-    private static class LanguageModel {
-        private String name;
-        private String title;
-        private String modelName;
-        private String description;
-        private String firstVersion;
-        private String label;
-        private String deprecated;
-        private String deprecationNote;
-        private String javaType;
-        private String modelJavaType;
-        private String groupId;
-        private String artifactId;
-        private String version;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getTitle() {
-            return title;
-        }
-
-        public void setTitle(String title) {
-            this.title = title;
-        }
-
-        public String getModelName() {
-            return modelName;
-        }
-
-        public void setModelName(String modelName) {
-            this.modelName = modelName;
-        }
-
-        public String getModelJavaType() {
-            return modelJavaType;
-        }
-
-        public void setModelJavaType(String modelJavaType) {
-            this.modelJavaType = modelJavaType;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-        public String getFirstVersion() {
-            return firstVersion;
-        }
-
-        public void setFirstVersion(String firstVersion) {
-            this.firstVersion = firstVersion;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getDeprecated() {
-            return deprecated;
-        }
-
-        public void setDeprecated(String deprecated) {
-            this.deprecated = deprecated;
-        }
-
-        public String getDeprecationNote() {
-            return deprecationNote;
-        }
-
-        public void setDeprecationNote(String deprecationNote) {
-            this.deprecationNote = deprecationNote;
-        }
-
-        public String getJavaType() {
-            return javaType;
-        }
-
-        public void setJavaType(String javaType) {
-            this.javaType = javaType;
-        }
-
-        public String getGroupId() {
-            return groupId;
-        }
-
-        public void setGroupId(String groupId) {
-            this.groupId = groupId;
-        }
-
-        public String getArtifactId() {
-            return artifactId;
-        }
-
-        public void setArtifactId(String artifactId) {
-            this.artifactId = artifactId;
-        }
-
-        public String getVersion() {
-            return version;
-        }
-
-        public void setVersion(String version) {
-            this.version = version;
-        }
-
-        @Override
-        public String toString() {
-            return "LanguageModel[" + "name='" + name + '\'' + ", modelName='" + modelName + '\'' + ", title='" + title + '\'' + ", description='" + description + '\''
-                   + ", label='" + label + '\'' + ", javaType='" + javaType + '\'' + ", modelJavaType='" + modelJavaType + '\'' + ", groupId='" + groupId + '\'' + ", artifactId='"
-                   + artifactId + '\'' + ", version='" + version + '\'' + ']';
-        }
-    }
-
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageModelMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageModelMojo.java
index 23ad95e..18e5473 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageModelMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageModelMojo.java
@@ -18,8 +18,6 @@ package org.apache.camel.maven.packaging;
 
 import java.io.File;
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
index b2ee6e7..bf1e5da 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PackageOtherMojo.java
@@ -20,6 +20,9 @@ import java.io.File;
 import java.nio.file.Path;
 import java.util.Collections;
 
+import org.apache.camel.tooling.model.OtherModel;
+import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.logging.Log;
@@ -29,8 +32,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.tooling.util.Strings.camelDashToTitle;
-
 /**
  * Analyses the Camel plugins in a project and generates extra descriptor
  * information for easier auto-discovery in Camel.
@@ -100,16 +101,12 @@ public class PackageOtherMojo extends AbstractGeneratorMojo {
             otherModel.setArtifactId(project.getArtifactId());
             otherModel.setVersion(project.getVersion());
             otherModel.setDescription(project.getDescription());
-            if (project.getName() != null && project.getName().contains("(deprecated)")) {
-                otherModel.setDeprecated("true");
-            } else {
-                otherModel.setDeprecated("false");
-            }
+            otherModel.setDeprecated(project.getName() != null && project.getName().contains("(deprecated)"));
             otherModel.setFirstVersion(project.getProperties().getProperty("firstVersion"));
             otherModel.setLabel(project.getProperties().getProperty("label"));
             String title = project.getProperties().getProperty("title");
             if (title == null) {
-                title = camelDashToTitle(name);
+                title = Strings.camelDashToTitle(name);
             }
             otherModel.setTitle(title);
 
@@ -117,10 +114,10 @@ public class PackageOtherMojo extends AbstractGeneratorMojo {
                 log.debug("Model: " + otherModel);
             }
 
-            String schema = createJsonSchema(otherModel);
+            String schema = OtherModel.createJsonSchema(otherModel);
 
             // write this to the directory
-            Path out = schemaOutDir.toPath().resolve(name + ".json");
+            Path out = schemaOutDir.toPath().resolve(name + PackageHelper.JSON_SUFIX);
             updateResource(buildContext, out, schema);
 
             if (log.isDebugEnabled()) {
@@ -139,130 +136,4 @@ public class PackageOtherMojo extends AbstractGeneratorMojo {
         log.info("Generated " + outFile + " containing 1 Camel other: " + name);
     }
 
-    private static String createJsonSchema(OtherModel otherModel) {
-        StringBuilder buffer = new StringBuilder("{");
-        // language model
-        buffer.append("\n \"other\": {");
-        buffer.append("\n    \"name\": \"").append(otherModel.getName()).append("\",");
-        buffer.append("\n    \"kind\": \"").append("other").append("\",");
-        if (otherModel.getTitle() != null) {
-            buffer.append("\n    \"title\": \"").append(otherModel.getTitle()).append("\",");
-        }
-        if (otherModel.getDescription() != null) {
-            buffer.append("\n    \"description\": \"").append(otherModel.getDescription()).append("\",");
-        }
-        buffer.append("\n    \"deprecated\": \"").append(otherModel.getDeprecated()).append("\",");
-        if (otherModel.getFirstVersion() != null) {
-            buffer.append("\n    \"firstVersion\": \"").append(otherModel.getFirstVersion()).append("\",");
-        }
-        if (otherModel.getLabel() != null) {
-            buffer.append("\n    \"label\": \"").append(otherModel.getLabel()).append("\",");
-        }
-        buffer.append("\n    \"groupId\": \"").append(otherModel.getGroupId()).append("\",");
-        buffer.append("\n    \"artifactId\": \"").append(otherModel.getArtifactId()).append("\",");
-        buffer.append("\n    \"version\": \"").append(otherModel.getVersion()).append("\"");
-        buffer.append("\n  }");
-        buffer.append("\n}");
-        return buffer.toString();
-    }
-
-    private static class OtherModel {
-        private String name;
-        private String title;
-        private String description;
-        private String deprecated;
-        private String deprecationNote;
-        private String firstVersion;
-        private String label;
-        private String groupId;
-        private String artifactId;
-        private String version;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getTitle() {
-            return title;
-        }
-
-        public void setTitle(String title) {
-            this.title = title;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-        public String getDeprecated() {
-            return deprecated;
-        }
-
-        public void setDeprecated(String deprecated) {
-            this.deprecated = deprecated;
-        }
-
-        public String getDeprecationNote() {
-            return deprecationNote;
-        }
-
-        public void setDeprecationNote(String deprecationNote) {
-            this.deprecationNote = deprecationNote;
-        }
-
-        public String getFirstVersion() {
-            return firstVersion;
-        }
-
-        public void setFirstVersion(String firstVersion) {
-            this.firstVersion = firstVersion;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getGroupId() {
-            return groupId;
-        }
-
-        public void setGroupId(String groupId) {
-            this.groupId = groupId;
-        }
-
-        public String getArtifactId() {
-            return artifactId;
-        }
-
-        public void setArtifactId(String artifactId) {
-            this.artifactId = artifactId;
-        }
-
-        public String getVersion() {
-            return version;
-        }
-
-        public void setVersion(String version) {
-            this.version = version;
-        }
-
-        @Override
-        public String toString() {
-            return "OtherModel[" + "name='" + name + '\'' + ", title='" + title + '\'' + ", description='" + description + '\'' + ", label='" + label + '\'' + ", groupId='"
-                   + groupId + '\'' + ", artifactId='" + artifactId + '\'' + ", version='" + version + '\'' + ']';
-        }
-    }
-
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogKarafMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogKarafMojo.java
index 5057f8b..b5f76ae 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogKarafMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogKarafMojo.java
@@ -36,6 +36,7 @@ import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import org.apache.camel.tooling.util.FileUtil;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -45,9 +46,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.w3c.dom.Node.ELEMENT_NODE;
-
 /**
  * Prepares the Karaf provider camel catalog to include component it supports
  */
@@ -338,7 +336,7 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean componentFile = !rootDir && file.isFile() && file.getName().equals("component.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -358,7 +356,7 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean dataFormatFile = !rootDir && file.isFile() && file.getName().equals("dataformat.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -378,7 +376,7 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean languageFile = !rootDir && file.isFile() && file.getName().equals("language.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -398,7 +396,7 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean otherFile = !rootDir && file.isFile() && file.getName().equals("other.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -420,11 +418,11 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // components there
                 return false;
             }
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a components json file
                 try {
-                    String json = loadText(pathname);
-                    return json.contains("\"kind\": \"component\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "component".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -442,11 +440,11 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // components there
                 return false;
             }
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a dataformat json file
                 try {
-                    String json = loadText(pathname);
-                    return json.contains("\"kind\": \"dataformat\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "dataformat".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -464,11 +462,11 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
                 // components there
                 return false;
             }
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a language json file
                 try {
-                    String json = loadText(pathname);
-                    return json.contains("\"kind\": \"language\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "language".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -481,11 +479,11 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
 
         @Override
         public boolean accept(File pathname) {
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a language json file
                 try {
-                    String json = loadText(pathname);
-                    return json.contains("\"kind\": \"other\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "other".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -508,9 +506,9 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
     public static Set<String> generateJsonList(Path outDir, String outFile) throws MojoFailureException {
         Path all = outDir.resolve(outFile);
         try {
-            Set<String> answer = Files.list(outDir).filter(p -> p.getFileName().toString().endsWith(".json")).map(p -> p.getFileName().toString())
+            Set<String> answer = Files.list(outDir).filter(p -> p.getFileName().toString().endsWith(PackageHelper.JSON_SUFIX)).map(p -> p.getFileName().toString())
                 // strip out .json from the name
-                .map(n -> n.substring(0, n.length() - ".json".length())).sorted().collect(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll);
+                .map(n -> n.substring(0, n.length() - PackageHelper.JSON_SUFIX.length())).sorted().collect(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll);
             String data = String.join("\n", answer) + "\n";
             FileUtil.updateFile(all, data);
             return answer;
@@ -542,7 +540,7 @@ public class PrepareCatalogKarafMojo extends AbstractMojo {
             NodeList children = dom.getElementsByTagName("features");
             for (int i = 0; i < children.getLength(); i++) {
                 Node child = children.item(i);
-                if (child.getNodeType() == ELEMENT_NODE) {
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
                     NodeList children2 = child.getChildNodes();
                     for (int j = 0; j < children2.getLength(); j++) {
                         Node child2 = children2.item(j);
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
index 295ec22..53d0e54 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
@@ -51,8 +51,6 @@ import org.apache.maven.project.MavenProjectHelper;
 import org.asciidoctor.Asciidoctor;
 import org.asciidoctor.OptionsBuilder;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-
 /**
  * Prepares the camel catalog to include component, data format, and eip
  * descriptors, and generates a report.
@@ -261,7 +259,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
 
             try {
                 // check if we have a label as we want the eip to include labels
-                String text = loadText(file);
+                String text = PackageHelper.loadText(file);
                 // just do a basic label check
                 if (text.contains("\"label\": \"\"")) {
                     missingLabels.add(file);
@@ -306,7 +304,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
             List<String> models = new ArrayList<>();
             // sort the names
             for (String name : names) {
-                if (name.endsWith(".json")) {
+                if (name.endsWith(PackageHelper.JSON_SUFIX)) {
                     // strip out .json from the name
                     String modelName = name.substring(0, name.length() - 5);
                     models.add(modelName);
@@ -435,7 +433,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
             // check if we have a component label as we want the components to
             // include labels
             try {
-                String text = loadText(file);
+                String text = PackageHelper.loadText(file);
                 String name = asComponentName(file);
                 Matcher matcher = LABEL_PATTERN.matcher(text);
                 // grab the label, and remember it in the used labels
@@ -581,7 +579,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
             // check if we have a label as we want the data format to include
             // labels
             try {
-                String text = loadText(file);
+                String text = PackageHelper.loadText(file);
                 String name = asComponentName(file);
                 Matcher matcher = LABEL_PATTERN.matcher(text);
                 // grab the label, and remember it in the used labels
@@ -683,7 +681,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
             // check if we have a label as we want the data format to include
             // labels
             try {
-                String text = loadText(file);
+                String text = PackageHelper.loadText(file);
                 String name = asComponentName(file);
                 Matcher matcher = LABEL_PATTERN.matcher(text);
                 // grab the label, and remember it in the used labels
@@ -791,7 +789,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
 
             // check if we have a label as we want the other to include labels
             try {
-                String text = loadText(file);
+                String text = PackageHelper.loadText(file);
                 String name = asComponentName(file);
                 Matcher matcher = LABEL_PATTERN.matcher(text);
                 // grab the label, and remember it in the used labels
@@ -1383,7 +1381,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
 
     private static String asComponentName(File file) {
         String name = file.getName();
-        if (name.endsWith(".json") || name.endsWith(".adoc")) {
+        if (name.endsWith(PackageHelper.JSON_SUFIX) || name.endsWith(".adoc")) {
             return name.substring(0, name.length() - 5);
         }
         return name;
@@ -1396,7 +1394,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean componentFile = !rootDir && file.isFile() && file.getName().equals("component.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -1416,7 +1414,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean dataFormatFile = !rootDir && file.isFile() && file.getName().equals("dataformat.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -1436,7 +1434,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = !rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean languageFile = !rootDir && file.isFile() && file.getName().equals("language.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -1456,7 +1454,7 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // skip files in root dirs as Camel does not store information
                 // there but others may do
                 boolean rootDir = "classes".equals(dir.getName()) || "META-INF".equals(dir.getName());
-                boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(".json");
+                boolean jsonFile = rootDir && file.isFile() && file.getName().endsWith(PackageHelper.JSON_SUFIX);
                 boolean otherFile = !rootDir && file.isFile() && file.getName().equals("other.properties");
                 if (jsonFile) {
                     found.add(file);
@@ -1495,11 +1493,11 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // components there
                 return false;
             }
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a components json file
                 try {
-                    String json = loadText(pathname);
-                    return json != null && json.contains("\"kind\": \"component\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "component".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -1517,11 +1515,11 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // components there
                 return false;
             }
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a dataformat json file
                 try {
-                    String json = loadText(pathname);
-                    return json != null && json.contains("\"kind\": \"dataformat\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "dataformat".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -1539,11 +1537,11 @@ public class PrepareCatalogMojo extends AbstractMojo {
                 // components there
                 return false;
             }
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a language json file
                 try {
-                    String json = loadText(pathname);
-                    return json != null && json.contains("\"kind\": \"language\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "language".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -1556,11 +1554,11 @@ public class PrepareCatalogMojo extends AbstractMojo {
 
         @Override
         public boolean accept(File pathname) {
-            if (pathname.isFile() && pathname.getName().endsWith(".json")) {
+            if (pathname.isFile() && pathname.getName().endsWith(PackageHelper.JSON_SUFIX)) {
                 // must be a language json file
                 try {
-                    String json = loadText(pathname);
-                    return json != null && json.contains("\"kind\": \"other\"");
+                    String json = PackageHelper.loadText(pathname);
+                    return "other".equals(PackageHelper.getSchemaKind(json));
                 } catch (IOException e) {
                     // ignore
                 }
@@ -1581,9 +1579,9 @@ public class PrepareCatalogMojo extends AbstractMojo {
         Set<String> answer;
         Path all = outDir.resolve(outFile);
         try {
-            answer = Files.list(outDir).filter(p -> p.getFileName().toString().endsWith(".json")).map(p -> p.getFileName().toString())
+            answer = Files.list(outDir).filter(p -> p.getFileName().toString().endsWith(PackageHelper.JSON_SUFIX)).map(p -> p.getFileName().toString())
                 // strip out .json from the name
-                .map(n -> n.substring(0, n.length() - ".json".length())).sorted().collect(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll);
+                .map(n -> n.substring(0, n.length() - PackageHelper.JSON_SUFIX.length())).sorted().collect(LinkedHashSet::new, LinkedHashSet::add, LinkedHashSet::addAll);
             String data = String.join("\n", answer) + "\n";
             FileUtil.updateFile(all, data);
             return answer;
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
index 5752db1..048f733 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareComponentMojo.java
@@ -28,11 +28,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.maven.packaging.PackageComponentMojo.prepareComponent;
-import static org.apache.camel.maven.packaging.PackageDataFormatMojo.prepareDataFormat;
-import static org.apache.camel.maven.packaging.PackageLanguageMojo.prepareLanguage;
-import static org.apache.camel.maven.packaging.PackageOtherMojo.prepareOthers;
-
 /**
  * Prepares a Camel component analyzing if the maven module contains Camel
  * <ul>
@@ -56,6 +51,12 @@ public class PrepareComponentMojo extends AbstractMojo {
     /**
      * The output directory for generated components file
      */
+    @Parameter(defaultValue = "${project.build.directory}/src/main/java")
+    protected File configurerOutDir;
+
+    /**
+     * The output directory for generated components file
+     */
     @Parameter(defaultValue = "${project.build.directory}/generated/camel/components")
     protected File componentOutDir;
 
@@ -113,12 +114,12 @@ public class PrepareComponentMojo extends AbstractMojo {
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
         int count = 0;
-        count += prepareComponent(getLog(), project, projectHelper, buildDir, componentOutDir, buildContext);
-        count += prepareDataFormat(getLog(), project, projectHelper, dataFormatOutDir, schemaOutDir, buildContext);
-        count += prepareLanguage(getLog(), project, projectHelper, buildDir, languageOutDir, schemaOutDir, buildContext);
+        count += PackageComponentMojo.prepareComponent(getLog(), project, projectHelper, buildDir, componentOutDir, buildContext);
+        count += PackageDataFormatMojo.prepareDataFormat(getLog(), project, projectHelper, dataFormatOutDir, configurerOutDir, schemaOutDir, buildContext);
+        count += PackageLanguageMojo.prepareLanguage(getLog(), project, projectHelper, buildDir, languageOutDir, schemaOutDir, buildContext);
         if (count == 0) {
             // okay its not any of the above then its other
-            prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext);
+            PackageOtherMojo.prepareOthers(getLog(), project, projectHelper, otherOutDir, schemaOutDir, buildContext);
         }
     }
 
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareExampleMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareExampleMojo.java
index 84ae65d..a0f338a 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareExampleMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareExampleMojo.java
@@ -30,7 +30,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
-import org.apache.camel.maven.packaging.model.ExampleModel;
+import org.apache.camel.tooling.model.ExampleModel;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.commons.io.FileUtils;
 import org.apache.maven.plugin.AbstractMojo;
@@ -43,9 +44,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.mvel2.templates.TemplateRuntime;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.writeText;
-
 /**
  * Prepares the readme.md files content up to date with all the examples that
  * Apache Camel ships.
@@ -169,7 +167,7 @@ public class PrepareExampleMojo extends AbstractMojo {
 
     private String templateExamples(List<ExampleModel> models, long deprecated) throws MojoExecutionException {
         try {
-            String template = loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("readme-examples.mvel"));
+            String template = PackageHelper.loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream("readme-examples.mvel"));
             Map<String, Object> map = new HashMap<>();
             map.put("examples", models);
             map.put("numberOfDeprecated", deprecated);
@@ -186,7 +184,7 @@ public class PrepareExampleMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "// examples: START", "// examples: END");
             if (existing != null) {
@@ -199,7 +197,7 @@ public class PrepareExampleMojo extends AbstractMojo {
                     String before = Strings.before(text, "// examples: START");
                     String after = Strings.after(text, "// examples: END");
                     text = before + "// examples: START\n" + changed + "\n// examples: END" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareParentPomMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareParentPomMojo.java
index 6bb8f2f..3ffc21f 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareParentPomMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareParentPomMojo.java
@@ -23,6 +23,7 @@ import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -33,11 +34,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 
-import static org.apache.camel.tooling.util.PackageHelper.after;
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.writeText;
-import static org.apache.camel.tooling.util.Strings.between;
-
 /**
  * Prepares the parent/pom.xml to keep the Camel artifacts up-to-date.
  */
@@ -131,10 +127,10 @@ public class PrepareParentPomMojo extends AbstractMojo {
     }
 
     private String asArtifactId(File pom) throws IOException {
-        String text = loadText(pom);
-        text = after(text, "</parent>");
+        String text = PackageHelper.loadText(pom);
+        text = Strings.after(text, "</parent>");
         if (text != null) {
-            text = between(text, "<artifactId>", "</artifactId>");
+            text = Strings.between(text, "<artifactId>", "</artifactId>");
             return text;
         }
         return null;
@@ -149,9 +145,9 @@ public class PrepareParentPomMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
-            String existing = between(text, start, end);
+            String existing = Strings.between(text, start, end);
             if (existing != null) {
                 // remove leading line breaks etc
                 existing = existing.trim();
@@ -162,7 +158,7 @@ public class PrepareParentPomMojo extends AbstractMojo {
                     String before = Strings.before(text, start);
                     String after = Strings.after(text, end);
                     text = before + start + "\n      " + changed + "\n      " + end + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareReleasePomMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareReleasePomMojo.java
index a619bdf..5b5a6cb 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareReleasePomMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareReleasePomMojo.java
@@ -23,6 +23,7 @@ import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -33,11 +34,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 
-import static org.apache.camel.tooling.util.PackageHelper.after;
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.writeText;
-import static org.apache.camel.tooling.util.Strings.between;
-
 /**
  * Prepares the apache-camel/pom.xml and common-bin to keep the Camel artifacts
  * up-to-date.
@@ -154,8 +150,8 @@ public class PrepareReleasePomMojo extends AbstractMojo {
     }
 
     private String asArtifactId(File pom) throws IOException {
-        String text = loadText(pom);
-        text = after(text, "</parent>");
+        String text = PackageHelper.loadText(pom);
+        text = Strings.after(text, "</parent>");
         if (text != null) {
             return Strings.between(text, "<artifactId>", "</artifactId>");
         }
@@ -175,9 +171,9 @@ public class PrepareReleasePomMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
-            String existing = between(text, start, end);
+            String existing = Strings.between(text, start, end);
             if (existing != null) {
                 // remove leading line breaks etc
                 existing = existing.trim();
@@ -188,7 +184,7 @@ public class PrepareReleasePomMojo extends AbstractMojo {
                     String before = Strings.before(text, start);
                     String after = Strings.after(text, end);
                     text = before + start + "\n" + spaces + changed + "\n" + spaces + end + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareUserGuideMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareUserGuideMojo.java
index 3157044..2f25e7f 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareUserGuideMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareUserGuideMojo.java
@@ -20,18 +20,16 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
-import org.apache.camel.maven.packaging.model.ComponentModel;
-import org.apache.camel.maven.packaging.model.DataFormatModel;
-import org.apache.camel.maven.packaging.model.LanguageModel;
-import org.apache.camel.maven.packaging.model.OtherModel;
-import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.model.BaseModel;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.DataFormatModel;
+import org.apache.camel.tooling.model.LanguageModel;
+import org.apache.camel.tooling.model.OtherModel;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -42,11 +40,6 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.writeText;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.getSafeValue;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.parseJsonSchema;
-
 /**
  * Prepares the user guide to keep the table of content up to date with the
  * components, data formats, and languages.
@@ -124,12 +117,12 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         try {
             List<ComponentModel> models = new ArrayList<>();
             for (File file : componentFiles) {
-                String json = loadText(file);
-                ComponentModel model = generateComponentModel(json);
+                String json =  PackageHelper.loadText(file);
+                ComponentModel model = ComponentModel.generateComponentModel(json);
 
                 // filter out alternative schemas which reuses documentation
                 boolean add = true;
-                if (!model.getAlternativeSchemes().isEmpty()) {
+                if (!Strings.isNullOrEmpty(model.getAlternativeSchemes())) {
                     String first = model.getAlternativeSchemes().split(",")[0];
                     if (!model.getScheme().equals(first)) {
                         add = false;
@@ -141,7 +134,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
             }
 
             // sor the models
-            Collections.sort(models, new ComponentComparator());
+            models.sort(ComponentModel.compareTitle());
 
             // the summary file has the TOC
             File file = new File(userGuideDir, "SUMMARY.md");
@@ -192,13 +185,13 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         try {
             List<OtherModel> models = new ArrayList<>();
             for (File file : otherFiles) {
-                String json = loadText(file);
-                OtherModel model = generateOtherModel(json);
+                String json =  PackageHelper.loadText(file);
+                OtherModel model = OtherModel.generateOtherModel(json);
                 models.add(model);
             }
 
-            // sor the models
-            Collections.sort(models, new OtherComparator());
+            // sort the models
+            models.sort(BaseModel.compareTitle());
 
             // the summary file has the TOC
             File file = new File(userGuideDir, "SUMMARY.md");
@@ -236,13 +229,13 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         try {
             List<DataFormatModel> models = new ArrayList<>();
             for (File file : dataFormatFiles) {
-                String json = loadText(file);
-                DataFormatModel model = generateDataFormatModel(json);
+                String json =  PackageHelper.loadText(file);
+                DataFormatModel model = DataFormatModel.generateDataFormatModel(json);
                 models.add(model);
             }
 
-            // sor the models
-            Collections.sort(models, new DataFormatComparator());
+            // sort the models
+            models.sort(BaseModel.compareTitle());
 
             // the summary file has the TOC
             File file = new File(userGuideDir, "SUMMARY.md");
@@ -280,13 +273,13 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         try {
             List<LanguageModel> models = new ArrayList<>();
             for (File file : languageFiles) {
-                String json = loadText(file);
-                LanguageModel model = generateLanguageModel(json);
+                String json =  PackageHelper.loadText(file);
+                LanguageModel model = LanguageModel.generateLanguageModel(json);
                 models.add(model);
             }
 
-            // sor the models
-            Collections.sort(models, new LanguageComparator());
+            // sort the models
+            models.sort(BaseModel.compareTitle());
 
             // the summary file has the TOC
             File file = new File(userGuideDir, "SUMMARY.md");
@@ -317,7 +310,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text =  PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "<!-- core components: START -->", "<!-- core components: END -->");
             if (existing != null) {
@@ -330,7 +323,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
                     String before = Strings.before(text, "<!-- core components: START -->");
                     String after = Strings.after(text, "<!-- core components: END -->");
                     text = before + "<!-- core components: START -->\n" + changed + "\n<!-- core components: END -->" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -351,7 +344,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text =  PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "<!-- components: START -->", "<!-- components: END -->");
             if (existing != null) {
@@ -364,7 +357,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
                     String before = Strings.before(text, "<!-- components: START -->");
                     String after = Strings.after(text, "<!-- components: END -->");
                     text = before + "<!-- components: START -->\n" + changed + "\n<!-- components: END -->" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -385,7 +378,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text =  PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "<!-- others: START -->", "<!-- others: END -->");
             if (existing != null) {
@@ -398,7 +391,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
                     String before = Strings.before(text, "<!-- others: START -->");
                     String after = Strings.after(text, "<!-- others: END -->");
                     text = before + "<!-- others: START -->\n" + changed + "\n<!-- others: END -->" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -419,7 +412,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text =  PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "<!-- dataformats: START -->", "<!-- dataformats: END -->");
             if (existing != null) {
@@ -432,7 +425,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
                     String before = Strings.before(text, "<!-- dataformats: START -->");
                     String after = Strings.after(text, "<!-- dataformats: END -->");
                     text = before + "<!-- dataformats: START -->\n" + changed + "\n<!-- dataformats: END -->" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -453,7 +446,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text =  PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "<!-- languages: START -->", "<!-- languages: END -->");
             if (existing != null) {
@@ -466,7 +459,7 @@ public class PrepareUserGuideMojo extends AbstractMojo {
                     String before = Strings.before(text, "<!-- languages: START -->");
                     String after = Strings.after(text, "<!-- languages: END -->");
                     text = before + "<!-- languages: START -->\n" + changed + "\n<!-- languages: END -->" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -507,123 +500,4 @@ public class PrepareUserGuideMojo extends AbstractMojo {
             return name;
         }
     }
-
-    private static class ComponentComparator implements Comparator<ComponentModel> {
-
-        @Override
-        public int compare(ComponentModel o1, ComponentModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class OtherComparator implements Comparator<OtherModel> {
-
-        @Override
-        public int compare(OtherModel o1, OtherModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class DataFormatComparator implements Comparator<DataFormatModel> {
-
-        @Override
-        public int compare(DataFormatModel o1, DataFormatModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class LanguageComparator implements Comparator<LanguageModel> {
-
-        @Override
-        public int compare(LanguageModel o1, LanguageModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private ComponentModel generateComponentModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("component", json, false);
-
-        ComponentModel component = new ComponentModel();
-        component.setScheme(getSafeValue("scheme", rows));
-        component.setSyntax(getSafeValue("syntax", rows));
-        component.setAlternativeSyntax(getSafeValue("alternativeSyntax", rows));
-        component.setAlternativeSchemes(getSafeValue("alternativeSchemes", rows));
-        component.setTitle(getSafeValue("title", rows));
-        component.setDescription(getSafeValue("description", rows));
-        component.setFirstVersion(getSafeValue("firstVersion", rows));
-        component.setLabel(getSafeValue("label", rows));
-        component.setDeprecated(getSafeValue("deprecated", rows));
-        component.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        component.setConsumerOnly(getSafeValue("consumerOnly", rows));
-        component.setProducerOnly(getSafeValue("producerOnly", rows));
-        component.setJavaType(getSafeValue("javaType", rows));
-        component.setGroupId(getSafeValue("groupId", rows));
-        component.setArtifactId(getSafeValue("artifactId", rows));
-        component.setVersion(getSafeValue("version", rows));
-
-        return component;
-    }
-
-    private OtherModel generateOtherModel(String json) {
-        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("other", json, false);
-
-        OtherModel other = new OtherModel();
-        other.setName(getSafeValue("name", rows));
-        other.setTitle(getSafeValue("title", rows));
-        other.setDescription(getSafeValue("description", rows));
-        other.setFirstVersion(getSafeValue("firstVersion", rows));
-        other.setLabel(getSafeValue("label", rows));
-        other.setDeprecated(getSafeValue("deprecated", rows));
-        other.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        other.setGroupId(getSafeValue("groupId", rows));
-        other.setArtifactId(getSafeValue("artifactId", rows));
-        other.setVersion(getSafeValue("version", rows));
-
-        return other;
-    }
-
-    private DataFormatModel generateDataFormatModel(String json) {
-        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("dataformat", json, false);
-
-        DataFormatModel dataFormat = new DataFormatModel();
-        dataFormat.setName(getSafeValue("name", rows));
-        dataFormat.setTitle(getSafeValue("title", rows));
-        dataFormat.setModelName(getSafeValue("modelName", rows));
-        dataFormat.setDescription(getSafeValue("description", rows));
-        dataFormat.setFirstVersion(getSafeValue("firstVersion", rows));
-        dataFormat.setLabel(getSafeValue("label", rows));
-        dataFormat.setDeprecated(getSafeValue("deprecated", rows));
-        dataFormat.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        dataFormat.setJavaType(getSafeValue("javaType", rows));
-        dataFormat.setGroupId(getSafeValue("groupId", rows));
-        dataFormat.setArtifactId(getSafeValue("artifactId", rows));
-        dataFormat.setVersion(getSafeValue("version", rows));
-
-        return dataFormat;
-    }
-
-    private LanguageModel generateLanguageModel(String json) {
-        List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("language", json, false);
-
-        LanguageModel language = new LanguageModel();
-        language.setTitle(getSafeValue("title", rows));
-        language.setName(getSafeValue("name", rows));
-        language.setModelName(getSafeValue("modelName", rows));
-        language.setDescription(getSafeValue("description", rows));
-        language.setFirstVersion(getSafeValue("firstVersion", rows));
-        language.setLabel(getSafeValue("label", rows));
-        language.setDeprecated(getSafeValue("deprecated", rows));
-        language.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        language.setJavaType(getSafeValue("javaType", rows));
-        language.setGroupId(getSafeValue("groupId", rows));
-        language.setArtifactId(getSafeValue("artifactId", rows));
-        language.setVersion(getSafeValue("version", rows));
-
-        return language;
-    }
-
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateDocComponentListMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateDocComponentListMojo.java
index 3d0db8c..ba6951e 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateDocComponentListMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateDocComponentListMojo.java
@@ -21,18 +21,18 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
-import org.apache.camel.maven.packaging.model.ComponentModel;
-import org.apache.camel.maven.packaging.model.DataFormatModel;
-import org.apache.camel.maven.packaging.model.EipModel;
-import org.apache.camel.maven.packaging.model.LanguageModel;
-import org.apache.camel.maven.packaging.model.OtherModel;
+import org.apache.camel.tooling.model.BaseModel;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.DataFormatModel;
+import org.apache.camel.tooling.model.LanguageModel;
+import org.apache.camel.tooling.model.OtherModel;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -44,11 +44,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.mvel2.templates.TemplateRuntime;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.writeText;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.getSafeValue;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.parseJsonSchema;
-
 /**
  * Updates the website docs with the component list to be up to date with all
  * the artifacts that Apache Camel ships.
@@ -126,12 +121,12 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         try {
             List<ComponentModel> models = new ArrayList<>();
             for (File file : componentFiles) {
-                String json = loadText(file);
-                ComponentModel model = generateComponentModel(json);
+                String json = PackageHelper.loadText(file);
+                ComponentModel model = ComponentModel.generateComponentModel(json);
 
                 // filter out alternative schemas which reuses documentation
                 boolean add = true;
-                if (!model.getAlternativeSchemes().isEmpty()) {
+                if (!Strings.isNullOrEmpty(model.getAlternativeSchemes())) {
                     String first = model.getAlternativeSchemes().split(",")[0];
                     if (!model.getScheme().equals(first)) {
                         add = false;
@@ -150,13 +145,13 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
             }
 
             // sort the models
-            models.sort(new ComponentComparator());
+            models.sort((m1, m2) -> m1.getTitle().compareToIgnoreCase(m2.getTitle()));
 
             // how many different artifacts
             long count = models.stream().map(ComponentModel::getArtifactId).distinct().count();
 
             // how many deprecated
-            long deprecated = models.stream().filter(c -> "true".equals(c.getDeprecated())).count();
+            long deprecated = models.stream().filter(BaseModel::isDeprecated).count();
 
             // update doc in the website dir
             File file = websiteDocFile;
@@ -186,26 +181,26 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         }
 
         try {
-            List<OtherModel> others = new ArrayList<>();
+            List<OtherModel> models = new ArrayList<>();
             for (File file : otherFiles) {
-                String json = loadText(file);
-                OtherModel model = generateOtherModel(json);
-                others.add(model);
+                String json = PackageHelper.loadText(file);
+                OtherModel model = OtherModel.generateOtherModel(json);
+                models.add(model);
             }
 
             // sort the models
-            others.sort(new OtherComparator());
+            models.sort((m1, m2) -> m1.getTitle().compareToIgnoreCase(m2.getTitle()));
 
             // how many different artifacts
-            long count = others.stream().map(OtherModel::getArtifactId).distinct().count();
+            long count = models.stream().map(OtherModel::getArtifactId).distinct().count();
 
             // how many deprecated
-            long deprecated = others.stream().filter(o -> "true".equals(o.getDeprecated())).count();
+            long deprecated = models.stream().filter(BaseModel::isDeprecated).count();
 
             // update doc in the website dir
             File file = websiteDocFile;
             boolean exists = file.exists();
-            String changed = templateOthers(others, count, deprecated);
+            String changed = templateOthers(models, count, deprecated);
             boolean updated = updateOthers(file, changed);
             if (updated) {
                 getLog().info("Updated website doc file: " + file);
@@ -233,8 +228,8 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         try {
             List<DataFormatModel> models = new ArrayList<>();
             for (File file : dataFormatFiles) {
-                String json = loadText(file);
-                DataFormatModel model = generateDataFormatModel(json);
+                String json = PackageHelper.loadText(file);
+                DataFormatModel model = DataFormatModel.generateDataFormatModel(json);
 
                 // special for bindy as we have one common file
                 if (model.getName().startsWith("bindy")) {
@@ -245,13 +240,13 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
             }
 
             // sort the models
-            models.sort(new DataFormatComparator());
+            models.sort((m1, m2) -> m1.getTitle().compareToIgnoreCase(m2.getTitle()));
 
             // how many different artifacts
             long count = models.stream().map(DataFormatModel::getArtifactId).distinct().count();
 
             // how many deprecated
-            long deprecated = models.stream().filter(m -> "true".equals(m.getDeprecated())).count();
+            long deprecated = models.stream().filter(BaseModel::isDeprecated).count();
 
             // update doc in the website dir
             File file = websiteDocFile;
@@ -284,19 +279,19 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         try {
             List<LanguageModel> models = new ArrayList<>();
             for (File file : languageFiles) {
-                String json = loadText(file);
-                LanguageModel model = generateLanguageModel(json);
+                String json = PackageHelper.loadText(file);
+                LanguageModel model = LanguageModel.generateLanguageModel(json);
                 models.add(model);
             }
 
             // sort the models
-            models.sort(new LanguageComparator());
+            models.sort((m1, m2) -> m1.getTitle().compareToIgnoreCase(m2.getTitle()));
 
             // how many different artifacts
             long count = models.stream().map(LanguageModel::getArtifactId).distinct().count();
 
             // how many deprecated
-            long deprecated = models.stream().filter(l -> "true".equals(l.getDeprecated())).count();
+            long deprecated = models.stream().filter(BaseModel::isDeprecated).count();
 
             // update doc in the website dir
             File file = websiteDocFile;
@@ -373,7 +368,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
     }
 
     private String loadResource(String name) throws IOException {
-        return loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream(name));
+        return PackageHelper.loadText(UpdateReadmeMojo.class.getClassLoader().getResourceAsStream(name));
     }
 
     private boolean updateComponents(File file, String changed) throws MojoExecutionException {
@@ -382,7 +377,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "// components: START", "// components: END");
             if (existing != null) {
@@ -395,7 +390,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
                     String before = Strings.before(text, "// components: START");
                     String after = Strings.after(text, "// components: END");
                     text = before + "// components: START\n" + changed + "\n// components: END" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -416,7 +411,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "// others: START", "// others: END");
             if (existing != null) {
@@ -429,7 +424,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
                     String before = Strings.before(text, "// others: START");
                     String after = Strings.after(text, "// others: END");
                     text = before + "// others: START\n" + changed + "\n// others: END" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -450,7 +445,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "// dataformats: START", "// dataformats: END");
             if (existing != null) {
@@ -463,7 +458,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
                     String before = Strings.before(text, "// dataformats: START");
                     String after = Strings.after(text, "// dataformats: END");
                     text = before + "// dataformats: START\n" + changed + "\n// dataformats: END" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -484,7 +479,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         }
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "// languages: START", "// languages: END");
             if (existing != null) {
@@ -497,7 +492,7 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
                     String before = Strings.before(text, "// languages: START");
                     String after = Strings.after(text, "// languages: END");
                     text = before + "// languages: START\n" + changed + "\n// languages: END" + after;
-                    writeText(file, text);
+                    PackageHelper.writeText(file, text);
                     return true;
                 }
             } else {
@@ -512,148 +507,4 @@ public class UpdateDocComponentListMojo extends AbstractMojo {
         }
     }
 
-    private static class EipComparator implements Comparator<EipModel> {
-
-        @Override
-        public int compare(EipModel o1, EipModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class ComponentComparator implements Comparator<ComponentModel> {
-
-        @Override
-        public int compare(ComponentModel o1, ComponentModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class OtherComparator implements Comparator<OtherModel> {
-
-        @Override
-        public int compare(OtherModel o1, OtherModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class DataFormatComparator implements Comparator<DataFormatModel> {
-
-        @Override
-        public int compare(DataFormatModel o1, DataFormatModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private static class LanguageComparator implements Comparator<LanguageModel> {
-
-        @Override
-        public int compare(LanguageModel o1, LanguageModel o2) {
-            // lets sort by title
-            return o1.getTitle().compareToIgnoreCase(o2.getTitle());
-        }
-    }
-
-    private EipModel generateEipModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("model", json, false);
-
-        EipModel eip = new EipModel();
-        eip.setName(getSafeValue("name", rows));
-        eip.setTitle(getSafeValue("title", rows));
-        eip.setDescription(getSafeValue("description", rows));
-        eip.setJavaType(getSafeValue("javaType", rows));
-        eip.setLabel(getSafeValue("label", rows));
-        eip.setDeprecated("true".equals(getSafeValue("deprecated", rows)));
-        eip.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        eip.setInput("true".equals(getSafeValue("input", rows)));
-        eip.setOutput("true".equals(getSafeValue("output", rows)));
-
-        return eip;
-    }
-
-    private ComponentModel generateComponentModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("component", json, false);
-
-        ComponentModel component = new ComponentModel();
-        component.setScheme(getSafeValue("scheme", rows));
-        component.setSyntax(getSafeValue("syntax", rows));
-        component.setAlternativeSyntax(getSafeValue("alternativeSyntax", rows));
-        component.setAlternativeSchemes(getSafeValue("alternativeSchemes", rows));
-        component.setTitle(getSafeValue("title", rows));
-        component.setDescription(getSafeValue("description", rows));
-        component.setFirstVersion(getSafeValue("firstVersion", rows));
-        component.setLabel(getSafeValue("label", rows));
-        component.setDeprecated(getSafeValue("deprecated", rows));
-        component.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        component.setConsumerOnly(getSafeValue("consumerOnly", rows));
-        component.setProducerOnly(getSafeValue("producerOnly", rows));
-        component.setJavaType(getSafeValue("javaType", rows));
-        component.setGroupId(getSafeValue("groupId", rows));
-        component.setArtifactId(getSafeValue("artifactId", rows));
-        component.setVersion(getSafeValue("version", rows));
-
-        return component;
-    }
-
-    private OtherModel generateOtherModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("other", json, false);
-
-        OtherModel other = new OtherModel();
-        other.setName(getSafeValue("name", rows));
-        other.setTitle(getSafeValue("title", rows));
-        other.setDescription(getSafeValue("description", rows));
-        other.setFirstVersion(getSafeValue("firstVersion", rows));
-        other.setLabel(getSafeValue("label", rows));
-        other.setDeprecated(getSafeValue("deprecated", rows));
-        other.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        other.setGroupId(getSafeValue("groupId", rows));
-        other.setArtifactId(getSafeValue("artifactId", rows));
-        other.setVersion(getSafeValue("version", rows));
-
-        return other;
-    }
-
-    private DataFormatModel generateDataFormatModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("dataformat", json, false);
-
-        DataFormatModel dataFormat = new DataFormatModel();
-        dataFormat.setName(getSafeValue("name", rows));
-        dataFormat.setTitle(getSafeValue("title", rows));
-        dataFormat.setModelName(getSafeValue("modelName", rows));
-        dataFormat.setDescription(getSafeValue("description", rows));
-        dataFormat.setFirstVersion(getSafeValue("firstVersion", rows));
-        dataFormat.setLabel(getSafeValue("label", rows));
-        dataFormat.setDeprecated(getSafeValue("deprecated", rows));
-        dataFormat.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        dataFormat.setJavaType(getSafeValue("javaType", rows));
-        dataFormat.setGroupId(getSafeValue("groupId", rows));
-        dataFormat.setArtifactId(getSafeValue("artifactId", rows));
-        dataFormat.setVersion(getSafeValue("version", rows));
-
-        return dataFormat;
-    }
-
-    private LanguageModel generateLanguageModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("language", json, false);
-
-        LanguageModel language = new LanguageModel();
-        language.setTitle(getSafeValue("title", rows));
-        language.setName(getSafeValue("name", rows));
-        language.setModelName(getSafeValue("modelName", rows));
-        language.setDescription(getSafeValue("description", rows));
-        language.setFirstVersion(getSafeValue("firstVersion", rows));
-        language.setLabel(getSafeValue("label", rows));
-        language.setDeprecated(getSafeValue("deprecated", rows));
-        language.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        language.setJavaType(getSafeValue("javaType", rows));
-        language.setGroupId(getSafeValue("groupId", rows));
-        language.setArtifactId(getSafeValue("artifactId", rows));
-        language.setVersion(getSafeValue("version", rows));
-
-        return language;
-    }
-
 }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateReadmeMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateReadmeMojo.java
index 77af9af..e1f3078 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateReadmeMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateReadmeMojo.java
@@ -24,20 +24,18 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
-
-import org.apache.camel.maven.packaging.model.ComponentModel;
-import org.apache.camel.maven.packaging.model.ComponentOptionModel;
-import org.apache.camel.maven.packaging.model.DataFormatModel;
-import org.apache.camel.maven.packaging.model.DataFormatOptionModel;
-import org.apache.camel.maven.packaging.model.EipModel;
-import org.apache.camel.maven.packaging.model.EipOptionModel;
-import org.apache.camel.maven.packaging.model.EndpointOptionModel;
-import org.apache.camel.maven.packaging.model.LanguageModel;
-import org.apache.camel.maven.packaging.model.LanguageOptionModel;
+import java.util.stream.Stream;
+
+import org.apache.camel.tooling.model.BaseOptionModel;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.DataFormatModel;
+import org.apache.camel.tooling.model.EipModel;
+import org.apache.camel.tooling.model.EipModel.EipOptionModel;
+import org.apache.camel.tooling.model.LanguageModel;
 import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.AbstractMojo;
@@ -49,12 +47,6 @@ import org.apache.maven.project.MavenProject;
 import org.mvel2.templates.TemplateRuntime;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.tooling.util.JSonSchemaHelper.getSafeValue;
-import static org.apache.camel.tooling.util.JSonSchemaHelper.parseJsonSchema;
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-import static org.apache.camel.tooling.util.PackageHelper.writeText;
-import static org.apache.camel.tooling.util.Strings.isEmpty;
-
 /**
  * Generate or updates the component/dataformat/language/eip readme.md and .adoc
  * files in the project root directory.
@@ -131,7 +123,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
 
                     // we only want the first scheme as the alternatives do not
                     // have their own readme file
-                    if (!isEmpty(model.getAlternativeSchemes())) {
+                    if (!Strings.isEmpty(model.getAlternativeSchemes())) {
                         String first = model.getAlternativeSchemes().split(",")[0];
                         if (!model.getScheme().equals(first)) {
                             continue;
@@ -139,7 +131,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
                     }
 
                     String docTitle = model.getTitle() + " Component";
-                    boolean deprecated = "true".equals(model.getDeprecated());
+                    boolean deprecated = model.isDeprecated();
                     if (deprecated) {
                         docTitle += " (deprecated)";
                     }
@@ -158,6 +150,14 @@ public class UpdateReadmeMojo extends AbstractMojo {
                         model.getComponentOptions().clear();
                     }
 
+                    // Fix description in options
+                    Stream.concat(model.getComponentOptions().stream(), model.getEndpointOptions().stream())
+                            .forEach(option -> {
+                                String desc = option.getDescription();
+                                desc = desc.replaceAll("\\\\n", "\n");
+                                option.setDescription(desc);
+                            });
+
                     String options = evaluateTemplate("component-options.mvel", model);
                     updated |= updateOptionsIn(file, "component", options);
 
@@ -197,12 +197,13 @@ public class UpdateReadmeMojo extends AbstractMojo {
 
                     File file = new File(docDir, dataFormatName + "-dataformat.adoc");
 
-                    DataFormatModel model = generateDataFormatModel(dataFormatName, json);
+                    DataFormatModel model = generateDataFormatModel(json);
+
                     String title = asDataFormatTitle(model.getName(), model.getTitle());
                     model.setTitle(title);
 
                     String docTitle = model.getTitle() + " DataFormat";
-                    boolean deprecated = "true".equals(model.getDeprecated());
+                    boolean deprecated = model.isDeprecated();
                     if (deprecated) {
                         docTitle += " (deprecated)";
                     }
@@ -255,10 +256,27 @@ public class UpdateReadmeMojo extends AbstractMojo {
                 if (json != null) {
                     File file = new File(docDir, languageName + "-language.adoc");
 
-                    LanguageModel model = generateLanguageModel(json);
+                    LanguageModel model = LanguageModel.generateLanguageModel(json);
+                    // skip option named id
+                    model.getOptions().removeIf(opt -> Objects.equals(opt.getName(), "id")
+                                                    || Objects.equals(opt.getName(), "expression"));
+                    // enhance description for deprecated options
+                    model.getOptions().stream()
+                            .filter(BaseOptionModel::isDeprecated)
+                            .forEach(option -> {
+                                String desc = "*Deprecated* " + option.getDescription();
+                                if (!Strings.isEmpty(option.getDeprecationNote())) {
+                                    desc = option.getDescription();
+                                    if (!desc.endsWith(".")) {
+                                        desc = desc + ".";
+                                    }
+                                    desc += " Deprecation note: " + option.getDeprecationNote();
+                                }
+                                option.setDescription(desc);
+                            });
 
                     String docTitle = model.getTitle() + " Language";
-                    boolean deprecated = "true".equals(model.getDeprecated());
+                    boolean deprecated = model.isDeprecated();
                     if (deprecated) {
                         docTitle += " (deprecated)";
                     }
@@ -309,9 +327,32 @@ public class UpdateReadmeMojo extends AbstractMojo {
             for (File jsonFile : jsonFiles) {
                 String json = loadEipJson(jsonFile);
                 if (json != null) {
-                    EipModel model = generateEipModel(json);
-                    String title = model.getTitle();
-                    model.setTitle(title);
+                    EipModel model = EipModel.generateEipModel(json);
+                    // skip option named id/description/expression/outputs
+                    model.getOptions().removeIf(option -> "id".equals(option.getName())
+                            || "description".equals(option.getName())
+                            || "expression".equals(option.getName())
+                            || "outputs".equals(option.getName()));
+                    // lets put required in the description
+                    model.getOptions().stream()
+                            .filter(EipOptionModel::isRequired)
+                            .forEach(option -> {
+                                String desc = "*Required* " + option.getDescription();
+                                option.setDescription(desc);
+                            });
+                    // is the option deprecated then include that as well in the description
+                    model.getOptions().stream()
+                            .filter(EipOptionModel::isDeprecated)
+                            .forEach(option -> {
+                                String desc = "*Deprecated* " + option.getDescription();
+                                if (!Strings.isEmpty(option.getDeprecationNote())) {
+                                    if (!desc.endsWith(".")) {
+                                        desc += ".";
+                                    }
+                                    desc = desc + " Deprecation note: " + option.getDeprecationNote();
+                                }
+                                option.setDescription(desc);
+                            });
 
                     String eipName = model.getName();
 
@@ -387,7 +428,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
 
         try {
             List<String> newLines = new ArrayList<>();
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
             String[] lines = text.split("\n");
             for (int i = 0; i < lines.length; i++) {
                 String line = lines[i];
@@ -409,7 +450,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
             if (updated) {
                 // build the new updated text
                 String newText = newLines.stream().collect(Collectors.joining("\n"));
-                writeText(file, newText);
+                PackageHelper.writeText(file, newText);
             }
         } catch (Exception e) {
             throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
@@ -428,7 +469,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
         try {
             List<String> newLines = new ArrayList<>();
 
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
             String[] lines = text.split("\n");
             // line 0 is the link
             for (int i = 1; i < lines.length; i++) {
@@ -476,7 +517,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
             if (updated) {
                 // build the new updated text
                 String newText = newLines.stream().collect(Collectors.joining("\n"));
-                writeText(file, newText);
+                PackageHelper.writeText(file, newText);
             }
         } catch (Exception e) {
             throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
@@ -496,7 +537,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
         final String headerText = generateHeaderTextData(model);
 
         try {
-            final String loadedText = loadText(file);
+            final String loadedText = PackageHelper.loadText(file);
 
             String existing = Strings.between(loadedText, markerStart, markerEnd);
 
@@ -511,7 +552,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
                 final String after = Strings.after(loadedText, markerEnd);
                 final String updatedHeaderText = before + markerStart + "\n" + headerText + "\n" + markerEnd + after;
 
-                writeText(file, updatedHeaderText);
+                PackageHelper.writeText(file, updatedHeaderText);
                 return true;
             } else {
                 // so we don't have the marker, so we add it somewhere after the
@@ -521,7 +562,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
                 final String after = Strings.after(loadedText, sinceVersion);
                 final String updatedHeaderText = before + sinceVersion + "\n\n" + markerStart + "\n" + headerText + "\n" + markerEnd + after;
 
-                writeText(file, updatedHeaderText);
+                PackageHelper.writeText(file, updatedHeaderText);
                 return true;
             }
         } catch (Exception e) {
@@ -530,8 +571,8 @@ public class UpdateReadmeMojo extends AbstractMojo {
     }
 
     private static String generateHeaderTextData(final ComponentModel model) {
-        final boolean consumerOnly = Boolean.parseBoolean(model.getConsumerOnly());
-        final boolean producerOnly = Boolean.parseBoolean(model.getProducerOnly());
+        final boolean consumerOnly = model.isConsumerOnly();
+        final boolean producerOnly = model.isProducerOnly();
         // if we have only producer support
         if (!consumerOnly && producerOnly) {
             return "*Only producer is supported*";
@@ -554,7 +595,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
         boolean updated = false;
 
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String[] lines = text.split("\n");
 
@@ -595,7 +636,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
             if (updated) {
                 // build the new updated text
                 String newText = String.join("\n", newLines);
-                writeText(file, newText);
+                PackageHelper.writeText(file, newText);
             }
         } catch (Exception e) {
             throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
@@ -621,7 +662,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
 
         final String updated = changed.trim();
         try {
-            String text = loadText(file);
+            String text = PackageHelper.loadText(file);
 
             String existing = Strings.between(text, "// " + kind + " options: START", "// " + kind + " options: END");
             if (existing != null) {
@@ -634,7 +675,7 @@ public class UpdateReadmeMojo extends AbstractMojo {
                 String before = Strings.before(text, "// " + kind + " options: START");
                 String after = Strings.after(text, "// " + kind + " options: END");
                 text = before + "// " + kind + " options: START\n" + updated + "\n// " + kind + " options: END" + after;
-                writeText(file, text);
+                PackageHelper.writeText(file, text);
                 return true;
             }
 
@@ -653,11 +694,10 @@ public class UpdateReadmeMojo extends AbstractMojo {
 
     private static String loadJsonFrom(Set<File> jsonFiles, String kind, String name) {
         for (File file : jsonFiles) {
-            if (file.getName().equals(name + ".json")) {
+            if (file.getName().equals(name + PackageHelper.JSON_SUFIX)) {
                 try {
-                    String json = loadText(file);
-                    boolean isRequestedKind = json.contains("\"kind\": \"" + kind + "\"");
-                    if (isRequestedKind) {
+                    String json = PackageHelper.loadText(file);
+                    if (Objects.equals(kind, PackageHelper.getSchemaKind(json))) {
                         return json;
                     }
                 } catch (IOException ignored) {
@@ -671,9 +711,8 @@ public class UpdateReadmeMojo extends AbstractMojo {
 
     private static String loadEipJson(File file) {
         try {
-            String json = loadText(file);
-            boolean isEip = json.contains("\"kind\": \"model\"");
-            if (isEip) {
+            String json = PackageHelper.loadText(file);
+            if ("model".equals(PackageHelper.getSchemaKind(json))) {
                 return json;
             }
         } catch (IOException ignored) {
@@ -683,316 +722,54 @@ public class UpdateReadmeMojo extends AbstractMojo {
     }
 
     private static ComponentModel generateComponentModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("component", json, false);
-
-        ComponentModel component = new ComponentModel();
-        component.setScheme(getSafeValue("scheme", rows));
-        component.setSyntax(getSafeValue("syntax", rows));
-        component.setAlternativeSyntax(getSafeValue("alternativeSyntax", rows));
-        component.setAlternativeSchemes(getSafeValue("alternativeSchemes", rows));
-        component.setTitle(getSafeValue("title", rows));
-        component.setDescription(getSafeValue("description", rows));
-        component.setFirstVersion(getSafeValue("firstVersion", rows));
-        component.setLabel(getSafeValue("label", rows));
-        component.setDeprecated(getSafeValue("deprecated", rows));
-        component.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        component.setConsumerOnly(getSafeValue("consumerOnly", rows));
-        component.setProducerOnly(getSafeValue("producerOnly", rows));
-        component.setJavaType(getSafeValue("javaType", rows));
-        component.setGroupId(getSafeValue("groupId", rows));
-        component.setArtifactId(getSafeValue("artifactId", rows));
-        component.setVersion(getSafeValue("version", rows));
-
-        String oldGroup = null;
-        rows = parseJsonSchema("componentProperties", json, true);
-        for (Map<String, String> row : rows) {
-            ComponentOptionModel option = new ComponentOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setKind(getSafeValue("kind", row));
-            option.setGroup(getSafeValue("group", row));
-            option.setRequired(getSafeValue("required", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setEnums(getSafeValue("enum", row));
-            option.setDeprecated(getSafeValue("deprecated", row));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setSecret(getSafeValue("secret", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setDescription(getSafeValue("description", row));
-            // lets put required in the description
-            if ("true".equals(option.getRequired())) {
-                String desc = "*Required* " + option.getDescription();
-                option.setDescription(desc);
-            }
-            // is the option deprecated then include that as well in the
-            // description
-            if ("true".equals(option.getDeprecated())) {
-                String desc = "*Deprecated* " + option.getDescription();
-                option.setDescription(desc);
-                if (!Strings.isEmpty(option.getDeprecationNote())) {
-                    desc = option.getDescription();
-                    if (!desc.endsWith(".")) {
-                        desc = desc + ". Deprecation note: " + option.getDeprecationNote();
-                    } else {
-                        desc = desc + " Deprecation note: " + option.getDeprecationNote();
-                    }
+        ComponentModel component = ComponentModel.generateComponentModel(json);
+        Stream.concat(component.getComponentOptions().stream(),
+                      component.getEndpointOptions().stream())
+                .filter(BaseOptionModel::isRequired)
+                .forEach(option -> {
+                    String desc = "*Required* " + option.getDescription();
                     option.setDescription(desc);
-                }
-            }
-            component.addComponentOption(option);
-
-            // group separate between different options
-            if (oldGroup == null || !oldGroup.equals(option.getGroup())) {
-                option.setNewGroup(true);
-            }
-            oldGroup = option.getGroup();
-        }
-
-        oldGroup = null;
-        rows = parseJsonSchema("properties", json, true);
-        for (Map<String, String> row : rows) {
-            EndpointOptionModel option = new EndpointOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setKind(getSafeValue("kind", row));
-            option.setGroup(getSafeValue("group", row));
-            option.setRequired(getSafeValue("required", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setEnums(getSafeValue("enum", row));
-            option.setPrefix(getSafeValue("prefix", row));
-            option.setMultiValue(getSafeValue("multiValue", row));
-            option.setDeprecated(getSafeValue("deprecated", row));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setSecret(getSafeValue("secret", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setDescription(getSafeValue("description", row));
-            // lets put required in the description
-            if ("true".equals(option.getRequired())) {
-                String desc = "*Required* " + option.getDescription();
-                option.setDescription(desc);
-            }
-            // is the option deprecated then include that as well in the
-            // description
-            if ("true".equals(option.getDeprecated())) {
-                String desc = "*Deprecated* " + option.getDescription();
-                option.setDescription(desc);
-                if (!Strings.isEmpty(option.getDeprecationNote())) {
-                    desc = option.getDescription();
-                    if (!desc.endsWith(".")) {
-                        desc = desc + ". Deprecation note: " + option.getDeprecationNote();
-                    } else {
+                });
+        Stream.concat(component.getComponentOptions().stream(),
+                      component.getEndpointOptions().stream())
+                .filter(BaseOptionModel::isDeprecated)
+                .forEach(option -> {
+                    String desc = "*Deprecated* " + option.getDescription();
+                    if (!Strings.isEmpty(option.getDeprecationNote())) {
+                        if (!desc.endsWith(".")) {
+                            desc += ".";
+                        }
                         desc = desc + " Deprecation note: " + option.getDeprecationNote();
                     }
                     option.setDescription(desc);
-                }
-            }
-            // separate the options in path vs parameter so we can generate two
-            // different tables
-            if ("path".equals(option.getKind())) {
-                component.addEndpointPathOption(option);
-            } else {
-                component.addEndpointOption(option);
-            }
-
-            // group separate between different options
-            if (oldGroup == null || !oldGroup.equals(option.getGroup())) {
-                option.setNewGroup(true);
-            }
-            oldGroup = option.getGroup();
-        }
-
+                });
         return component;
     }
 
-    private DataFormatModel generateDataFormatModel(String dataFormatName, String json) {
-        List<Map<String, String>> rows = parseJsonSchema("dataformat", json, false);
-
-        DataFormatModel dataFormat = new DataFormatModel();
-        dataFormat.setTitle(getSafeValue("title", rows));
-        dataFormat.setModelName(getSafeValue("modelName", rows));
-        dataFormat.setName(getSafeValue("name", rows));
-        dataFormat.setDescription(getSafeValue("description", rows));
-        dataFormat.setFirstVersion(getSafeValue("firstVersion", rows));
-        dataFormat.setLabel(getSafeValue("label", rows));
-        dataFormat.setDeprecated(getSafeValue("deprecated", rows));
-        dataFormat.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        dataFormat.setJavaType(getSafeValue("javaType", rows));
-        dataFormat.setGroupId(getSafeValue("groupId", rows));
-        dataFormat.setArtifactId(getSafeValue("artifactId", rows));
-        dataFormat.setVersion(getSafeValue("version", rows));
-
-        rows = parseJsonSchema("properties", json, true);
-        for (Map<String, String> row : rows) {
-            DataFormatOptionModel option = new DataFormatOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setKind(getSafeValue("kind", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setDeprecated(getSafeValue("deprecated", row));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setEnumValues(getSafeValue("enum", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setDescription(getSafeValue("description", row));
-
-            // special for bindy as we reuse one readme file
-            if (dataFormatName.startsWith("bindy") && option.getName().equals("type")) {
-                option.setDefaultValue("");
-                String doc = option.getDescription() + " The default value is either Csv or KeyValue depending on chosen dataformat.";
-                option.setDescription(doc);
-            }
-            // lets put required in the description
-            // is the option deprecated then include that as well in the
-            // description
-            if ("true".equals(option.getDeprecated())) {
-                String desc = "*Deprecated* " + option.getDescription();
-                option.setDescription(desc);
-                if (!Strings.isEmpty(option.getDeprecationNote())) {
-                    desc = option.getDescription();
-                    if (!desc.endsWith(".")) {
-                        desc = desc + ". Deprecation note: " + option.getDeprecationNote();
-                    } else {
-                        desc = desc + " Deprecation note: " + option.getDeprecationNote();
-                    }
-                    option.setDescription(desc);
-                }
-            }
-
-            // skip option named id
-            if ("id".equals(option.getName())) {
-                getLog().debug("Skipping option: " + option.getName());
-            } else {
-                dataFormat.addDataFormatOption(option);
-            }
-        }
-
-        return dataFormat;
-    }
-
-    private LanguageModel generateLanguageModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("language", json, false);
-
-        LanguageModel language = new LanguageModel();
-        language.setTitle(getSafeValue("title", rows));
-        language.setModelName(getSafeValue("modelName", rows));
-        language.setName(getSafeValue("name", rows));
-        language.setDescription(getSafeValue("description", rows));
-        language.setFirstVersion(getSafeValue("firstVersion", rows));
-        language.setLabel(getSafeValue("label", rows));
-        language.setDeprecated(getSafeValue("deprecated", rows));
-        language.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        language.setJavaType(getSafeValue("javaType", rows));
-        language.setGroupId(getSafeValue("groupId", rows));
-        language.setArtifactId(getSafeValue("artifactId", rows));
-        language.setVersion(getSafeValue("version", rows));
-
-        rows = parseJsonSchema("properties", json, true);
-        for (Map<String, String> row : rows) {
-            LanguageOptionModel option = new LanguageOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setKind(getSafeValue("kind", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setDeprecated(getSafeValue("deprecated", row));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setEnumValues(getSafeValue("enum", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setDescription(getSafeValue("description", row));
-
-            // is the option deprecated then include that as well in the
-            // description
-            if ("true".equals(option.getDeprecated())) {
-                String desc = "*Deprecated* " + option.getDescription();
-                option.setDescription(desc);
-                if (!Strings.isEmpty(option.getDeprecationNote())) {
-                    desc = option.getDescription();
-                    if (!desc.endsWith(".")) {
-                        desc = desc + ". Deprecation note: " + option.getDeprecationNote();
-                    } else {
-                        desc = desc + " Deprecation note: " + option.getDeprecationNote();
-                    }
-                    option.setDescription(desc);
-                }
-            }
-
-            // skip option named id/expression
-            if ("id".equals(option.getName()) || "expression".equals(option.getName())) {
-                getLog().debug("Skipping option: " + option.getName());
-            } else {
-                language.addLanguageOption(option);
-            }
-        }
-
-        return language;
-    }
-
-    private EipModel generateEipModel(String json) {
-        List<Map<String, String>> rows = parseJsonSchema("model", json, false);
-
-        EipModel eip = new EipModel();
-        eip.setName(getSafeValue("name", rows));
-        eip.setTitle(getSafeValue("title", rows));
-        eip.setDescription(getSafeValue("description", rows));
-        eip.setJavaType(getSafeValue("javaType", rows));
-        eip.setLabel(getSafeValue("label", rows));
-        eip.setDeprecated("true".equals(getSafeValue("deprecated", rows)));
-        eip.setDeprecationNote(getSafeValue("deprecationNote", rows));
-        eip.setInput("true".equals(getSafeValue("input", rows)));
-        eip.setOutput("true".equals(getSafeValue("output", rows)));
-
-        rows = parseJsonSchema("properties", json, true);
-        for (Map<String, String> row : rows) {
-            EipOptionModel option = new EipOptionModel();
-            option.setName(getSafeValue("name", row));
-            option.setDisplayName(getSafeValue("displayName", row));
-            option.setType(getSafeValue("type", row));
-            option.setJavaType(getSafeValue("javaType", row));
-            option.setRequired(getSafeValue("required", row));
-            option.setDeprecated("true".equals(getSafeValue("deprecated", row)));
-            option.setDeprecationNote(getSafeValue("deprecationNote", row));
-            option.setDefaultValue(getSafeValue("defaultValue", row));
-            option.setDescription(getSafeValue("description", row));
-            option.setInput("true".equals(getSafeValue("input", row)));
-            option.setOutput("true".equals(getSafeValue("output", row)));
-
-            // lets put required in the description
-            if ("true".equals(option.getRequired())) {
-                String desc = "*Required* " + option.getDescription();
-                option.setDescription(desc);
-            }
-            // is the option deprecated then include that as well in the
-            // description
-            if (option.isDeprecated()) {
-                String desc = "*Deprecated* " + option.getDescription();
-                option.setDescription(desc);
-                if (!Strings.isEmpty(option.getDeprecationNote())) {
-                    desc = option.getDescription();
-                    if (!desc.endsWith(".")) {
-                        desc = desc + ". Deprecation note: " + option.getDeprecationNote();
-                    } else {
-                        desc = desc + " Deprecation note: " + option.getDeprecationNote();
+    private static DataFormatModel generateDataFormatModel(String json) {
+        DataFormatModel model = DataFormatModel.generateDataFormatModel(json);
+        // skip option named id
+        model.getOptions().removeIf(opt -> Objects.equals(opt.getName(), "id"));
+        // enhance description for deprecated options
+        model.getOptions().stream()
+                .filter(BaseOptionModel::isDeprecated)
+                .forEach(option -> {
+                    String desc = "*Deprecated* " + option.getDescription();
+                    if (!Strings.isEmpty(option.getDeprecationNote())) {
+                        desc = option.getDescription();
+                        if (!desc.endsWith(".")) {
+                            desc = desc + ".";
+                        }
+                        desc += " Deprecation note: " + option.getDeprecationNote();
                     }
                     option.setDescription(desc);
-                }
-            }
-
-            // skip option named id/description/expression/outputs
-            if ("id".equals(option.getName()) || "description".equals(option.getName()) || "expression".equals(option.getName()) || "outputs".equals(option.getName())) {
-                getLog().debug("Skipping option: " + option.getName());
-            } else {
-                eip.addEipOptionModel(option);
-            }
-        }
-
-        return eip;
+                });
+        return model;
     }
 
     private static String evaluateTemplate(final String templateName, final Object model) throws MojoExecutionException {
         try (InputStream templateStream = UpdateReadmeMojo.class.getClassLoader().getResourceAsStream(templateName)) {
-            String template = loadText(templateStream);
+            String template = PackageHelper.loadText(templateStream);
             return (String)TemplateRuntime.eval(template, model, Collections.singletonMap("util", MvelHelper.INSTANCE));
         } catch (IOException e) {
             throw new MojoExecutionException("Error processing mvel template `" + templateName + "`", e);
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateComponentMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateComponentMojo.java
index 888ca85..11ea086 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateComponentMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateComponentMojo.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import org.apache.camel.tooling.util.PackageHelper;
+import org.apache.camel.tooling.util.Strings;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -32,10 +33,6 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
-import static org.apache.camel.tooling.util.Strings.indentCollection;
-import static org.apache.camel.tooling.util.PackageHelper.asName;
-import static org.apache.camel.maven.packaging.ValidateHelper.validate;
-
 /**
  * Validate a Camel component analyzing if the meta-data files for
  * <ul>
@@ -98,11 +95,11 @@ public class ValidateComponentMojo extends AbstractMojo {
             boolean failed = false;
 
             for (Path file : jsonFiles) {
-                final String name = asName(file);
+                final String name = PackageHelper.asName(file);
                 final ErrorDetail detail = new ErrorDetail();
 
                 getLog().debug("Validating file " + file);
-                validate(file.toFile(), detail);
+                ValidateHelper.validate(file.toFile(), detail);
 
                 if (detail.hasErrors()) {
                     failed = true;
@@ -120,10 +117,10 @@ public class ValidateComponentMojo extends AbstractMojo {
                         getLog().warn("Missing @UriPath on endpoint");
                     }
                     if (!detail.getMissingComponentDocumentation().isEmpty()) {
-                        getLog().warn("Missing component documentation for the following options:" + indentCollection("\n\t", detail.getMissingComponentDocumentation()));
+                        getLog().warn("Missing component documentation for the following options:" + Strings.indentCollection("\n\t", detail.getMissingComponentDocumentation()));
                     }
                     if (!detail.getMissingEndpointDocumentation().isEmpty()) {
-                        getLog().warn("Missing endpoint documentation for the following options:" + indentCollection("\n\t", detail.getMissingEndpointDocumentation()));
+                        getLog().warn("Missing endpoint documentation for the following options:" + Strings.indentCollection("\n\t", detail.getMissingEndpointDocumentation()));
                     }
                 }
             }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateHelper.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateHelper.java
index 7398f3c..a84bbe9 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateHelper.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/ValidateHelper.java
@@ -22,10 +22,9 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.camel.tooling.util.JSonSchemaHelper;
+import org.apache.camel.tooling.util.PackageHelper;
 import org.apache.camel.tooling.util.Strings;
 
-import static org.apache.camel.tooling.util.PackageHelper.loadText;
-
 /**
  * Validation helper for validating components, data formats and languages
  */
@@ -42,11 +41,12 @@ public final class ValidateHelper {
      */
     public static void validate(File file, ErrorDetail errorDetail) {
         try {
-            String json = loadText(file);
+            String json = PackageHelper.loadText(file);
 
-            boolean isComponent = json.contains("\"kind\": \"component\"");
-            boolean isDataFormat = json.contains("\"kind\": \"dataformat\"");
-            boolean isLanguage = json.contains("\"kind\": \"language\"");
+            String kind = PackageHelper.getSchemaKind(json);
+            boolean isComponent = "component".equals(kind);
+            boolean isDataFormat = "dataformat".equals(kind);
+            boolean isLanguage = "language".equals(kind);
 
             // only check these kind
             if (!isComponent && !isDataFormat && !isLanguage) {
@@ -114,8 +114,7 @@ public final class ValidateHelper {
                 if (doc == null || doc.isEmpty()) {
                     errorDetail.addMissingEndpointDoc(key);
                 }
-                String kind = row.get("kind");
-                if ("path".equals(kind)) {
+                if ("path".equals(row.get("kind"))) {
                     path = true;
                 }
             }
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ComponentModel.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ComponentModel.java
deleted file mode 100644
index 3a6a667..0000000
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ComponentModel.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.maven.packaging.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.camel.tooling.util.Strings;
-
-import static org.apache.camel.tooling.util.Strings.cutLastZeroDigit;
-
-public class ComponentModel {
-
-    private String kind;
-    private String scheme;
-    private String syntax;
-    private String alternativeSyntax;
-    private String alternativeSchemes;
-    private String title;
-    private String description;
-    private String firstVersion;
-    private String label;
-    private String deprecated;
-    private String deprecationNote;
-    private String consumerOnly;
-    private String producerOnly;
-    private String javaType;
-    private String groupId;
-    private String artifactId;
-    private String version;
-    private final List<ComponentOptionModel> componentOptions = new ArrayList<>();
-    private final List<EndpointOptionModel> endpointPathOptions = new ArrayList<>();
-    private final List<EndpointOptionModel> endpointOptions = new ArrayList<>();
-
-    public String getKind() {
-        return kind;
-    }
-
-    public void setKind(String kind) {
-        this.kind = kind;
-    }
-
-    public String getScheme() {
-        return scheme;
-    }
-
-    public void setScheme(String scheme) {
-        this.scheme = scheme;
-    }
-
-    public String getSyntax() {
-        return syntax;
-    }
-
-    public void setSyntax(String syntax) {
-        this.syntax = syntax;
-    }
-
-    public String getAlternativeSyntax() {
-        return alternativeSyntax;
-    }
-
-    public void setAlternativeSyntax(String alternativeSyntax) {
-        this.alternativeSyntax = alternativeSyntax;
-    }
-
-    public String getAlternativeSchemes() {
-        return alternativeSchemes;
-    }
-
-    public void setAlternativeSchemes(String alternativeSchemes) {
-        this.alternativeSchemes = alternativeSchemes;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getFirstVersion() {
-        return firstVersion;
-    }
-
-    public void setFirstVersion(String firstVersion) {
-        this.firstVersion = firstVersion;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    public boolean isDeprecated() {
-        return "true".equals(deprecated);
-    }
-
-    public String getDeprecated() {
-        return deprecated;
-    }
-
-    public void setDeprecated(String deprecated) {
-        this.deprecated = deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public void setDeprecationNote(String deprecationNote) {
-        this.deprecationNote = deprecationNote;
-    }
-
-    public String getConsumerOnly() {
-        return consumerOnly;
-    }
-
-    public void setConsumerOnly(String consumerOnly) {
-        this.consumerOnly = consumerOnly;
-    }
-
-    public String getProducerOnly() {
-        return producerOnly;
-    }
-
-    public void setProducerOnly(String producerOnly) {
-        this.producerOnly = producerOnly;
-    }
-
-    public String getJavaType() {
-        return javaType;
-    }
-
-    public void setJavaType(String javaType) {
-        this.javaType = javaType;
-    }
-
-    public String getGroupId() {
-        return groupId;
-    }
-
-    public void setGroupId(String groupId) {
-        this.groupId = groupId;
-    }
-
-    public String getArtifactId() {
-        return artifactId;
-    }
-
-    public void setArtifactId(String artifactId) {
-        this.artifactId = artifactId;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public void setVersion(String version) {
-        this.version = version;
-    }
-
-    public List<ComponentOptionModel> getComponentOptions() {
-        return componentOptions;
-    }
-
-    public void addComponentOption(ComponentOptionModel option) {
-        componentOptions.add(option);
-    }
-
-    public List<EndpointOptionModel> getEndpointOptions() {
-        return endpointOptions;
-    }
-
-    public List<EndpointOptionModel> getEndpointPathOptions() {
-        return endpointPathOptions;
-    }
-
-    public void addEndpointOption(EndpointOptionModel option) {
-        endpointOptions.add(option);
-    }
-
-    public void addEndpointPathOption(EndpointOptionModel option) {
-        endpointPathOptions.add(option);
-    }
-
-    public String getShortJavaType() {
-        return Strings.getClassShortName(javaType);
-    }
-
-    public String getFirstVersionShort() {
-        return cutLastZeroDigit(firstVersion);
-    }
-}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ComponentOptionModel.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ComponentOptionModel.java
deleted file mode 100644
index 1ea7a71..0000000
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/ComponentOptionModel.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.maven.packaging.model;
-
-import org.apache.camel.tooling.util.Strings;
-
-import static org.apache.camel.tooling.util.Strings.wrapCamelCaseWords;
-
-public class ComponentOptionModel {
-
-    private String name;
-    private String displayName;
-    private String kind;
-    private String group;
-    private String required;
-    private String type;
-    private String javaType;
-    private String deprecated;
-    private String deprecationNote;
-    private String secret;
-    private String description;
-    private String defaultValue;
-    private String enums;
-
-    // special for documentation rendering
-    private boolean newGroup;
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public void setDisplayName(String displayName) {
-        this.displayName = displayName;
-    }
-
-    public String getKind() {
-        return kind;
-    }
-
-    public void setKind(String kind) {
-        this.kind = kind;
-    }
-
-    public String getGroup() {
-        return group;
-    }
-
-    public void setGroup(String group) {
-        this.group = group;
-    }
-
-    public String getRequired() {
-        return required;
-    }
-
-    public void setRequired(String required) {
-        this.required = required;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public String getJavaType() {
-        return javaType;
-    }
-
-    public void setJavaType(String javaType) {
-        this.javaType = javaType;
-    }
-
-    public String getDeprecated() {
-        return deprecated;
-    }
-
-    public void setDeprecated(String deprecated) {
-        this.deprecated = deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public void setDeprecationNote(String deprecationNote) {
-        this.deprecationNote = deprecationNote;
-    }
-
-    public String getSecret() {
-        return secret;
-    }
-
-    public void setSecret(String secret) {
-        this.secret = secret;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getDefaultValue() {
-        return defaultValue;
-    }
-
-    public void setDefaultValue(String defaultValue) {
-        this.defaultValue = defaultValue;
-    }
-
-    public String getEnums() {
-        return enums;
-    }
-
-    public void setEnums(String enums) {
-        this.enums = enums;
-    }
-
-    public boolean isNewGroup() {
-        return newGroup;
-    }
-
-    public void setNewGroup(boolean newGroup) {
-        this.newGroup = newGroup;
-    }
-
-    public String getShortJavaType() {
-        return Strings.getClassShortName(javaType);
-    }
-
-    @Deprecated
-    public String getShortJavaType(int watermark) {
-
-        String text = Strings.getClassShortName(javaType);
-
-        // if its some kind of java object then lets wrap it as its long
-        if ("object".equals(type)) {
-            text = wrapCamelCaseWords(text, watermark, " ");
-        }
-        return text;
-    }
-
-    public String getShortGroup() {
-        if (group.endsWith(" (advanced)")) {
-            return group.substring(0, group.length() - 11);
-        }
-        return group;
-    }
-
-    public String getShortDefaultValue(int watermark) {
-        if (defaultValue.isEmpty()) {
-            return "";
-        }
-        String text = defaultValue;
-        if (text.endsWith("<T>")) {
-            text = text.substring(0, text.length() - 3);
-        } else if (text.endsWith("<T>>")) {
-            text = text.substring(0, text.length() - 4);
-        }
-
-        return text;
-    }
-
-    public String getShortName(int watermark) {
-        String text = wrapCamelCaseWords(name, watermark, " ");
-        // ensure the option name starts with lower-case
-        return Character.toLowerCase(text.charAt(0)) + text.substring(1);
-    }
-
-}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/DataFormatModel.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/DataFormatModel.java
deleted file mode 100644
index a6dbd58..0000000
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/DataFormatModel.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.maven.packaging.model;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.camel.tooling.util.Strings;
-
-import static org.apache.camel.tooling.util.Strings.cutLastZeroDigit;
-
-public class DataFormatModel {
-
-    private String kind;
-    private String name;
-    private String modelName;
-    private String title;
-    private String description;
-    private String firstVersion;
-    private String label;
-    private String deprecated;
-    private String deprecationNote;
-    private String javaType;
-    private String groupId;
-    private String artifactId;
-    private String version;
-    private final List<DataFormatOptionModel> dataFormatOptions = new ArrayList<>();
-
-    public String getKind() {
-        return kind;
-    }
-
-    public void setKind(String kind) {
-        this.kind = kind;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getModelName() {
-        return modelName;
-    }
-
-    public void setModelName(String modelName) {
-        this.modelName = modelName;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getFirstVersion() {
-        return firstVersion;
-    }
-
-    public void setFirstVersion(String firstVersion) {
-        this.firstVersion = firstVersion;
-    }
-
-    public String getLabel() {
-        return label;
-    }
-
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    public String getDeprecated() {
-        return deprecated;
-    }
-
-    public void setDeprecated(String deprecated) {
-        this.deprecated = deprecated;
-    }
-
-    public String getDeprecationNote() {
-        return deprecationNote;
-    }
-
-    public void setDeprecationNote(String deprecationNote) {
-        this.deprecationNote = deprecationNote;
-    }
-
-    public String getJavaType() {
-        return javaType;
-    }
-
-    public void setJavaType(String javaType) {
-        this.javaType = javaType;
-    }
-
-    public String getGroupId() {
-        return groupId;
-    }
-
-    public void setGroupId(String groupId) {
-        this.groupId = groupId;
-    }
-
-    public String getArtifactId() {
-        return artifactId;
-    }
-
-    public void setArtifactId(String artifactId) {
-        this.artifactId = artifactId;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public void setVersion(String version) {
-        this.version = version;
-    }
-
-    public List<DataFormatOptionModel> getDataFormatOptions() {
-        return dataFormatOptions;
-    }
-
-    public void addDataFormatOption(DataFormatOptionModel option) {
-        dataFormatOptions.add(option);
... 1451 lines suppressed ...