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 2019/06/05 21:01:20 UTC
[camel] 01/02: First pass at a generated endpoint DSL
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch endpoint-dsl
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5c684ea3774a5c2a0cc8388bb19a4d83ae180977
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Jun 5 10:50:26 2019 +0200
First pass at a generated endpoint DSL
---
components/camel-blueprint/pom.xml | 1 +
components/camel-spring/pom.xml | 1 +
.../camel/component/web3j/Web3jConfiguration.java | 2 +-
components/pom.xml | 1 +
.../model/endpoint/EndpointConfiguration.java | 5 +
.../camel/maven/packaging/EndpointDslMojo.java | 631 +++++++++++++++
.../camel/maven/packaging/generics/ClassUtil.java | 218 +++++
.../maven/packaging/generics/GenericsUtil.java | 880 +++++++++++++++++++++
.../generics/OwbGenericArrayTypeImpl.java | 64 ++
.../generics/OwbParametrizedTypeImpl.java | 125 +++
.../packaging/generics/OwbTypeVariableImpl.java | 182 +++++
.../packaging/generics/OwbWildcardTypeImpl.java | 82 ++
.../maven/packaging/model/EndpointOptionModel.java | 9 +
.../camel/maven/packaging/srcgen/GenericType.java | 2 +-
.../camel/maven/packaging/srcgen/JavaClass.java | 39 +-
15 files changed, 2237 insertions(+), 5 deletions(-)
diff --git a/components/camel-blueprint/pom.xml b/components/camel-blueprint/pom.xml
index 3179463..cd197ec 100644
--- a/components/camel-blueprint/pom.xml
+++ b/components/camel-blueprint/pom.xml
@@ -357,6 +357,7 @@
<include name="org/apache/camel/LoggingLevel.java"/>
<include name="org/apache/camel/ManagementStatisticsLevel.java"/>
<include name="**/package-info.java"/>
+ <exclude name="org/apache/camel/model/endpoint/*.java"/>
</fileset>
<fileset dir="${basedir}/../../core/camel-util/src/main/java">
<include
diff --git a/components/camel-spring/pom.xml b/components/camel-spring/pom.xml
index 2a873b7..8ba2a98 100644
--- a/components/camel-spring/pom.xml
+++ b/components/camel-spring/pom.xml
@@ -386,6 +386,7 @@
<include name="org/apache/camel/ShutdownRunningTask.java"/>
<include name="org/apache/camel/WaitForTaskToComplete.java"/>
<include name="org/apache/camel/package-info.java"/>
+ <exclude name="org/apache/camel/model/endpoint/*.java"/>
</fileset>
<fileset dir="${basedir}/../../core/camel-util/src/main/java">
<include name="org/apache/camel/concurrent/ThreadPoolRejectedPolicy.java"/>
diff --git a/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java b/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java
index 70db1be..d6a901e 100644
--- a/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java
+++ b/components/camel-web3j/src/main/java/org/apache/camel/component/web3j/Web3jConfiguration.java
@@ -48,7 +48,7 @@ public class Web3jConfiguration implements Cloneable {
@UriParam(label = "producer")
private String address;
- @UriParam(label = "common", javaType = "String")
+ @UriParam(label = "common", javaType = "java.lang.String")
private List<String> topics;
@UriParam(label = "producer")
diff --git a/components/pom.xml b/components/pom.xml
index ded9f83..97da906 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -368,6 +368,7 @@
<goal>prepare-spring-boot-starter</goal>
<goal>prepare-spring-boot-auto-configuration</goal>
<goal>generate-legal</goal>
+ <goal>generate-endpoint-dsl</goal>
</goals>
<phase>prepare-package</phase>
</execution>
diff --git a/core/camel-core/src/main/java/org/apache/camel/model/endpoint/EndpointConfiguration.java b/core/camel-core/src/main/java/org/apache/camel/model/endpoint/EndpointConfiguration.java
new file mode 100644
index 0000000..a8b85bf
--- /dev/null
+++ b/core/camel-core/src/main/java/org/apache/camel/model/endpoint/EndpointConfiguration.java
@@ -0,0 +1,5 @@
+package org.apache.camel.model.endpoint;
+
+public class EndpointConfiguration {
+
+}
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
new file mode 100644
index 0000000..c6730e1
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/EndpointDslMojo.java
@@ -0,0 +1,631 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Function;
+import java.util.function.Supplier;
+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.maven.packaging.srcgen.GenericType;
+import org.apache.camel.maven.packaging.srcgen.GenericType.BoundType;
+import org.apache.camel.maven.packaging.srcgen.JavaClass;
+import org.apache.camel.maven.packaging.srcgen.Property;
+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.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.jboss.forge.roaster.model.util.Strings;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.springframework.boot.liquibase.SpringPackageScanClassResolver;
+
+import static org.apache.camel.maven.packaging.AbstractGeneratorMojo.updateResource;
+import static org.apache.camel.maven.packaging.JSonSchemaHelper.getSafeValue;
+import static org.apache.camel.maven.packaging.PackageHelper.findCamelCoreDirectory;
+import static org.apache.camel.maven.packaging.PackageHelper.loadText;
+
+/**
+ * Generate Spring Boot auto configuration files for Camel components and data
+ * formats.
+ */
+@Mojo(name = "generate-endpoint-dsl", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, defaultPhase = LifecyclePhase.PROCESS_CLASSES)
+public class EndpointDslMojo extends AbstractMojo {
+
+ private static final Map<String, String> PRIMITIVEMAP;
+ private static final Map<String, Class> PRIMITIVE_CLASSES = new HashMap<>();
+
+ static {
+ PRIMITIVEMAP = new HashMap<>();
+ PRIMITIVEMAP.put("boolean", "java.lang.Boolean");
+ PRIMITIVEMAP.put("char", "java.lang.Character");
+ PRIMITIVEMAP.put("long", "java.lang.Long");
+ PRIMITIVEMAP.put("int", "java.lang.Integer");
+ PRIMITIVEMAP.put("integer", "java.lang.Integer");
+ PRIMITIVEMAP.put("byte", "java.lang.Byte");
+ PRIMITIVEMAP.put("short", "java.lang.Short");
+ PRIMITIVEMAP.put("double", "java.lang.Double");
+ PRIMITIVEMAP.put("float", "java.lang.Float");
+
+ PRIMITIVE_CLASSES.put("int", int.class);
+ PRIMITIVE_CLASSES.put("short", short.class);
+ PRIMITIVE_CLASSES.put("long", long.class);
+ PRIMITIVE_CLASSES.put("byte", byte.class);
+ PRIMITIVE_CLASSES.put("char", char.class);
+ PRIMITIVE_CLASSES.put("float", float.class);
+ PRIMITIVE_CLASSES.put("double", double.class);
+ PRIMITIVE_CLASSES.put("boolean", boolean.class);
+ PRIMITIVE_CLASSES.put("void", void.class);
+ }
+
+ private static final String[] IGNORE_MODULES = {/* Non-standard -> */ };
+
+ /**
+ * The maven project.
+ */
+ @Parameter(property = "project", required = true, readonly = true)
+ protected MavenProject project;
+
+ /**
+ * The project build directory
+ */
+ @Parameter(defaultValue = "${project.build.directory}")
+ protected File buildDir;
+
+ /**
+ * The base directory
+ */
+ @Parameter(defaultValue = "${basedir}")
+ protected File baseDir;
+
+ DynamicClassLoader projectClassLoader;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ // Do not generate code for ignored module
+ if (Arrays.asList(IGNORE_MODULES).contains(project.getArtifactId())) {
+ getLog().info("Component auto-configuration will not be created: component contained in the ignore list");
+ return;
+ }
+
+ executeAll();
+ }
+
+ private void executeAll() throws MojoExecutionException, MojoFailureException {
+ Map<File, Supplier<String>> files = PackageHelper.findJsonFiles(buildDir, p -> p.isDirectory() || p.getName().endsWith(".json")).stream()
+ .collect(Collectors.toMap(Function.identity(), s -> cache(() -> loadJson(s))));
+
+ executeComponent(files);
+ }
+
+ private static String loadJson(File file) {
+ try (InputStream is = new FileInputStream(file)) {
+ return loadText(is);
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ private static <T> Supplier<T> cache(Supplier<T> supplier) {
+ return new Supplier<T>() {
+ T value;
+
+ @Override
+ public T get() {
+ if (value == null) {
+ value = supplier.get();
+ }
+ return value;
+ }
+ };
+ }
+
+ private void executeComponent(Map<File, Supplier<String>> jsonFiles) throws MojoExecutionException, MojoFailureException {
+ // find the component names
+ Set<String> componentNames = new TreeSet<>();
+ findComponentNames(buildDir, componentNames);
+
+ // create auto configuration for the components
+ if (!componentNames.isEmpty()) {
+ getLog().debug("Found " + componentNames.size() + " components");
+
+ List<ComponentModel> allModels = new LinkedList<>();
+ for (String componentName : componentNames) {
+ String json = loadComponentJson(jsonFiles, componentName);
+ if (json != null) {
+ ComponentModel model = generateComponentModel(componentName, json);
+ allModels.add(model);
+ }
+ }
+
+ // Group the models by implementing classes
+ Map<String, List<ComponentModel>> grModels = allModels.stream().collect(Collectors.groupingBy(ComponentModel::getJavaType));
+ for (String componentClass : grModels.keySet()) {
+ List<ComponentModel> compModels = grModels.get(componentClass);
+ ComponentModel model = compModels.get(0); // They should be
+ // equivalent
+ List<String> aliases = compModels.stream().map(ComponentModel::getScheme).sorted().collect(Collectors.toList());
+
+ String pkg = "org.apache.camel.model.endpoint";
+
+ String overrideComponentName = null;
+ if (aliases.size() > 1) {
+ // determine component name when there are multiple ones
+ overrideComponentName = model.getArtifactId().replace("camel-", "");
+ }
+
+ createEndpointDsl(pkg, model, overrideComponentName);
+ }
+ }
+ }
+
+ private void createEndpointDsl(String packageName, ComponentModel model, String overrideComponentName) throws MojoFailureException {
+ int pos = model.getJavaType().lastIndexOf(".");
+ String name = model.getJavaType().substring(pos + 1);
+ name = name.replace("Component", "Endpoint");
+
+ Class<?> realComponentClass = loadClass(model.getJavaType());
+ Class<?> realEndpointClass = findEndpointClass(realComponentClass);
+
+ final JavaClass javaClass = new JavaClass(getProjectClassLoader());
+ javaClass.setPackage(packageName);
+ javaClass.setName(name);
+
+ Map<String, JavaClass> enumClasses = new HashMap<>();
+
+ JavaClass commonClass = javaClass.addNestedType().setPublic().setStatic(true);
+ commonClass.setName(name.replace("Endpoint", "Common"));
+ commonClass.extendSuperType("EndpointConfiguration");
+
+ JavaClass consumerClass = javaClass.addNestedType().setPublic().setStatic(true);
+ consumerClass.setName(name.replace("Endpoint", "Consumer"));
+ consumerClass.extendSuperType(name.replace("Endpoint", "Common"));
+
+ JavaClass producerClass = javaClass.addNestedType().setPublic().setStatic(true);
+ producerClass.setName(name.replace("Endpoint", "Producer"));
+ producerClass.extendSuperType(name.replace("Endpoint", "Common"));
+
+ String doc = "Generated by camel-package-maven-plugin - do not edit this file!";
+ if (!Strings.isBlank(model.getDescription())) {
+ doc = model.getDescription() + "\n\n" + doc;
+ }
+ javaClass.getJavaDoc().setText(doc);
+
+ javaClass.addAnnotation(Generated.class.getName())
+ .setStringValue("value", EndpointDslMojo.class.getName());
+
+ for (EndpointOptionModel option : model.getEndpointOptions()) {
+
+ JavaClass target = commonClass;
+ if (option.getLabel() != null) {
+ if (option.getLabel().contains("producer")) {
+ target = producerClass;
+ } else if (option.getLabel().contains("consumer")) {
+ target = consumerClass;
+ }
+ }
+
+ GenericType gtype;
+ try {
+ Field field = findField(realComponentClass, realEndpointClass, option);
+ gtype = new GenericType(GenericsUtil.resolveType(realEndpointClass, field));
+ gtype = getType(javaClass, enumClasses, option.getEnums(), gtype.toString());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+
+ Property prop = target.addProperty(gtype, option.getName());
+ if ("true".equals(option.getDeprecated())) {
+ prop.getField().addAnnotation(Deprecated.class);
+ prop.getAccessor().addAnnotation(Deprecated.class);
+ prop.getMutator().addAnnotation(Deprecated.class);
+ }
+ if (!Strings.isBlank(option.getDescription())) {
+ String desc = option.getDescription();
+ if (!desc.endsWith(".")) {
+ desc = desc + ".";
+ }
+ desc = desc + " The option is a " + option.getJavaType() + " type.";
+ prop.getField().getJavaDoc().setFullText(desc);
+ }
+ }
+
+ String fileName = packageName.replaceAll("\\.", "\\/") + "/" + name + ".java";
+ writeSourceIfChanged(javaClass, fileName, false);
+ }
+
+ private Class<?> findEndpointClass(Class<?> componentClass) {
+ String endpointName = componentClass.getCanonicalName().replaceFirst("Component", "Endpoint");
+ if ("org.apache.camel.component.disruptor.vm.DisruptorVmEndpoint".equals(endpointName)) {
+ endpointName = "org.apache.camel.component.disruptor.DisruptorEndpoint";
+ } else if ("org.apache.camel.component.etcd.EtcdEndpoint".equals(endpointName)) {
+ endpointName = "org.apache.camel.component.etcd.AbstractEtcdPollingEndpoint";
+ } else if ("org.apache.camel.websocket.jsr356.JSR356WebSocketEndpoint".equals(endpointName)) {
+ endpointName = "org.apache.camel.websocket.jsr356.JSR356Endpoint";
+ }
+ return loadClass(endpointName);
+ }
+
+ private Field findField(Class<?> realComponentClass, Class<?> realEndpointClass, EndpointOptionModel option) throws NoSuchFieldException {
+ Field field = null;
+ List<Class<?>> classes = new ArrayList<>();
+ classes.add(realComponentClass);
+ classes.add(realEndpointClass);
+ while (!classes.isEmpty()) {
+ Class cl = classes.remove(0);
+ for (Field f : cl.getDeclaredFields()) {
+ String n = f.getName();
+ UriPath path = f.getAnnotation(UriPath.class);
+ if (path != null && !Strings.isBlank(path.name())) {
+ n = path.name();
+ }
+ UriParam param = f.getAnnotation(UriParam.class);
+ if (param != null && !Strings.isBlank(param.name())) {
+ n = param.name();
+ }
+ if (n.equals(option.getName())) {
+ field = f;
+ break;
+ }
+ if (f.getType().isAnnotationPresent(UriParams.class)) {
+ classes.add(f.getType());
+ }
+ }
+ if (field != null) {
+ break;
+ }
+ cl = cl.getSuperclass();
+ if (cl != null) {
+ classes.add(cl);
+ }
+ }
+ if (field == null) {
+ throw new NoSuchFieldException("Could not find field for option " + option.getName());
+ }
+ return field;
+ }
+
+ static boolean isPrimitive(String type) {
+ return PRIMITIVE_CLASSES.containsKey(type);
+ }
+
+ private Class<?> loadClass(String loadClassName) {
+ Class<?> optionClass;
+ String org = loadClassName;
+ while (true) {
+ try {
+ optionClass = getProjectClassLoader().loadClass(loadClassName);
+ break;
+ } catch (ClassNotFoundException e) {
+ int dotIndex = loadClassName.lastIndexOf('.');
+ if (dotIndex == -1) {
+ throw new IllegalArgumentException(org);
+ } else {
+ loadClassName = loadClassName.substring(0, dotIndex) + "$" + loadClassName.substring(dotIndex + 1);
+ }
+ }
+ }
+ return optionClass;
+ }
+
+
+ private GenericType getType(JavaClass javaClass, Map<String, JavaClass> enumClasses, String enums, String type) {
+ type = type.trim();
+ // Check if this is an array
+ if (type.endsWith("[]")) {
+ GenericType t = getType(javaClass, enumClasses, enums, type.substring(0, type.length() - 2));
+ return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t);
+ }
+ // Check if this is a generic
+ int genericIndex = type.indexOf('<');
+ if (genericIndex > 0) {
+ if (!type.endsWith(">")) {
+ throw new IllegalArgumentException("Can not load type: " + type);
+ }
+ GenericType base = getType(javaClass, enumClasses, enums, type.substring(0, genericIndex));
+ if (base.getRawClass() == Object.class) {
+ return base;
+ }
+ String[] params = splitParams(type.substring(genericIndex + 1, type.length() - 1));
+ GenericType[] types = new GenericType[params.length];
+ for (int i = 0; i < params.length; i++) {
+ types[i] = getType(javaClass, enumClasses, enums, params[i]);
+ }
+ return new GenericType(base.getRawClass(), types);
+ }
+ // Primitive
+ if (isPrimitive(type)) {
+ return new GenericType(PRIMITIVE_CLASSES.get(type));
+ }
+ // Extends
+ if (type.startsWith("? extends ")) {
+ String raw = type.substring("? extends ".length());
+ return new GenericType(loadClass(raw), BoundType.Extends);
+ }
+ // Super
+ if (type.startsWith("? super ")) {
+ String raw = type.substring("? extends ".length());
+ return new GenericType(loadClass(raw), BoundType.Super);
+ }
+ // Wildcard
+ if (type.equals("?")) {
+ return new GenericType(Object.class, BoundType.Extends);
+ }
+ if (loadClass(type).isEnum() && !Strings.isBlank(enums) && !isCamelCoreType(type)) {
+ String enumClassName = type.substring(type.lastIndexOf('.') + 1);
+ if (enumClassName.contains("$")) {
+ enumClassName = enumClassName.substring(enumClassName.indexOf('$') + 1);
+ }
+ JavaClass enumClass = enumClasses.get(enumClassName);
+ if (enumClass == null) {
+ enumClass = javaClass.addNestedType().setPublic().setStatic(true)
+ .setName(enumClassName).setEnum(true);
+ enumClasses.put(enumClassName, enumClass);
+ for (String value : enums.split(",")) {
+ enumClass.addValue(value
+ .replace('.', '_')
+ .replace('-', '_'));
+ }
+ }
+ type = javaClass.getPackage() + "." + javaClass.getName() + "$" + enumClassName;
+ return new GenericType(generateDummyClass(type));
+ }
+ if (!isCamelCoreType(type)) {
+ getLog().debug("Substituting java.lang.Object to " + type);
+ return new GenericType(Object.class);
+ }
+ return new GenericType(loadClass(type));
+ }
+
+ private String[] splitParams(String string) {
+ List<String> params = new ArrayList<>();
+ int cur = 0;
+ int start = 0;
+ int opened = 0;
+ while (true) {
+ int nextComma = string.indexOf(',', cur);
+ int nextOpen = string.indexOf('<', cur);
+ int nextClose = string.indexOf('>', cur);
+ if (nextComma < 0) {
+ params.add(string.substring(start));
+ return params.toArray(new String[0]);
+ } else if ((nextOpen < 0 || nextComma < nextOpen)
+ && (nextClose < 0 || nextComma < nextClose)
+ && opened == 0) {
+ params.add(string.substring(start, nextComma));
+ start = cur = nextComma + 1;
+ } else if (nextOpen < 0) {
+ if (--opened < 0) {
+ throw new IllegalStateException();
+ }
+ cur = nextClose + 1;
+ } else if (nextClose < 0 || nextOpen < nextClose) {
+ ++opened;
+ cur = nextOpen + 1;
+ } else {
+ if (--opened < 0) {
+ throw new IllegalStateException();
+ }
+ cur = nextClose + 1;
+ }
+ }
+ }
+
+ private boolean isCamelCoreType(String type) {
+ return type.startsWith("java.")
+ || type.matches("org\\.apache\\.camel\\.(spi\\.)?([A-Za-z]+)");
+ }
+
+ protected DynamicClassLoader getProjectClassLoader() {
+ if (projectClassLoader == null) {
+ final List<?> classpathElements;
+ try {
+ classpathElements = project.getTestClasspathElements();
+ } catch (org.apache.maven.artifact.DependencyResolutionRequiredException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ final URL[] urls = new URL[classpathElements.size()];
+ int i = 0;
+ for (Iterator<?> it = classpathElements.iterator(); it.hasNext(); i++) {
+ try {
+ urls[i] = new File((String)it.next()).toURI().toURL();
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ projectClassLoader = new DynamicClassLoader(urls, tccl != null ? tccl : getClass().getClassLoader());
+ }
+ return projectClassLoader;
+ }
+
+ static class DynamicClassLoader extends URLClassLoader {
+ public DynamicClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ }
+
+ public Class defineClass(String name, byte[] data) {
+ return super.defineClass(name, data, 0, data.length);
+ }
+ }
+
+ private Class generateDummyClass(String clazzName) {
+ try {
+ return getProjectClassLoader().loadClass(clazzName);
+ } catch (ClassNotFoundException e) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, clazzName.replace('.', '/'), null, "java/lang/Object", null);
+ cw.visitEnd();
+ return getProjectClassLoader().defineClass(clazzName, cw.toByteArray());
+ }
+ }
+
+ private static String loadComponentJson(Map<File, Supplier<String>> jsonFiles, String componentName) {
+ return loadJsonOfType(jsonFiles, componentName, "component");
+ }
+
+ 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")) {
+ String json = entry.getValue().get();
+ if (json.contains("\"kind\": \"" + type + "\"")) {
+ return json;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static ComponentModel generateComponentModel(String componentName, String json) {
+ List<Map<String, String>> rows = JSonSchemaHelper.parseJsonSchema("component", json, false);
+
+ ComponentModel component = new ComponentModel(true);
+ 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");
+
+ if (f.exists() && f.isDirectory()) {
+ File[] files = f.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ // skip directories as there may be a sub .resolver
+ // directory
+ if (file.isDirectory()) {
+ continue;
+ }
+ String name = file.getName();
+ if (name.charAt(0) != '.') {
+ componentNames.add(name);
+ }
+ }
+ }
+ }
+ }
+
+ private void writeSourceIfChanged(JavaClass source, String fileName, boolean innerClassesLast) throws MojoFailureException {
+ writeSourceIfChanged(source.printClass(innerClassesLast), fileName);
+ }
+
+ private void writeSourceIfChanged(String source, String fileName) throws MojoFailureException {
+ File core = findCamelCoreDirectory(project.getBasedir());
+
+ File target = new File(new File(core, "src/main/java"), fileName);
+
+ try {
+ String header;
+ try (InputStream is = getClass().getClassLoader().getResourceAsStream("license-header-java.txt")) {
+ header = loadText(is);
+ }
+ String code = header + source;
+ getLog().debug("Source code generated:\n" + code);
+
+ updateResource(null, target.toPath(), 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/generics/ClassUtil.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.java
new file mode 100644
index 0000000..7c1d0ea
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/ClassUtil.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.maven.packaging.generics;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Utility classes with respect to the class operations.
+ *
+ * @author <a href="mailto:gurkanerdogdu@yahoo.com">Gurkan Erdogdu</a>
+ * @since 1.0
+ */
+public final class ClassUtil {
+ public static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPERS_MAP;
+
+ static {
+ Map<Class<?>, Class<?>> primitiveToWrappersMap = new HashMap<Class<?>, Class<?>>();
+ primitiveToWrappersMap.put(Integer.TYPE, Integer.class);
+ primitiveToWrappersMap.put(Float.TYPE, Float.class);
+ primitiveToWrappersMap.put(Double.TYPE, Double.class);
+ primitiveToWrappersMap.put(Character.TYPE, Character.class);
+ primitiveToWrappersMap.put(Long.TYPE, Long.class);
+ primitiveToWrappersMap.put(Byte.TYPE, Byte.class);
+ primitiveToWrappersMap.put(Short.TYPE, Short.class);
+ primitiveToWrappersMap.put(Boolean.TYPE, Boolean.class);
+ primitiveToWrappersMap.put(Void.TYPE, Void.class);
+ PRIMITIVE_TO_WRAPPERS_MAP = Collections.unmodifiableMap(primitiveToWrappersMap);
+ }
+
+ public static final Type[] NO_TYPES = new Type[0];
+
+ /*
+ * Private constructor
+ */
+ private ClassUtil() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static boolean isSame(Type type1, Type type2) {
+ if ((type1 instanceof Class) && ((Class<?>) type1).isPrimitive()) {
+ type1 = PRIMITIVE_TO_WRAPPERS_MAP.get(type1);
+ }
+ if ((type2 instanceof Class) && ((Class<?>) type2).isPrimitive()) {
+ type2 = PRIMITIVE_TO_WRAPPERS_MAP.get(type2);
+ }
+ return type1 == type2;
+ }
+
+ public static Class<?> getPrimitiveWrapper(Class<?> clazz) {
+ return PRIMITIVE_TO_WRAPPERS_MAP.get(clazz);
+
+ }
+
+ /**
+ * Gets the class of the given type arguments.
+ * <p>
+ * If the given type {@link Type} parameters is an instance of the
+ * {@link ParameterizedType}, it returns the raw type otherwise it return
+ * the casted {@link Class} of the type argument.
+ * </p>
+ *
+ * @param type class or parametrized type
+ * @return
+ */
+ public static Class<?> getClass(Type type)
+ {
+ return getClazz(type);
+ }
+
+
+ /**
+ * Returns true if type is an instance of <code>ParameterizedType</code>
+ * else otherwise.
+ *
+ * @param type type of the artifact
+ * @return true if type is an instance of <code>ParameterizedType</code>
+ */
+ public static boolean isParametrizedType(Type type)
+ {
+ return type instanceof ParameterizedType;
+ }
+
+ /**
+ * Returns true if type is an instance of <code>WildcardType</code>
+ * else otherwise.
+ *
+ * @param type type of the artifact
+ * @return true if type is an instance of <code>WildcardType</code>
+ */
+ public static boolean isWildCardType(Type type)
+ {
+ return type instanceof WildcardType;
+ }
+
+
+ /**
+ * Returns true if rhs is assignable type
+ * to the lhs, false otherwise.
+ *
+ * @param lhs left hand side class
+ * @param rhs right hand side class
+ * @return true if rhs is assignable to lhs
+ */
+ public static boolean isClassAssignableFrom(Class<?> lhs, Class<?> rhs)
+ {
+ if(lhs.isPrimitive())
+ {
+ lhs = getPrimitiveWrapper(lhs);
+ }
+
+ if(rhs.isPrimitive())
+ {
+ rhs = getPrimitiveWrapper(rhs);
+ }
+
+ if (lhs.isAssignableFrom(rhs))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return raw class type for given type.
+ *
+ * @param type base type instance
+ * @return class type for given type
+ */
+ public static Class<?> getClazz(Type type) {
+ if (type instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) type;
+ return (Class<?>) pt.getRawType();
+ } else if (type instanceof Class) {
+ return (Class<?>) type;
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType arrayType = (GenericArrayType) type;
+ return Array.newInstance(getClazz(arrayType.getGenericComponentType()), 0).getClass();
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ Type[] bounds = wildcardType.getUpperBounds();
+ if (bounds.length > 1) {
+ throw new IllegalArgumentException("Illegal use of wild card type with more than one upper bound: " + wildcardType);
+ } else if (bounds.length == 0) {
+ return Object.class;
+ } else {
+ return getClass(bounds[0]);
+ }
+ } else if (type instanceof TypeVariable) {
+ TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+ if (typeVariable.getBounds().length > 1) {
+ throw new IllegalArgumentException("Illegal use of type variable with more than one bound: " + typeVariable);
+ } else {
+ Type[] bounds = typeVariable.getBounds();
+ if (bounds.length == 0) {
+ return Object.class;
+ } else {
+ return getClass(bounds[0]);
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + type);
+ }
+ }
+
+
+ public static boolean isRawClassEquals(Type ipType, Type apiType) {
+ Class ipClass = getRawPrimitiveType(ipType);
+ Class apiClass = getRawPrimitiveType(apiType);
+
+ if (ipClass == null || apiClass == null) {
+ // we found some illegal generics
+ return false;
+ }
+
+ return ipClass.equals(apiClass);
+ }
+
+ private static Class getRawPrimitiveType(Type type) {
+ if (type instanceof Class) {
+ if (((Class) type).isPrimitive()) {
+ return getPrimitiveWrapper((Class) type);
+ }
+ return (Class) type;
+ }
+
+ if (type instanceof ParameterizedType) {
+ return getRawPrimitiveType(((ParameterizedType) type).getRawType());
+ }
+
+ return null;
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/GenericsUtil.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/GenericsUtil.java
new file mode 100644
index 0000000..ec68bfb
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/GenericsUtil.java
@@ -0,0 +1,880 @@
+/*
+ * 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.generics;
+
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility classes for generic type operations.
+ */
+public final class GenericsUtil {
+ public static boolean satisfiesDependency(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType) {
+ if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+ } else {
+ Type injectionPointRawType = injectionPointType instanceof ParameterizedType ? ((ParameterizedType) injectionPointType).getRawType() : injectionPointType;
+ Type beanRawType = beanType instanceof ParameterizedType ? ((ParameterizedType) beanType).getRawType() : beanType;
+
+ if (ClassUtil.isSame(injectionPointRawType, beanRawType)) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean satisfiesDependencyRaw(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType) {
+ if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+ } else {
+ Type injectionPointRawType = injectionPointType instanceof ParameterizedType ? ((ParameterizedType) injectionPointType).getRawType() : injectionPointType;
+ Type beanRawType = beanType instanceof ParameterizedType ? ((ParameterizedType) beanType).getRawType() : beanType;
+
+ if (ClassUtil.isSame(injectionPointRawType, beanRawType)) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointRawType, beanRawType);
+ } else {
+ Class bean = (Class) beanType;
+ if (bean.getSuperclass() != null && ClassUtil.isRawClassEquals(injectionPointType, bean.getSuperclass())) {
+ return true;
+ }
+
+ Class<?>[] interfaces = bean.getInterfaces();
+ if (interfaces == null || interfaces.length == 0) {
+ return false;
+ }
+
+ for (Class<?> clazz : interfaces) {
+ if (ClassUtil.isRawClassEquals(injectionPointType, clazz)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 5.2.3 and 5.2.4
+ */
+ public static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, Type requiredType, Type beanType) {
+ if (requiredType instanceof Class) {
+ return isAssignableFrom(isDelegateOrEvent, (Class<?>) requiredType, beanType);
+ } else if (requiredType instanceof ParameterizedType) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, (ParameterizedType) requiredType, beanType);
+ } else if (requiredType instanceof TypeVariable) {
+ return isAssignableFrom(isDelegateOrEvent, (TypeVariable<?>) requiredType, beanType);
+ } else if (requiredType instanceof GenericArrayType) {
+ return Class.class.isInstance(beanType) && Class.class.cast(beanType).isArray()
+ && isAssignableFrom(isDelegateOrEvent, (GenericArrayType) requiredType, beanType);
+ } else if (requiredType instanceof WildcardType) {
+ return isAssignableFrom(isDelegateOrEvent, (WildcardType) requiredType, beanType);
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
+ }
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, Type beanType) {
+ if (beanType instanceof Class) {
+ return isAssignableFrom(injectionPointType, (Class<?>) beanType);
+ } else if (beanType instanceof TypeVariable) {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>) beanType);
+ } else if (beanType instanceof ParameterizedType) {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType) beanType);
+ } else if (beanType instanceof GenericArrayType) {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (GenericArrayType) beanType);
+ } else if (beanType instanceof WildcardType) {
+ return isAssignableFrom(isDelegateOrEvent, (Type) injectionPointType, (WildcardType) beanType);
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass());
+ }
+ }
+
+ private static boolean isAssignableFrom(Class<?> injectionPointType, Class<?> beanType) {
+ return ClassUtil.isClassAssignableFrom(injectionPointType, beanType);
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, TypeVariable<?> beanType) {
+ for (Type bounds : beanType.getBounds()) {
+ if (isAssignableFrom(isDelegateOrEvent, injectionPointType, bounds)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * CDI Spec. 5.2.4: "A parameterized bean type is considered assignable to a raw required type
+ * if the raw generics are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object."
+ */
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, ParameterizedType beanType) {
+ if (beanType.getRawType() != injectionPointType) {
+ return false; //raw generics don't match
+ }
+
+ if (isDelegateOrEvent) {
+ // for delegate and events we match 'in reverse' kind off
+ // @Observes ProcessInjectionPoint<?, Instance> does also match Instance<SomeBean>
+ return isAssignableFrom(true, injectionPointType, beanType.getRawType());
+ }
+
+ for (Type typeArgument : beanType.getActualTypeArguments()) {
+ if (typeArgument == Object.class) {
+ continue;
+ }
+ if (!(typeArgument instanceof TypeVariable)) {
+ return false; //neither object nor type variable
+ }
+ TypeVariable<?> typeVariable = (TypeVariable<?>) typeArgument;
+ for (Type bounds : typeVariable.getBounds()) {
+ if (bounds != Object.class) {
+ return false; //bound type variable
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, GenericArrayType beanType) {
+ return injectionPointType.isArray() && isAssignableFrom(isDelegateOrEvent, injectionPointType.getComponentType(), beanType.getGenericComponentType());
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, Type injectionPointType, WildcardType beanType) {
+ for (Type bounds : beanType.getLowerBounds()) {
+ if (!isAssignableFrom(isDelegateOrEvent, false, bounds, injectionPointType)) {
+ return false;
+ }
+ }
+ for (Type bounds : beanType.getUpperBounds()) {
+ if (isAssignableFrom(isDelegateOrEvent, false, injectionPointType, bounds)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Type beanType) {
+ if (beanType instanceof Class) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (Class<?>) beanType);
+ } else if (beanType instanceof TypeVariable) {
+ return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (TypeVariable<?>) beanType);
+ } else if (beanType instanceof ParameterizedType) {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType) beanType);
+ } else if (beanType instanceof WildcardType) {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (WildcardType) beanType);
+ } else if (beanType instanceof GenericArrayType) {
+ return false;
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + beanType.getClass());
+ }
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Class<?> beanType) {
+ Class<?> rawInjectionPointType = getRawType(injectionPointType);
+ if (rawInjectionPointType.equals(beanType)) {
+ if (isProducer) {
+ for (final Type t : injectionPointType.getActualTypeArguments()) {
+ if (!TypeVariable.class.isInstance(t) || !isNotBound(TypeVariable.class.cast(t).getBounds())) {
+ if (!Class.class.isInstance(t) || Object.class != t) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ if (!rawInjectionPointType.isAssignableFrom(beanType)) {
+ return false;
+ }
+ if (beanType.getSuperclass() != null && isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType.getGenericSuperclass())) {
+ return true;
+ }
+ for (Type genericInterface : beanType.getGenericInterfaces()) {
+ if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, genericInterface)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, TypeVariable<?> beanType) {
+ final Type[] types = beanType.getBounds();
+ if (isNotBound(types)) {
+ return true;
+ }
+ for (final Type bounds : types) {
+ if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, bounds)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * CDI Spec. 5.2.4
+ */
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, ParameterizedType injectionPointType, ParameterizedType beanType) {
+ if (injectionPointType.getRawType() != beanType.getRawType()) {
+ return false;
+ }
+ boolean swapParams = !isDelegateOrEvent;
+ Type[] injectionPointTypeArguments = injectionPointType.getActualTypeArguments();
+ Type[] beanTypeArguments = beanType.getActualTypeArguments();
+ for (int i = 0; i < injectionPointTypeArguments.length; i++) {
+ Type injectionPointTypeArgument = injectionPointTypeArguments[i];
+ Type beanTypeArgument = beanTypeArguments[i];
+
+ // for this special case it's actually an 'assignable to', thus we swap the params, see CDI-389
+ // but this special rule does not apply to Delegate injection points...
+ if (swapParams &&
+ (injectionPointTypeArgument instanceof Class || injectionPointTypeArgument instanceof TypeVariable) &&
+ beanTypeArgument instanceof TypeVariable) {
+ final Type[] bounds = ((TypeVariable<?>) beanTypeArgument).getBounds();
+ final boolean isNotBound = isNotBound(bounds);
+ if (!isNotBound) {
+ for (final Type upperBound : bounds) {
+ if (!isAssignableFrom(true, false, upperBound, injectionPointTypeArgument)) {
+ return false;
+ }
+ }
+ }
+ } else if (swapParams && injectionPointTypeArgument instanceof TypeVariable) {
+ return false;
+ } else if (isDelegateOrEvent && injectionPointTypeArgument instanceof Class && beanTypeArgument instanceof Class) {
+ // if no wildcard type was given then we require a real exact match.
+ return injectionPointTypeArgument.equals(beanTypeArgument);
+
+ } else if (!isAssignableFrom(isDelegateOrEvent, false, injectionPointTypeArgument, beanTypeArgument)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isNotBound(final Type... bounds) {
+ return bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class == bounds[0]);
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, TypeVariable<?> injectionPointType, Type beanType) {
+ for (Type bounds : injectionPointType.getBounds()) {
+ if (!isAssignableFrom(isDelegateOrEvent, false, bounds, beanType)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // rules are a bit different when in an array so we handle ParameterizedType manually (not reusing isAssignableFrom)
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, GenericArrayType injectionPointType, Type beanType) {
+ final Type genericComponentType = injectionPointType.getGenericComponentType();
+ final Class componentType = Class.class.cast(beanType).getComponentType();
+ if (Class.class.isInstance(genericComponentType)) {
+ return Class.class.cast(genericComponentType).isAssignableFrom(componentType);
+ }
+ if (ParameterizedType.class.isInstance(genericComponentType)) {
+ return isAssignableFrom(isDelegateOrEvent, false, ParameterizedType.class.cast(genericComponentType).getRawType(), componentType);
+ }
+ return isAssignableFrom(isDelegateOrEvent, false, genericComponentType, componentType);
+ }
+
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, Type beanType) {
+ if (beanType instanceof TypeVariable) {
+ return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>) beanType);
+ }
+ for (Type bounds : injectionPointType.getLowerBounds()) {
+ if (!isAssignableFrom(isDelegateOrEvent, false, beanType, bounds)) {
+ return false;
+ }
+ }
+ for (Type bounds : injectionPointType.getUpperBounds()) {
+ Set<Type> beanTypeClosure = getTypeClosure(beanType);
+ boolean isAssignable = false;
+ for (Type beanSupertype : beanTypeClosure) {
+ if (isAssignableFrom(isDelegateOrEvent, false, bounds, beanSupertype)
+ || (Class.class.isInstance(bounds)
+ && ParameterizedType.class.isInstance(beanSupertype)
+ && bounds == ParameterizedType.class.cast(beanSupertype).getRawType())) {
+ isAssignable = true;
+ break;
+ }
+ }
+ if (!isAssignable) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * CDI 1.1 Spec. 5.2.4, third bullet point
+ */
+ private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, TypeVariable<?> beanType) {
+ for (Type upperBound : injectionPointType.getUpperBounds()) {
+ for (Type bound : beanType.getBounds()) {
+ if (!isAssignableFrom(isDelegateOrEvent, false, upperBound, bound) && !isAssignableFrom(isDelegateOrEvent, false, bound, upperBound)) {
+ return false;
+ }
+ }
+ }
+ for (Type lowerBound : injectionPointType.getLowerBounds()) {
+ for (Type bound : beanType.getBounds()) {
+ if (!isAssignableFrom(isDelegateOrEvent, false, bound, lowerBound)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return <tt>true</tt>, if the specified type declaration contains an unresolved type variable.
+ */
+ public static boolean containsTypeVariable(Type type) {
+ if (type instanceof Class) {
+ return false;
+ } else if (type instanceof TypeVariable) {
+ return true;
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ return containTypeVariable(parameterizedType.getActualTypeArguments());
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ return containTypeVariable(wildcardType.getUpperBounds()) || containTypeVariable(wildcardType.getLowerBounds());
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType arrayType = (GenericArrayType) type;
+ return containsTypeVariable(arrayType.getGenericComponentType());
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+ }
+
+ }
+
+ public static boolean containTypeVariable(Collection<? extends Type> types) {
+ return containTypeVariable(types.toArray(new Type[types.size()]));
+ }
+
+ public static boolean containTypeVariable(Type[] types) {
+ for (Type type : types) {
+ if (containsTypeVariable(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param type to check
+ * @return {@code true} if the given type contains a {@link WildcardType}
+ * {@code false} otherwise
+ */
+ public static boolean containsWildcardType(Type type) {
+ if (!(type instanceof ParameterizedType)) {
+ return false;
+ }
+
+ for (Type typeArgument : getParameterizedType(type).getActualTypeArguments()) {
+ if (ClassUtil.isParametrizedType(typeArgument)) {
+ if (containsWildcardType(typeArgument)) {
+ return true;
+ }
+ } else {
+ if (ClassUtil.isWildCardType(typeArgument)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Resolves the actual type of the specified field for the type hierarchy specified by the given subclass
+ */
+ public static Type resolveType(Class<?> subclass, Field field) {
+ return resolveType(field.getGenericType(), subclass, newSeenList());
+ }
+
+ /**
+ * Resolves the actual return type of the specified method for the type hierarchy specified by the given subclass
+ */
+ public static Type resolveReturnType(Class<?> subclass, Method method) {
+ return resolveType(method.getGenericReturnType(), subclass, newSeenList());
+ }
+
+ /**
+ * Resolves the actual parameter generics of the specified constructor for the type hierarchy specified by the given subclass
+ */
+ public static Type[] resolveParameterTypes(Class<?> subclass, Constructor<?> constructor) {
+ return resolveTypes(constructor.getGenericParameterTypes(), subclass);
+ }
+
+ /**
+ * Resolves the actual parameter generics of the specified method for the type hierarchy specified by the given subclass
+ */
+ public static Type[] resolveParameterTypes(Class<?> subclass, Method method) {
+ return resolveTypes(method.getGenericParameterTypes(), subclass);
+ }
+
+ /**
+ * Resolves the actual type of the specified type for the type hierarchy specified by the given subclass
+ */
+ public static Type resolveType(Type type, Class<?> subclass, Member member) {
+ return resolveType(type, subclass, newSeenList());
+ }
+
+ public static Type resolveType(Type type, Class<?> subclass, Member member, Collection<TypeVariable<?>> seen) {
+ return resolveType(type, subclass, seen);
+ }
+
+ public static Type resolveType(Type type, Type actualType, Collection<TypeVariable<?>> seen) {
+ if (type instanceof Class) {
+ return type;
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+
+ Type[] resolvedTypeArguments;
+ if (Enum.class.equals(parameterizedType.getRawType())) {
+ // Enums derive from themselves, which would create an infinite loop
+ // we directly escape the loop if we detect this.
+ resolvedTypeArguments = new Type[]{new OwbWildcardTypeImpl(new Type[]{Enum.class}, ClassUtil.NO_TYPES)};
+ } else {
+ resolvedTypeArguments = resolveTypes(parameterizedType.getActualTypeArguments(), actualType, seen);
+
+ }
+
+ return new OwbParametrizedTypeImpl(parameterizedType.getOwnerType(), parameterizedType.getRawType(), resolvedTypeArguments);
+ } else if (type instanceof TypeVariable) {
+ TypeVariable<?> variable = (TypeVariable<?>) type;
+ return resolveTypeVariable(variable, actualType, seen);
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ Type[] upperBounds = resolveTypes(wildcardType.getUpperBounds(), actualType, seen);
+ Type[] lowerBounds = resolveTypes(wildcardType.getLowerBounds(), actualType, seen);
+ return new OwbWildcardTypeImpl(upperBounds, lowerBounds);
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType arrayType = (GenericArrayType) type;
+ return createArrayType(resolveType(arrayType.getGenericComponentType(), actualType, seen));
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+ }
+ }
+
+ public static Type[] resolveTypes(Type[] types, Type actualType, Collection<TypeVariable<?>> seen) {
+ Type[] resolvedTypeArguments = new Type[types.length];
+ for (int i = 0; i < types.length; i++) {
+ final Type type = resolveType(types[i], actualType, seen);
+ if (type != null) // means a stackoverflow was avoided, just keep what we have
+ {
+ resolvedTypeArguments[i] = type;
+ }
+ }
+ return resolvedTypeArguments;
+ }
+
+ public static Type[] resolveTypes(Type[] types, Type actualType) {
+ Type[] resolvedTypeArguments = new Type[types.length];
+ for (int i = 0; i < types.length; i++) {
+ resolvedTypeArguments[i] = resolveType(types[i], actualType, newSeenList());
+ }
+ return resolvedTypeArguments;
+ }
+
+ public static Set<Type> getTypeClosure(Class<?> type) {
+ return getTypeClosure(type, type);
+ }
+
+ public static Set<Type> getTypeClosure(Type actualType) {
+ return getTypeClosure(actualType, actualType);
+ }
+
+ /**
+ * Returns the type closure for the specified parameters.
+ * <h3>Example 1:</h3>
+ * <p>
+ * Take the following classes:
+ * </p>
+ * <code>
+ * public class Foo<T> {
+ * private T t;
+ * }
+ * public class Bar extends Foo<Number> {
+ * }
+ * </code>
+ * <p>
+ * To get the type closure of T in the context of Bar (which is {Number.class, Object.class}), you have to call this method like
+ * </p>
+ * <code>
+ * GenericUtil.getTypeClosure(Foo.class.getDeclaredField("t").getType(), Bar.class, Foo.class);
+ * </code>
+ * <h3>Example 2:</h3>
+ * <p>
+ * Take the following classes:
+ * </p>
+ * <code>
+ * public class Foo<T> {
+ * private T t;
+ * }
+ * public class Bar<T> extends Foo<T> {
+ * }
+ * </code>
+ * <p>
+ * To get the type closure of Bar<T> in the context of Foo<Number> (which are besides Object.class the <tt>ParameterizedType</tt>s Bar<Number> and Foo<Number>),
+ * you have to call this method like
+ * </p>
+ * <code>
+ * GenericUtil.getTypeClosure(Foo.class, new TypeLiteral<Foo<Number>>() {}.getType(), Bar.class);
+ * </code>
+ *
+ * @param type the type to get the closure for
+ * @param actualType the context to bind type variables
+ * @return the type closure
+ */
+ public static Set<Type> getTypeClosure(Type type, Type actualType) {
+ Class<?> rawType = getRawType(type);
+ Class<?> actualRawType = getRawType(actualType);
+ if (rawType.isAssignableFrom(actualRawType) && rawType != actualRawType) {
+ return getTypeClosure(actualType, type);
+ }
+ if (hasTypeParameters(type)) {
+ type = getParameterizedType(type);
+ }
+ return getDirectTypeClosure(type, actualType);
+ }
+
+ public static Set<Type> getDirectTypeClosure(final Type type, final Type actualType) {
+ Set<Type> typeClosure = new HashSet<Type>();
+ typeClosure.add(Object.class);
+ fillTypeHierarchy(typeClosure, type, actualType);
+ return typeClosure;
+ }
+
+ private static void fillTypeHierarchy(Set<Type> set, Type type, Type actualType) {
+ if (type == null) {
+ return;
+ }
+ Type resolvedType = GenericsUtil.resolveType(type, actualType, newSeenList());
+ set.add(resolvedType);
+ Class<?> resolvedClass = GenericsUtil.getRawType(resolvedType, actualType);
+ if (resolvedClass.getSuperclass() != null) {
+ fillTypeHierarchy(set, resolvedClass.getGenericSuperclass(), resolvedType);
+ }
+ for (Type interfaceType : resolvedClass.getGenericInterfaces()) {
+ fillTypeHierarchy(set, interfaceType, resolvedType);
+ }
+ }
+
+ private static Collection<TypeVariable<?>> newSeenList() {
+ return new ArrayList<TypeVariable<?>>();
+ }
+
+ public static boolean hasTypeParameters(Type type) {
+ if (type instanceof Class) {
+ Class<?> classType = (Class<?>) type;
+ return classType.getTypeParameters().length > 0;
+ }
+ return false;
+ }
+
+ public static ParameterizedType getParameterizedType(Type type) {
+ if (type instanceof ParameterizedType) {
+ return (ParameterizedType) type;
+ } else if (type instanceof Class) {
+ Class<?> classType = (Class<?>) type;
+ return new OwbParametrizedTypeImpl(classType.getDeclaringClass(), classType, classType.getTypeParameters());
+ } else {
+ throw new IllegalArgumentException(type.getClass().getSimpleName() + " is not supported");
+ }
+ }
+
+ public static <T> Class<T> getRawType(Type type) {
+ return getRawType(type, null);
+ }
+
+ static <T> Class<T> getRawType(Type type, Type actualType) {
+ if (type instanceof Class) {
+ return (Class<T>) type;
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ return getRawType(parameterizedType.getRawType(), actualType);
+ } else if (type instanceof TypeVariable) {
+ TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+ Type mostSpecificType = getMostSpecificType(getRawTypes(typeVariable.getBounds(), actualType), typeVariable.getBounds());
+ return getRawType(mostSpecificType, actualType);
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ Type mostSpecificType = getMostSpecificType(getRawTypes(wildcardType.getUpperBounds(), actualType), wildcardType.getUpperBounds());
+ return getRawType(mostSpecificType, actualType);
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType arrayType = (GenericArrayType) type;
+ return getRawType(createArrayType(getRawType(arrayType.getGenericComponentType(), actualType)), actualType);
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
+ }
+ }
+
+ private static <T> Class<T>[] getRawTypes(Type[] types) {
+ return getRawTypes(types, null);
+ }
+
+ private static <T> Class<T>[] getRawTypes(Type[] types, Type actualType) {
+ Class<T>[] rawTypes = new Class[types.length];
+ for (int i = 0; i < types.length; i++) {
+ rawTypes[i] = getRawType(types[i], actualType);
+ }
+ return rawTypes;
+ }
+
+ private static Type getMostSpecificType(Class<?>[] types, Type[] genericTypes) {
+ Class<?> mostSpecificType = types[0];
+ int mostSpecificIndex = 0;
+ for (int i = 0; i < types.length; i++) {
+ if (mostSpecificType.isAssignableFrom(types[i])) {
+ mostSpecificType = types[i];
+ mostSpecificIndex = i;
+ }
+ }
+ return genericTypes[mostSpecificIndex];
+ }
+
+ private static Class<?>[] getClassTypes(Class<?>[] rawTypes) {
+ List<Class<?>> classTypes = new ArrayList<Class<?>>();
+ for (Class<?> rawType : rawTypes) {
+ if (!rawType.isInterface()) {
+ classTypes.add(rawType);
+ }
+ }
+ return classTypes.toArray(new Class[classTypes.size()]);
+ }
+
+ private static Type resolveTypeVariable(TypeVariable<?> variable, Type actualType, Collection<TypeVariable<?>> seen) {
+ if (actualType == null) {
+ return variable;
+ }
+ Class<?> declaringClass = getDeclaringClass(variable.getGenericDeclaration());
+ Class<?> actualClass = getRawType(actualType);
+ if (actualClass == declaringClass) {
+ return resolveTypeVariable(variable, variable.getGenericDeclaration(), getParameterizedType(actualType), seen);
+ } else if (actualClass.isAssignableFrom(declaringClass)) {
+ Class<?> directSubclass = getDirectSubclass(declaringClass, actualClass);
+ Type[] typeArguments = resolveTypeArguments(directSubclass, actualType);
+ Type directSubtype = new OwbParametrizedTypeImpl(directSubclass.getDeclaringClass(), directSubclass, typeArguments);
+ return resolveTypeVariable(variable, directSubtype, seen);
+ } else // if (declaringClass.isAssignableFrom(actualClass))
+ {
+ Type genericSuperclass = getGenericSuperclass(actualClass, declaringClass);
+ if (genericSuperclass == null) {
+ return variable;
+ } else if (genericSuperclass instanceof Class) {
+ // special handling for type erasure
+ Class<?> superclass = (Class<?>) genericSuperclass;
+ genericSuperclass = new OwbParametrizedTypeImpl(superclass.getDeclaringClass(), superclass, getRawTypes(superclass.getTypeParameters()));
+ } else {
+ ParameterizedType genericSupertype = getParameterizedType(genericSuperclass);
+ Type[] typeArguments = resolveTypeArguments(getParameterizedType(actualType), genericSupertype);
+ genericSuperclass = new OwbParametrizedTypeImpl(genericSupertype.getOwnerType(), genericSupertype.getRawType(), typeArguments);
+ }
+ Type resolvedType = resolveTypeVariable(variable, genericSuperclass, seen);
+ if (resolvedType instanceof TypeVariable) {
+ TypeVariable<?> resolvedTypeVariable = (TypeVariable<?>) resolvedType;
+ TypeVariable<?>[] typeParameters = actualClass.getTypeParameters();
+ for (int i = 0; i < typeParameters.length; i++) {
+ if (typeParameters[i].getName().equals(resolvedTypeVariable.getName())) {
+ resolvedType = getParameterizedType(actualType).getActualTypeArguments()[i];
+ break;
+ }
+ }
+ }
+ return resolvedType;
+ }
+ }
+
+ private static Class<?> getDeclaringClass(GenericDeclaration declaration) {
+ if (declaration instanceof Class) {
+ return (Class<?>) declaration;
+ } else if (declaration instanceof Member) {
+ return ((Member) declaration).getDeclaringClass();
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + declaration.getClass());
+ }
+ }
+
+ private static Type resolveTypeVariable(TypeVariable<?> variable, GenericDeclaration declaration, ParameterizedType type,
+ Collection<TypeVariable<?>> seen) {
+ int index = getIndex(declaration, variable);
+ if (declaration instanceof Class) {
+ if (index >= 0) {
+ return type.getActualTypeArguments()[index];
+ } else {
+ index = getIndex(type, variable);
+ if (index >= 0) {
+ return declaration.getTypeParameters()[index];
+ }
+ }
+ } else {
+ if (seen.contains(variable)) {
+ return null;
+ }
+ seen.add(variable);
+
+ Type[] resolvedBounds = resolveTypes(declaration.getTypeParameters()[index].getBounds(), type, seen);
+ return OwbTypeVariableImpl.createTypeVariable(variable, resolvedBounds);
+ }
+ return variable;
+ }
+
+ private static int getIndex(GenericDeclaration declaration, TypeVariable<?> variable) {
+ Type[] typeParameters = declaration.getTypeParameters();
+ for (int i = 0; i < typeParameters.length; i++) {
+ if (typeParameters[i] instanceof TypeVariable) {
+ TypeVariable<?> variableArgument = (TypeVariable<?>) typeParameters[i];
+ if (variableArgument.getName().equals(variable.getName())) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private static int getIndex(ParameterizedType type, TypeVariable<?> variable) {
+ Type[] actualTypeArguments = type.getActualTypeArguments();
+ for (int i = 0; i < actualTypeArguments.length; i++) {
+ if (actualTypeArguments[i] instanceof TypeVariable) {
+ TypeVariable<?> variableArgument = (TypeVariable<?>) actualTypeArguments[i];
+ if (variableArgument.getName().equals(variable.getName())) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private static Class<?> getDirectSubclass(Class<?> declaringClass, Class<?> actualClass) {
+ if (actualClass.isInterface()) {
+ Class<?> subclass = declaringClass;
+ for (Class<?> iface : declaringClass.getInterfaces()) {
+ if (iface == actualClass) {
+ return subclass;
+ }
+ if (actualClass.isAssignableFrom(iface)) {
+ subclass = iface;
+ } else {
+ subclass = declaringClass.getSuperclass();
+ }
+ }
+ return getDirectSubclass(subclass, actualClass);
+ } else {
+ Class<?> directSubclass = declaringClass;
+ while (directSubclass.getSuperclass() != actualClass) {
+ directSubclass = directSubclass.getSuperclass();
+ }
+ return directSubclass;
+ }
+ }
+
+ private static Type getGenericSuperclass(Class<?> subclass, Class<?> superclass) {
+ if (!superclass.isInterface()) {
+ return subclass.getGenericSuperclass();
+ } else {
+ for (Type genericInterface : subclass.getGenericInterfaces()) {
+ if (getRawType(genericInterface) == superclass) {
+ return genericInterface;
+ }
+ }
+ }
+ return superclass;
+ }
+
+ private static Type[] resolveTypeArguments(Class<?> subclass, Type supertype) {
+ if (supertype instanceof ParameterizedType) {
+ ParameterizedType parameterizedSupertype = (ParameterizedType) supertype;
+ return resolveTypeArguments(subclass, parameterizedSupertype);
+ } else {
+ return subclass.getTypeParameters();
+ }
+ }
+
+ private static Type[] resolveTypeArguments(Class<?> subclass, ParameterizedType parameterizedSupertype) {
+ Type genericSuperclass = getGenericSuperclass(subclass, getRawType(parameterizedSupertype));
+ if (!(genericSuperclass instanceof ParameterizedType)) {
+ return subclass.getTypeParameters();
+ }
+ ParameterizedType parameterizedSuperclass = (ParameterizedType) genericSuperclass;
+ Type[] typeParameters = subclass.getTypeParameters();
+ Type[] actualTypeArguments = parameterizedSupertype.getActualTypeArguments();
+ return resolveTypeArguments(parameterizedSuperclass, typeParameters, actualTypeArguments);
+ }
+
+ private static Type[] resolveTypeArguments(ParameterizedType subtype, ParameterizedType parameterizedSupertype) {
+ return resolveTypeArguments(getParameterizedType(getRawType(subtype)), parameterizedSupertype.getActualTypeArguments(), subtype.getActualTypeArguments());
+ }
+
+ private static Type[] resolveTypeArguments(ParameterizedType parameterizedType, Type[] typeParameters, Type[] actualTypeArguments) {
+ Type[] resolvedTypeArguments = new Type[typeParameters.length];
+ for (int i = 0; i < typeParameters.length; i++) {
+ resolvedTypeArguments[i] = resolveTypeArgument(parameterizedType, typeParameters[i], actualTypeArguments);
+ }
+ return resolvedTypeArguments;
+ }
+
+ private static Type resolveTypeArgument(ParameterizedType parameterizedType, Type typeParameter, Type[] actualTypeArguments) {
+ if (typeParameter instanceof TypeVariable) {
+ TypeVariable<?> variable = (TypeVariable<?>) typeParameter;
+ int index = getIndex(parameterizedType, variable);
+ if (index == -1) {
+ return typeParameter;
+ } else {
+ return actualTypeArguments[index];
+ }
+ } else if (typeParameter instanceof GenericArrayType) {
+ GenericArrayType array = (GenericArrayType) typeParameter;
+ return createArrayType(resolveTypeArgument(parameterizedType, array.getGenericComponentType(), actualTypeArguments));
+ } else {
+ return typeParameter;
+ }
+ }
+
+ private static Type createArrayType(Type componentType) {
+ if (componentType instanceof Class) {
+ return Array.newInstance((Class<?>) componentType, 0).getClass();
+ } else {
+ return new OwbGenericArrayTypeImpl(componentType);
+ }
+ }
+
+ public static Type resolveType(ParameterizedType parameterizedType, Type metadataType) {
+ return resolveType(parameterizedType, metadataType, newSeenList());
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbGenericArrayTypeImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbGenericArrayTypeImpl.java
new file mode 100644
index 0000000..2b3722b
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbGenericArrayTypeImpl.java
@@ -0,0 +1,64 @@
+/*
+ * 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.generics;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+
+public class OwbGenericArrayTypeImpl implements GenericArrayType {
+
+ private Type componentType;
+
+ public OwbGenericArrayTypeImpl(Type componentType) {
+ this.componentType = componentType;
+ }
+
+ @Override
+ public Type getGenericComponentType() {
+ return componentType;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return componentType.hashCode();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj instanceof GenericArrayType) {
+ return ((GenericArrayType) obj).getGenericComponentType().equals(componentType);
+ } else {
+ return false;
+ }
+
+ }
+
+ public String toString() {
+ return componentType + "[]";
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbParametrizedTypeImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbParametrizedTypeImpl.java
new file mode 100644
index 0000000..2cf3a95
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbParametrizedTypeImpl.java
@@ -0,0 +1,125 @@
+/*
+ * 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.generics;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+/**
+ * Custom parametrized type implementation.
+ *
+ * @version $Rev: 1621935 $ $Date: 2014-09-02 09:07:32 +0200 (Tue, 02 Sep 2014) $
+ */
+public class OwbParametrizedTypeImpl implements ParameterizedType {
+ /**
+ * Owner type
+ */
+ private final Type owner;
+
+ /**
+ * Raw type
+ */
+ private final Type rawType;
+
+ /**
+ * Actual type arguments
+ */
+ private final Type[] types;
+
+ /**
+ * New instance.
+ *
+ * @param owner owner
+ * @param raw raw
+ */
+ public OwbParametrizedTypeImpl(Type owner, Type raw, Type... types) {
+ this.owner = owner;
+ rawType = raw;
+ this.types = types;
+ }
+
+ @Override
+ public Type[] getActualTypeArguments() {
+ return types.clone();
+ }
+
+ @Override
+ public Type getOwnerType() {
+ return owner;
+ }
+
+ @Override
+ public Type getRawType() {
+ return rawType;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(types) ^ (owner == null ? 0 : owner.hashCode()) ^ (rawType == null ? 0 : rawType.hashCode());
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj instanceof ParameterizedType) {
+ ParameterizedType that = (ParameterizedType) obj;
+ Type thatOwnerType = that.getOwnerType();
+ Type thatRawType = that.getRawType();
+ return (owner == null ? thatOwnerType == null : owner.equals(thatOwnerType))
+ && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))
+ && Arrays.equals(types, that.getActualTypeArguments());
+ } else {
+ return false;
+ }
+
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(((Class<?>) rawType).getName());
+ Type[] actualTypes = getActualTypeArguments();
+ if (actualTypes.length > 0) {
+ buffer.append("<");
+ int length = actualTypes.length;
+ for (int i = 0; i < length; i++) {
+ if (actualTypes[i] instanceof Class) {
+ buffer.append(((Class<?>) actualTypes[i]).getSimpleName());
+ } else {
+ buffer.append(actualTypes[i].toString());
+ }
+ if (i != actualTypes.length - 1) {
+ buffer.append(", ");
+ }
+ }
+
+ buffer.append(">");
+ }
+
+ return buffer.toString();
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbTypeVariableImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbTypeVariableImpl.java
new file mode 100644
index 0000000..7abd222
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbTypeVariableImpl.java
@@ -0,0 +1,182 @@
+/*
+ * 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.generics;
+
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+
+
+public class OwbTypeVariableImpl
+{
+ private static final Class<?>[] TYPE_VARIABLE_TYPES = new Class<?>[]{TypeVariable.class};
+
+ /**
+ * Java TypeVariable is different in various JDK versions. Thus it is not possible to e.g.
+ * write a custom TypeVariable which works in either Java7 and Java8 as they introduced
+ * new methods in Java8 which have return generics which only exist in Java8 :(
+ *
+ * As workaround we dynamically crate a proxy to wrap this and do the delegation manually.
+ * This is of course slower, but as we do not use it often it might not have much impact.
+ *
+ * @param typeVariable
+ * @param bounds
+ * @return the typeVariable with the defined bounds.
+ */
+ public static TypeVariable createTypeVariable(TypeVariable typeVariable, Type... bounds)
+ {
+ TypeVariable tv = (TypeVariable) Proxy.newProxyInstance(OwbTypeVariableImpl.class.getClassLoader(), TYPE_VARIABLE_TYPES,
+ new OwbTypeVariableInvocationHandler(typeVariable, bounds));
+
+ return tv;
+ }
+
+
+
+ public static class OwbTypeVariableInvocationHandler implements InvocationHandler
+ {
+
+ private String name;
+ private GenericDeclaration genericDeclaration;
+ private Type[] bounds;
+
+
+ public OwbTypeVariableInvocationHandler(TypeVariable typeVariable, Type... bounds)
+ {
+ name = typeVariable.getName();
+ genericDeclaration = typeVariable.getGenericDeclaration();
+ if (bounds == null || bounds.length == 0)
+ {
+ this.bounds = typeVariable.getBounds();
+ }
+ else
+ {
+ this.bounds = bounds;
+ }
+ }
+
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ String methodName = method.getName();
+ if ("equals".equals(methodName))
+ {
+ return typeVariableEquals(args[0]);
+ }
+ else if ("hashCode".equals(methodName))
+ {
+ return typeVariableHashCode();
+ }
+ else if ("toString".equals(methodName))
+ {
+ return typeVariableToString();
+ }
+ else if ("getName".equals(methodName))
+ {
+ return getName();
+ }
+ else if ("getGenericDeclaration".equals(methodName))
+ {
+ return getGenericDeclaration();
+ }
+ else if ("getBounds".equals(methodName))
+ {
+ return getBounds();
+ }
+
+
+ // new method from java8...
+ return null;
+ }
+
+ /** method from TypeVariable */
+ public String getName()
+ {
+ return name;
+ }
+
+ /** method from TypeVariable */
+ public GenericDeclaration getGenericDeclaration()
+ {
+ return genericDeclaration;
+ }
+
+ /** method from TypeVariable */
+ public Type[] getBounds()
+ {
+ return bounds.clone();
+ }
+
+ /** method from TypeVariable */
+ public int typeVariableHashCode()
+ {
+ return Arrays.hashCode(bounds) ^ name.hashCode() ^ genericDeclaration.hashCode();
+ }
+
+ /** method from TypeVariable */
+ public boolean typeVariableEquals(Object object)
+ {
+ if (this == object)
+ {
+ return true;
+ }
+ else if (object instanceof TypeVariable)
+ {
+ TypeVariable<?> that = (TypeVariable<?>)object;
+ return name.equals(that.getName()) && genericDeclaration.equals(that.getGenericDeclaration()) && Arrays.equals(bounds, that.getBounds());
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ /** method from TypeVariable */
+ public String typeVariableToString()
+ {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(name);
+ if (bounds.length > 0)
+ {
+ buffer.append(" extends ");
+ boolean first = true;
+ for (Type bound: bounds)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buffer.append(',');
+ }
+ buffer.append(' ').append(bound);
+ }
+ }
+ return buffer.toString();
+ }
+
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbWildcardTypeImpl.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbWildcardTypeImpl.java
new file mode 100644
index 0000000..aebcfd0
--- /dev/null
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/generics/OwbWildcardTypeImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.generics;
+
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+
+public class OwbWildcardTypeImpl implements WildcardType {
+
+ private Type[] upperBounds;
+ private Type[] lowerBounds;
+
+ public OwbWildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
+ this.upperBounds = upperBounds.clone();
+ this.lowerBounds = lowerBounds.clone();
+ }
+
+ @Override
+ public Type[] getUpperBounds() {
+ return upperBounds.clone();
+ }
+
+ @Override
+ public Type[] getLowerBounds() {
+ return lowerBounds.clone();
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder("?");
+ if (upperBounds.length > 0) {
+ buffer.append(" extends");
+ boolean first = true;
+ for (Type upperBound : upperBounds) {
+ if (first) {
+ first = false;
+ } else {
+ buffer.append(',');
+ }
+ buffer.append(' ');
+ if (upperBound instanceof Class) {
+ buffer.append(((Class<?>) upperBound).getSimpleName());
+ } else {
+ buffer.append(upperBound);
+ }
+ }
+ }
+ if (lowerBounds.length > 0) {
+ buffer.append(" super");
+ boolean first = true;
+ for (Type lowerBound : lowerBounds) {
+ if (first) {
+ first = false;
+ } else {
+ buffer.append(',');
+ }
+ buffer.append(' ');
+ if (lowerBound instanceof Class) {
+ buffer.append(((Class<?>) lowerBound).getSimpleName());
+ } else {
+ buffer.append(lowerBound);
+ }
+ }
+ }
+ return buffer.toString();
+ }
+}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
index 87d8df8..3416df5 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/model/EndpointOptionModel.java
@@ -26,6 +26,7 @@ public class EndpointOptionModel {
private String displayName;
private String kind;
private String group;
+ private String label;
private String required;
private String type;
private String javaType;
@@ -74,6 +75,14 @@ public class EndpointOptionModel {
this.group = group;
}
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
public String getRequired() {
return required;
}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java
index 3004605..8baa0ac 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/GenericType.java
@@ -35,7 +35,7 @@ public class GenericType {
private static final Map<String, Class> PRIMITIVE_CLASSES = new HashMap<>();
- enum BoundType {
+ public enum BoundType {
Exact, Extends, Super
}
diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java
index 22ae6ca..0be7f1d 100644
--- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java
+++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/srcgen/JavaClass.java
@@ -25,6 +25,8 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
+import org.jboss.forge.roaster.model.util.Strings;
+
public class JavaClass {
ClassLoader classLoader;
@@ -38,6 +40,7 @@ public class JavaClass {
List<Field> fields = new ArrayList<>();
List<Method> methods = new ArrayList<>();
List<JavaClass> nested = new ArrayList<>();
+ List<String> values = new ArrayList<>();
Javadoc javadoc = new Javadoc();
boolean isStatic;
boolean isPublic = true;
@@ -57,6 +60,14 @@ public class JavaClass {
this.parent = parent;
}
+ protected ClassLoader getClassLoader() {
+ if (classLoader == null && parent != null) {
+ return parent.getClassLoader();
+ } else {
+ return classLoader;
+ }
+ }
+
public JavaClass setStatic(boolean aStatic) {
isStatic = aStatic;
return this;
@@ -72,6 +83,9 @@ public class JavaClass {
return this;
}
+ public String getPackage() {
+ return packageName;
+ }
public JavaClass setPackage(String packageName) {
this.packageName = packageName;
return this;
@@ -121,7 +135,7 @@ public class JavaClass {
public Annotation addAnnotation(String type) {
try {
- Class<?> cl = classLoader.loadClass(type);
+ Class<?> cl = getClassLoader().loadClass(type);
return addAnnotation(cl);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to parse type", e);
@@ -139,9 +153,9 @@ public class JavaClass {
public Property addProperty(String type, String name) {
try {
- return addProperty(GenericType.parse(type, classLoader), name);
+ return addProperty(GenericType.parse(type, getClassLoader()), name);
} catch (ClassNotFoundException e) {
- throw new IllegalArgumentException("Unable to parse type", e);
+ throw new IllegalArgumentException("Unable to parse type " + type + " for property " + name, e);
}
}
@@ -173,6 +187,10 @@ public class JavaClass {
return clazz;
}
+ public void addValue(String value) {
+ values.add(value);
+ }
+
public boolean isClass() {
return isClass;
}
@@ -236,6 +254,21 @@ public class JavaClass {
printJavadoc(sb, indent, javadoc);
printAnnotations(sb, indent, annotations);
+ if (isEnum) {
+ sb.append(indent)
+ .append(isPublic ? "public " : "")
+ .append(isStatic ? "static " : "")
+ .append("enum ").append(name).append(" {\n")
+ .append(indent)
+ .append(" ")
+ .append(Strings.join(values, ", "))
+ .append(";\n")
+ .append(indent)
+ .append("}");
+ return;
+
+ }
+
StringBuilder sb2 = new StringBuilder();
sb2.append(indent);
if (isPublic) {