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

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

commit 341e04e8381fa61ed1211d8d965a514b96a2bafb
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Fri Aug 16 07:33:04 2024 +0200

    CAMEL-20569: camel-catalog - Markup languages which functions they support 
for better doc and tooling.
---
 .../org/apache/camel/language/simple/simple.json   | 22 ++++++++
 .../camel/language/simple/SimpleConstants.java     | 45 +++++++++------
 .../packaging/AbstractGenerateConfigurerMojo.java  | 10 ++++
 .../camel/maven/packaging/PackageLanguageMojo.java | 66 +++++++++++++++++++++-
 .../camel/maven/packaging/generics/ClassUtil.java  |  4 +-
 5 files changed, 127 insertions(+), 20 deletions(-)

diff --git 
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
 
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
index 223741d6576..09b0ba79cb7 100644
--- 
a/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
+++ 
b/core/camel-core-languages/src/generated/resources/META-INF/org/apache/camel/language/simple/simple.json
@@ -20,5 +20,27 @@
     "expression": { "index": 1, "kind": "value", "displayName": "Expression", 
"group": "common", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "autowired": false, "secret": false, 
"description": "The expression value in your chosen language syntax" },
     "resultType": { "index": 2, "kind": "attribute", "displayName": "Result 
Type", "group": "common", "required": false, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "autowired": false, "secret": false, 
"description": "Sets the class of the result type (type from output)" },
     "trim": { "index": 3, "kind": "attribute", "displayName": "Trim", "group": 
"advanced", "label": "advanced", "required": false, "type": "boolean", 
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": true, "description": "Whether to trim the 
value to remove leading and trailing whitespaces and line breaks" }
+  },
+  "functions": {
+    "body": { "index": 0, "kind": "function", "displayName": "Body", "group": 
"function", "label": "function", "required": false, "javaType": "Object", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The message body" },
+    "prettyBody": { "index": 1, "kind": "function", "displayName": "Pretty 
Body", "group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Converts the body to a String, and attempts to 
pretty print if JSon or XML; otherwise the body is returned as the String 
value." },
+    "bodyOneLine": { "index": 2, "kind": "function", "displayName": "Body One 
Line", "group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Converts the body to a String and removes all 
line-breaks, so the string is in one line." },
+    "originalBody": { "index": 3, "kind": "function", "displayName": "Original 
Body", "group": "function", "label": "function", "required": false, "javaType": 
"Object", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The original incoming body (only available if 
allowUseOriginalMessage=true)." },
+    "id": { "index": 4, "kind": "function", "displayName": "Id", "group": 
"function", "label": "function", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The message id" },
+    "messageTimestamp": { "index": 5, "kind": "function", "displayName": 
"Message Timestamp", "group": "function", "label": "function", "required": 
false, "javaType": "long", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The message timestamp 
(millis since epoc) that this message originates from. Some systems like JMS, 
Kafka, AWS have a timestamp on the event\/message that Camel received. This 
method returns the timestamp if a timestamp  [...]
+    "exchangeId": { "index": 6, "kind": "function", "displayName": "Exchange 
Id", "group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The exchange id" },
+    "exchange": { "index": 7, "kind": "function", "displayName": "Exchange", 
"group": "function", "label": "function", "required": false, "javaType": 
"org.apache.camel.Exchange", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The exchange" },
+    "exception": { "index": 8, "kind": "function", "displayName": "Exception", 
"group": "function", "label": "function", "required": false, "javaType": 
"java.lang.Exception", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The exception object on the exchange 
(also from caught exceptions), is null if no exception present." },
+    "exception.message": { "index": 9, "kind": "function", "displayName": 
"Exception.message", "group": "function", "label": "function", "required": 
false, "javaType": "String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The exception message 
(also from caught exceptions), is null if no exception present." },
+    "exception.stackTrace": { "index": 10, "kind": "function", "displayName": 
"Exception.stack Trace", "group": "function", "label": "function", "required": 
false, "javaType": "String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "The exception stacktrace 
(also from caught exceptions), is null if no exception present." },
+    "threadId": { "index": 11, "kind": "function", "displayName": "Thread Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"long", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current thread. Can be 
used for logging." },
+    "threadName": { "index": 12, "kind": "function", "displayName": "Thread 
Name", "group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the name of the current thread. Can be 
used for logging." },
+    "hostName": { "index": 13, "kind": "function", "displayName": "Host Name", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the local hostname (may be empty if 
not possible to resolve)." },
+    "camelId": { "index": 14, "kind": "function", "displayName": "Camel Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The name of the CamelContext" },
+    "routeId": { "index": 15, "kind": "function", "displayName": "Route Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The route id of the current route the Exchange 
is being routed" },
+    "fromRouteId": { "index": 16, "kind": "function", "displayName": "From 
Route Id", "group": "function", "label": "function", "required": false, 
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The original route id where this 
exchange was created." },
+    "routeGroup": { "index": 17, "kind": "function", "displayName": "Route 
Group", "group": "function", "label": "function", "required": false, 
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Returns the route group of the current 
route the Exchange is being routed. Not all routes have a group assigned, so 
this may be null." },
+    "stepId": { "index": 18, "kind": "function", "displayName": "Step Id", 
"group": "function", "label": "function", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Returns the id of the current step the 
Exchange is being routed." },
+    "null": { "index": 19, "kind": "function", "displayName": "Null", "group": 
"function", "label": "function", "required": false, "javaType": "Object", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Represents a null value" }
   }
 }
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
index 64c221ca50f..a4f2ce69140 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleConstants.java
@@ -20,13 +20,16 @@ import org.apache.camel.spi.Metadata;
 
 public final class SimpleConstants {
 
-    @Metadata(description = "The message body", label = "function")
+    @Metadata(description = "The message body", javaType = "Object", label = 
"function")
     public static final String BODY = "body";
-    @Metadata(description = "Converts the body to a String, and attempts to 
pretty print if JSon or XML; otherwise the body is returned as the String 
value.", javaType = "String", label = "function")
+    @Metadata(description = "Converts the body to a String, and attempts to 
pretty print if JSon or XML; otherwise the body is returned as the String 
value.",
+              javaType = "String", label = "function")
     public static final String PRETTY_BODY = "prettyBody";
-    @Metadata(description = "Converts the body to a String and removes all 
line-breaks, so the string is in one line.", javaType = "String", label = 
"function")
+    @Metadata(description = "Converts the body to a String and removes all 
line-breaks, so the string is in one line.",
+              javaType = "String", label = "function")
     public static final String BODY_ONE_LINE = "bodyOneLine";
-    @Metadata(description = "The original incoming body (only available if 
allowUseOriginalMessage=true).", label = "function")
+    @Metadata(description = "The original incoming body (only available if 
allowUseOriginalMessage=true).", javaType = "Object",
+              label = "function")
     public static final String ORIGINAL_BODY = "originalBody";
     @Metadata(description = "The message id", javaType = "String", label = 
"function")
     public static final String ID = "id";
@@ -35,35 +38,45 @@ public final class SimpleConstants {
                             + " The message timestamp and exchange created are 
different. An exchange always has a created timestamp which is the"
                             + " local timestamp when Camel created the 
exchange. The message timestamp is only available in some Camel components"
                             + " when the consumer is able to extract the 
timestamp from the source event."
-                            + " If the message has no timestamp, then 0 is 
returned.", javaType = "long", label = "function")
+                            + " If the message has no timestamp, then 0 is 
returned.",
+              javaType = "long", label = "function")
     public static final String MESSAGE_TIMESTAMP = "messageTimestamp";
     @Metadata(description = "The exchange id", javaType = "String", label = 
"function")
     public static final String EXCHANGE_ID = "exchangeId";
-    @Metadata(description = "The exchange", javaType = "Exchange", label = 
"function")
+    @Metadata(description = "The exchange", javaType = 
"org.apache.camel.Exchange", label = "function")
     public static final String EXCHANGE = "exchange";
-    @Metadata(description = "The exception object on the exchange (also from 
caught exceptions), is null if no exception present.", javaType = "Exception", 
label = "function")
+    @Metadata(description = "The exception object on the exchange (also from 
caught exceptions), is null if no exception present.",
+              javaType = "java.lang.Exception", label = "function")
     public static final String EXCEPTION = "exception";
-    @Metadata(description = "The exception message (also from caught 
exceptions), is null if no exception present.", javaType = "String", label = 
"function")
+    @Metadata(description = "The exception message (also from caught 
exceptions), is null if no exception present.",
+              javaType = "String", label = "function")
     public static final String EXCEPTION_MESSAGE = "exception.message";
-    @Metadata(description = "The exception stacktrace (also from caught 
exceptions), is null if no exception present.", javaType = "String", label = 
"function")
+    @Metadata(description = "The exception stacktrace (also from caught 
exceptions), is null if no exception present.",
+              javaType = "String", label = "function")
     public static final String EXCEPTION_STACKTRACE = "exception.stackTrace";
-    @Metadata(description = "Returns the id of the current thread. Can be used 
for logging.", javaType = "long", label = "function")
+    @Metadata(description = "Returns the id of the current thread. Can be used 
for logging.", javaType = "long",
+              label = "function")
     public static final String THREAD_ID = "threadId";
-    @Metadata(description = "Returns the name of the current thread. Can be 
used for logging.", javaType = "String", label = "function")
+    @Metadata(description = "Returns the name of the current thread. Can be 
used for logging.", javaType = "String",
+              label = "function")
     public static final String THREAD_NAME = "threadName";
-    @Metadata(description = "Returns the local hostname (may be empty if not 
possible to resolve).", javaType = "String", label = "function")
+    @Metadata(description = "Returns the local hostname (may be empty if not 
possible to resolve).", javaType = "String",
+              label = "function")
     public static final String HOST_NAME = "hostName";
     @Metadata(description = "The name of the CamelContext", javaType = 
"String", label = "function")
     public static final String CAMEL_ID = "camelId";
-    @Metadata(description = "The route id of the current route the Exchange is 
being routed", javaType = "String", label = "function")
+    @Metadata(description = "The route id of the current route the Exchange is 
being routed", javaType = "String",
+              label = "function")
     public static final String ROUTE_ID = "routeId";
     @Metadata(description = "The original route id where this exchange was 
created.", javaType = "String", label = "function")
     public static final String FROM_ROUTE_ID = "fromRouteId";
-    @Metadata(description = "Returns the route group of the current route the 
Exchange is being routed. Not all routes have a group assigned, so this may be 
null.", javaType = "String", label = "function")
+    @Metadata(description = "Returns the route group of the current route the 
Exchange is being routed. Not all routes have a group assigned, so this may be 
null.",
+              javaType = "String", label = "function")
     public static final String ROUTE_GROUP = "routeGroup";
-    @Metadata(description = "Returns the id of the current step the Exchange 
is being routed.", javaType = "String", label = "function")
+    @Metadata(description = "Returns the id of the current step the Exchange 
is being routed.", javaType = "String",
+              label = "function")
     public static final String STEP_ID = "stepId";
-    @Metadata(description = "Represents a null value", label = "function")
+    @Metadata(description = "Represents a null value", label = "function", 
javaType = "Object")
     public static final String NULL = "null";
 
 }
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java
index 504f774d1b2..f76a3e6fc3c 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/AbstractGenerateConfigurerMojo.java
@@ -518,4 +518,14 @@ public abstract class AbstractGenerateConfigurerMojo 
extends AbstractGeneratorMo
         return false;
     }
 
+    private static String asString(AnnotationInstance ai, String name) {
+        if (ai != null) {
+            AnnotationValue av = ai.value(name);
+            if (av != null) {
+                return av.asString();
+            }
+        }
+        return null;
+    }
+
 }
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 1fec1f4b1e5..c9c561e5929 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
@@ -18,6 +18,7 @@ package org.apache.camel.maven.packaging;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.nio.file.Path;
 import java.util.Collections;
 import java.util.HashMap;
@@ -26,7 +27,9 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.camel.maven.packaging.generics.ClassUtil;
+import org.apache.camel.maven.packaging.generics.PackagePluginUtils;
 import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.Language;
 import org.apache.camel.tooling.model.EipModel;
 import org.apache.camel.tooling.model.EipModel.EipOptionModel;
 import org.apache.camel.tooling.model.JsonMapper;
@@ -34,6 +37,7 @@ import org.apache.camel.tooling.model.LanguageModel;
 import org.apache.camel.tooling.model.LanguageModel.LanguageOptionModel;
 import org.apache.camel.tooling.model.SupportLevel;
 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;
@@ -42,6 +46,10 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.codehaus.plexus.build.BuildContext;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.IndexView;
+
+import static java.lang.reflect.Modifier.isStatic;
 
 /**
  * Analyses the Camel plugins in a project and generates extra descriptor 
information for easier auto-discovery in
@@ -50,6 +58,9 @@ import org.codehaus.plexus.build.BuildContext;
 @Mojo(name = "generate-languages-list", threadSafe = true)
 public class PackageLanguageMojo extends AbstractGeneratorMojo {
 
+    private static final DotName LANGUAGE = 
DotName.createSimple(Language.class.getName());
+    private IndexView indexView;
+
     /**
      * The output directory for generated languages file
      */
@@ -203,7 +214,7 @@ public class PackageLanguageMojo extends 
AbstractGeneratorMojo {
         return count;
     }
 
-    protected static LanguageModel extractLanguageModel(MavenProject project, 
String json, String name, Class<?> javaType) {
+    protected LanguageModel extractLanguageModel(MavenProject project, String 
json, String name, Class<?> javaType) {
         EipModel def = JsonMapper.generateEipModel(json);
         LanguageModel model = new LanguageModel();
         model.setName(name);
@@ -259,11 +270,55 @@ public class PackageLanguageMojo extends 
AbstractGeneratorMojo {
             model.addOption(option);
         }
 
-        // TODO: read class and find functionClass and add each field as a 
function in the model
+        // read class and find functionClass and add each field as a function 
in the model
+        Class<?> classElement = loadClass(javaType.getName());
+        final Language lan = classElement.getAnnotation(Language.class);
+        if (lan.functionsClass() != void.class) {
+            classElement = loadClass(lan.functionsClass().getName());
+            if (classElement != null) {
+                for (Field field : classElement.getFields()) {
+                    final boolean isEnum = classElement.isEnum();
+                    if ((isEnum || isStatic(field.getModifiers()) && 
field.getType() == String.class)
+                            && field.isAnnotationPresent(Metadata.class)) {
+                        try {
+                            addFunction(model, field);
+                        } catch (Exception e) {
+                            getLog().warn(e);
+                        }
+                    }
+                }
+            }
+        }
 
         return model;
     }
 
+    private void addFunction(LanguageModel model, Field field) throws 
Exception {
+        final Class<?> declaringClass = field.getDeclaringClass();
+        final Metadata metadata = field.getAnnotation(Metadata.class);
+        LanguageModel.LanguageFunctionModel fun = new 
LanguageModel.LanguageFunctionModel();
+        fun.setConstantName(String.format("%s#%s", declaringClass.getName(), 
field.getName()));
+        fun.setName((String) field.get(null));
+        fun.setDescription(metadata.description().trim());
+        fun.setKind("function");
+        String displayName = metadata.displayName();
+        // compute a display name if we don't have anything
+        if (Strings.isNullOrEmpty(displayName)) {
+            displayName = Strings.asTitle(fun.getName());
+        }
+        fun.setDisplayName(displayName);
+        fun.setJavaType(metadata.javaType());
+        fun.setRequired(metadata.required());
+        fun.setDefaultValue(metadata.defaultValue());
+        fun.setDeprecated(field.isAnnotationPresent(Deprecated.class));
+        fun.setDeprecationNote(metadata.deprecationNote());
+        fun.setSecret(metadata.secret());
+        String group = EndpointHelper.labelAsGroupName(metadata.label(), 
false, false);
+        fun.setGroup(group);
+        fun.setLabel(metadata.label());
+        model.addFunction(fun);
+    }
+
     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
@@ -329,4 +384,11 @@ public class PackageLanguageMojo extends 
AbstractGeneratorMojo {
         return "META-INF/" + pckName.replace('.', '/');
     }
 
+    private IndexView getIndex() {
+        if (indexView == null) {
+            indexView = PackagePluginUtils.readJandexIndexQuietly(project);
+        }
+        return indexView;
+    }
+
 }
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
index f15d3c1c401..6616cbcbbdd 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
@@ -81,8 +81,7 @@ public final class ClassUtil {
      * type otherwise it return the casted {@link Class} of the type argument.
      * </p>
      *
-     * @param  type class or parametrized type
-     * @return
+     * @param type class or parametrized type
      */
     public static Class<?> getClass(Type type) {
         return getClazz(type);
@@ -209,4 +208,5 @@ public final class ClassUtil {
                 .map(annotation -> annotation.annotationType().getName())
                 .anyMatch(fqAnnotationName::equals);
     }
+
 }

Reply via email to