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 2020/09/16 07:17:14 UTC
[camel] 02/04: CAMEL-15478: Java source parser should resolve
return type that may be generic and parameterized.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch api
in repository https://gitbox.apache.org/repos/asf/camel.git
commit e4f5744ec65a9a5c9d36faa82f0292899148d278
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Sep 16 09:00:19 2020 +0200
CAMEL-15478: Java source parser should resolve return type that may be generic and parameterized.
---
.../org/apache/camel/maven/JavaSourceParser.java | 88 ++++++++++++++++++----
1 file changed, 75 insertions(+), 13 deletions(-)
diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavaSourceParser.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavaSourceParser.java
index 60286b0..a05596e 100644
--- a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavaSourceParser.java
+++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavaSourceParser.java
@@ -37,6 +37,8 @@ import org.jboss.forge.roaster.model.source.MethodHolderSource;
import org.jboss.forge.roaster.model.source.MethodSource;
import org.jboss.forge.roaster.model.source.ParameterSource;
import org.jboss.forge.roaster.model.source.TypeVariableSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.apache.camel.tooling.util.JavadocHelper.sanitizeDescription;
@@ -45,6 +47,8 @@ import static org.apache.camel.tooling.util.JavadocHelper.sanitizeDescription;
*/
public class JavaSourceParser {
+ private static final Logger LOG = LoggerFactory.getLogger(JavaSourceParser.class);
+
private List<String> methods = new ArrayList<>();
private Map<String, String> methodText = new HashMap<>();
private Map<String, Map<String, String>> parameters = new LinkedHashMap<>();
@@ -53,6 +57,65 @@ public class JavaSourceParser {
private String apiDescription;
private final Map<String, String> methodDescriptions = new HashMap<>();
+ private static String resolveParameterizedType(
+ AbstractGenericCapableJavaSource clazz, MethodSource ms, ParameterSource ps, Type type) {
+ String answer = resolveType(clazz, clazz, ms, type);
+
+ if (type.isParameterized()) {
+ // for parameterized types then it can get complex if they are variables (T, T extends Foo etc)
+ // or if there are no bounds for these types which we then can't resolve.
+ List<Type> types = type.getTypeArguments();
+ boolean bounds = false;
+ boolean found = false;
+ for (Type t : types) {
+ if (hasTypeVariableBounds(ms, clazz, t.getName())) {
+ bounds = true;
+ // okay now it gets complex as we have a type like T which is a type variable and we need to resolve that into
+ // what base class that is
+ String tn = resolveTypeVariable(ms, clazz, t.getName());
+ if (tn != null) {
+ answer = answer.replace(t.getName(), tn);
+ found = true;
+ }
+ }
+ }
+ if (!bounds && !found) {
+ // argh this is getting complex, it may be T or just java.lang.String but this **** generics and roaster
+ // does not make this easy, so let see if we can find out if all the types are a qualified type or only a variable
+ boolean fqn = types.stream().allMatch(t -> {
+ // if its from java itself then its okay
+ if (t.getQualifiedName().startsWith("java")) {
+ return true;
+ }
+ // okay lets assume its a type variable if the name is upper case only
+ boolean upperOnly = isUpperCaseOnly(t.getName());
+ return !upperOnly && t.getQualifiedName().indexOf('.') != -1;
+ });
+ if (!fqn) {
+ // remove generics we could not resolve that even if we have bounds information
+ bounds = true;
+ found = false;
+ }
+ }
+ if (bounds && !found) {
+ // remove generics we could not resolve that even if we have bounds information
+ answer = type.getQualifiedName();
+ }
+ } else if (ms.hasTypeVariable(answer) || clazz.hasTypeVariable(answer)) {
+ // okay now it gets complex as we have a type like T which is a type variable and we need to resolve that into
+ // what base class that is
+ answer = resolveTypeVariable(ms, clazz, answer);
+ }
+ if ((ps != null && ps.isVarArgs()) || type.isArray()) {
+ // the old way with javadoc did not use varargs in the signature, so lets transform this to an array style
+ answer = answer + "[]";
+ }
+
+ // remove java.lang. prefix as it should not be there
+ answer = answer.replaceAll("java.lang.", "");
+ return answer;
+ }
+
@SuppressWarnings("unchecked")
public synchronized void parse(InputStream in, String innerClass) throws Exception {
AbstractGenericCapableJavaSource rootClazz = (AbstractGenericCapableJavaSource) Roaster.parse(in);
@@ -67,6 +130,8 @@ public class JavaSourceParser {
}
}
+ LOG.debug("Parsing class: {}", clazz.getQualifiedName());
+
String rawClass = clazz.toUnformattedString();
String doc = getClassJavadocRaw(clazz, rawClass);
apiDescription = sanitizeJavaDocValue(doc, true);
@@ -81,6 +146,9 @@ public class JavaSourceParser {
List<MethodSource> ml = ((MethodHolderSource) clazz).getMethods();
for (MethodSource ms : ml) {
+ String methodName = ms.getName();
+ LOG.debug("Parsing method: {}", methodName);
+
// should not be constructor and must not be private
boolean isInterface = clazz instanceof JavaInterfaceSource;
boolean accept = isInterface || (!ms.isConstructor() && ms.isPublic());
@@ -97,20 +165,11 @@ public class JavaSourceParser {
methodDescriptions.put(ms.getName(), doc);
}
- String result;
- Type rt = ms.getReturnType();
- boolean hasTypeVariables = ms.hasTypeVariable(rt.getName()) || clazz.hasTypeVariable(rt.getName());
- if (hasTypeVariables) {
- // okay this gets to complex then remove the generics
- result = "Object";
- } else {
- result = resolveType(rootClazz, clazz, ms, rt);
- if (result == null || result.isEmpty()) {
- result = "void";
- }
+ String result = resolveParameterizedType(clazz, ms, null, ms.getReturnType());
+ if (result.isEmpty()) {
+ result = "void";
}
- // remove java.lang. prefix as it should not be there
- result = result.replaceAll("java.lang.", "");
+ LOG.trace("Parsed return type as: {}", result);
List<JavaDocTag> params = ms.getJavaDoc().getTags("@param");
@@ -123,6 +182,9 @@ public class JavaSourceParser {
ParameterSource ps = list.get(i);
String name = ps.getName();
String type = resolveType(rootClazz, clazz, ms, ps.getType());
+ LOG.trace("Parsing parameter #{} ({} {})", i, type, name);
+
+ // TODO: Call that other method
if (ps.getType().isParameterized()) {
// for parameterized types then it can get complex if they are variables (T, T extends Foo etc)
// or if there are no bounds for these types which we then can't resolve.