You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/12/21 19:24:48 UTC

(camel) 01/01: CAMEL-20275: components - Mark options that can are used for text inputs such as a SQL query

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

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

commit b6e8bf4abba1ebdf9b198e5027ba6346858c07de
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Dec 21 20:24:33 2023 +0100

    CAMEL-20275: components - Mark options that can are used for text inputs such as a SQL query
---
 .../resources/org/apache/camel/component/sql/sql.json  | 18 +++++++++---------
 .../apache/camel/component/sql/stored/sql-stored.json  |  8 ++++----
 .../org/apache/camel/component/sql/SqlEndpoint.java    |  2 +-
 .../generated/java/org/apache/camel/spi/Metadata.java  | 15 +++++++++++++++
 .../apache/camel/tooling/model/BaseOptionModel.java    | 18 ++++++++++++++++++
 .../org/apache/camel/tooling/model/JsonMapper.java     | 10 ++++++++++
 .../maven/packaging/EndpointSchemaGeneratorMojo.java   |  8 ++++++++
 .../src/main/java/org/apache/camel/spi/Metadata.java   | 15 +++++++++++++++
 8 files changed, 80 insertions(+), 14 deletions(-)

diff --git a/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/sql.json b/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/sql.json
index e2a1a32d991..7d7d698ef1a 100644
--- a/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/sql.json
+++ b/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/sql.json
@@ -22,14 +22,14 @@
     "lenientProperties": false
   },
   "componentProperties": {
-    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "common", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": true, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
-    "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the like [...]
-    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
-    "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
-    "rowMapperFactory": { "index": 4, "kind": "property", "displayName": "Row Mapper Factory", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.sql.RowMapperFactory", "deprecated": false, "autowired": true, "secret": false, "description": "Factory for creating RowMapper" },
-    "usePlaceholder": { "index": 5, "kind": "property", "displayName": "Use Placeholder", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to use placeholder and replace all placeholder characters with sign in the SQL queries. This option is default true" },
-    "healthCheckConsumerEnabled": { "index": 6, "kind": "property", "displayName": "Health Check Consumer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all consumer based health checks from this component" },
-    "healthCheckProducerEnabled": { "index": 7, "kind": "property", "displayName": "Health Check Producer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all producer based health checks from this component. Notice: Camel has by default disabled all producer based health-checks. You can turn on producer [...]
+    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "common", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": true, "secret": false, "inputLanguage": "", "description": "Sets the DataSource to use to communicate with the database." },
+    "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "inputLanguage": "", "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming  [...]
+    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "inputLanguage": "", "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+    "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "inputLanguage": "", "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single i [...]
+    "rowMapperFactory": { "index": 4, "kind": "property", "displayName": "Row Mapper Factory", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.sql.RowMapperFactory", "deprecated": false, "autowired": true, "secret": false, "inputLanguage": "", "description": "Factory for creating RowMapper" },
+    "usePlaceholder": { "index": 5, "kind": "property", "displayName": "Use Placeholder", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "inputLanguage": "", "description": "Sets whether to use placeholder and replace all placeholder characters with sign in the SQL queries. This option is default true" },
+    "healthCheckConsumerEnabled": { "index": 6, "kind": "property", "displayName": "Health Check Consumer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "inputLanguage": "", "description": "Used for enabling or disabling all consumer based health checks from this component" },
+    "healthCheckProducerEnabled": { "index": 7, "kind": "property", "displayName": "Health Check Producer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "inputLanguage": "", "description": "Used for enabling or disabling all producer based health checks from this component. Notice: Camel has by default disabled all producer based health-checks. You [...]
   },
   "headers": {
     "CamelSqlQuery": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Query to execute. This query takes precedence over the query specified in the endpoint URI. Note that query parameters in the header _are_ represented by a instead of a pass:# symbol", "constantName": "org.apache.camel.component.sql.SqlCons [...]
@@ -42,7 +42,7 @@
     "CamelSqlParameters": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Iterator", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The SQL parameters when using the option useMessageBodyForSql", "constantName": "org.apache.camel.component.sql.SqlConstants#SQL_PARAMETERS" }
   },
   "properties": {
-    "query": { "index": 0, "kind": "path", "displayName": "Query", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "description": "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file." },
+    "query": { "index": 0, "kind": "path", "displayName": "Query", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "largeInput": true, "inputLanguage": "sql", "description": "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file." },
     "allowNamedParameters": { "index": 1, "kind": "parameter", "displayName": "Allow Named Parameters", "group": "common", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow using named parameters in the queries." },
     "dataSource": { "index": 2, "kind": "parameter", "displayName": "Data Source", "group": "common", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "description": "Sets the DataSource to use to communicate with the database at endpoint level." },
     "outputClass": { "index": 3, "kind": "parameter", "displayName": "Output Class", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Specify the full package and class name to use as conversion when outputType=SelectOne." },
diff --git a/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json b/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json
index 8dce1fcec9b..47cdffb4be1 100644
--- a/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json
+++ b/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json
@@ -22,9 +22,9 @@
     "lenientProperties": false
   },
   "componentProperties": {
-    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": true, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
-    "lazyStartProducer": { "index": 1, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
-    "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": true, "secret": false, "inputLanguage": "", "description": "Sets the DataSource to use to communicate with the database." },
+    "lazyStartProducer": { "index": 1, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "inputLanguage": "", "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+    "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "inputLanguage": "", "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single i [...]
   },
   "headers": {
     "CamelSqlStoredTemplate": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The template", "constantName": "org.apache.camel.component.sql.stored.SqlStoredConstants#SQL_STORED_TEMPLATE" },
@@ -32,7 +32,7 @@
     "CamelSqlStoredUpdateCount": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The update count", "constantName": "org.apache.camel.component.sql.stored.SqlStoredConstants#SQL_STORED_UPDATE_COUNT" }
   },
   "properties": {
-    "template": { "index": 0, "kind": "path", "displayName": "Template", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "description": "Sets the stored procedure template to perform. You can externalize the template by using file: or classpath: as prefix and specify the location of the file." },
+    "template": { "index": 0, "kind": "path", "displayName": "Template", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "inputLanguage": "", "description": "Sets the stored procedure template to perform. You can externalize the template by using file: or classpath: as prefix and specify the location of the file." },
     "batch": { "index": 1, "kind": "parameter", "displayName": "Batch", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enables or disables batch mode" },
     "dataSource": { "index": 2, "kind": "parameter", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
     "function": { "index": 3, "kind": "parameter", "displayName": "Function", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether this call is for a function." },
diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java
index 938cf543f60..be0aad03b47 100644
--- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java
+++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/SqlEndpoint.java
@@ -34,7 +34,7 @@ import org.apache.camel.util.UnsafeUriCharactersEncoder;
 public class SqlEndpoint extends DefaultSqlEndpoint {
 
     @UriPath(description = "Sets the SQL query to perform. You can externalize the query by using file: or classpath: as prefix and specify the location of the file.")
-    @Metadata(required = true, supportFileReference = true)
+    @Metadata(required = true, supportFileReference = true, largeInput = true, inputLanguage = "sql")
     private String query;
 
     public SqlEndpoint() {
diff --git a/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java b/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java
index 53c0ce67c36..a8ae70fcded 100644
--- a/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java
+++ b/core/camel-api/src/generated/java/org/apache/camel/spi/Metadata.java
@@ -155,4 +155,19 @@ public @interface Metadata {
      * file.
      */
     boolean supportFileReference() default false;
+
+    /**
+     * Whether the option can be large input such as a SQL query, XSLT template, or scripting code.
+     *
+     * This can be used to help tooling to provide an input form instead of a single input field to give better user
+     * experience.
+     */
+    boolean largeInput() default false;
+
+    /**
+     * If the option is some specific language such as SQL, XSLT, XML, JavaScript or something else.
+     *
+     * This can be used to help tooling to provide a better user experience.
+     */
+    String inputLanguage() default "";
 }
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java
index 663cce54943..574660ebe09 100644
--- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BaseOptionModel.java
@@ -49,6 +49,8 @@ public abstract class BaseOptionModel {
     protected String description;
     protected String nestedType;  // optional and currently only used by configurer
     protected boolean supportFileReference;
+    protected boolean largeInput;
+    protected String inputLanguage;
 
     // todo: move this as a helper method
     protected boolean newGroup; // special for documentation rendering
@@ -285,6 +287,22 @@ public abstract class BaseOptionModel {
         this.supportFileReference = supportFileReference;
     }
 
+    public boolean isLargeInput() {
+        return largeInput;
+    }
+
+    public void setLargeInput(boolean largeInput) {
+        this.largeInput = largeInput;
+    }
+
+    public String getInputLanguage() {
+        return inputLanguage;
+    }
+
+    public void setInputLanguage(String inputLanguage) {
+        this.inputLanguage = inputLanguage;
+    }
+
     public String getShortGroup() {
         if (group != null && group.endsWith(" (advanced)")) {
             return group.substring(0, group.length() - 11);
diff --git a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
index 6c9df322e7f..27247339d85 100644
--- a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
+++ b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/JsonMapper.java
@@ -458,6 +458,8 @@ public final class JsonMapper {
         option.setGetterMethod(mp.getString("getterMethod"));
         option.setSetterMethod(mp.getString("setterMethod"));
         option.setSupportFileReference(mp.getBooleanOrDefault("supportFileReference", false));
+        option.setLargeInput(mp.getBooleanOrDefault("largeInput", false));
+        option.setInputLanguage(mp.getString("inputLanguage"));
     }
 
     private static void parseGroup(JsonObject mp, MainGroupModel option) {
@@ -538,6 +540,14 @@ public final class JsonMapper {
             // only include if supported to not regen all files
             prop.put("supportFileReference", option.isSupportFileReference());
         }
+        if (option.isLargeInput()) {
+            // only include if supported to not regen all files
+            prop.put("largeInput", option.isLargeInput());
+        }
+        if (option.getInputLanguage() != null) {
+            // only include if supported to not regen all files
+            prop.put("inputLanguage", option.getInputLanguage());
+        }
         prop.put("asPredicate", option.isAsPredicate());
         prop.put("configurationClass", option.getConfigurationClass());
         prop.put("configurationField", option.getConfigurationField());
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
index c54443b9faf..ca05171c1a2 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojo.java
@@ -970,6 +970,8 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
                 boolean secret = metadata != null && metadata.secret();
                 boolean autowired = metadata != null && metadata.autowired();
                 boolean supportFileReference = metadata != null && metadata.supportFileReference();
+                boolean largeInput = metadata != null && metadata.largeInput();
+                String inputLanguage = metadata != null ? metadata.inputLanguage() : null;
 
                 // we do not yet have default values / notes / as no annotation
                 // support yet
@@ -1086,6 +1088,8 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
                     option.setConfigurationClass(nestedTypeName);
                     option.setConfigurationField(nestedFieldName);
                     option.setSupportFileReference(supportFileReference);
+                    option.setLargeInput(largeInput);
+                    option.setInputLanguage(inputLanguage);
                     componentModel.addComponentOption(option);
                 }
             }
@@ -1506,6 +1510,8 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
             boolean isSecret = secret != null && secret || path.secret();
             boolean isAutowired = metadata != null && metadata.autowired();
             boolean supportFileReference = metadata != null && metadata.supportFileReference();
+            boolean largeInput = metadata != null && metadata.largeInput();
+            String inputLanguage = metadata != null ? metadata.inputLanguage() : null;
             String group = EndpointHelper.labelAsGroupName(label, componentModel.isConsumerOnly(),
                     componentModel.isProducerOnly());
 
@@ -1558,6 +1564,8 @@ public class EndpointSchemaGeneratorMojo extends AbstractGeneratorMojo {
             option.setConfigurationClass(nestedTypeName);
             option.setConfigurationField(nestedFieldName);
             option.setSupportFileReference(supportFileReference);
+            option.setLargeInput(largeInput);
+            option.setInputLanguage(inputLanguage);
             if (componentModel.getEndpointOptions().stream().noneMatch(opt -> name.equals(opt.getName()))) {
                 componentModel.addEndpointOption((EndpointOptionModel) option);
             }
diff --git a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java
index 53c0ce67c36..a8ae70fcded 100644
--- a/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java
+++ b/tooling/spi-annotations/src/main/java/org/apache/camel/spi/Metadata.java
@@ -155,4 +155,19 @@ public @interface Metadata {
      * file.
      */
     boolean supportFileReference() default false;
+
+    /**
+     * Whether the option can be large input such as a SQL query, XSLT template, or scripting code.
+     *
+     * This can be used to help tooling to provide an input form instead of a single input field to give better user
+     * experience.
+     */
+    boolean largeInput() default false;
+
+    /**
+     * If the option is some specific language such as SQL, XSLT, XML, JavaScript or something else.
+     *
+     * This can be used to help tooling to provide a better user experience.
+     */
+    String inputLanguage() default "";
 }