From 091888cd03fcb54a7c89e39fcfaa5e5f94f70abd Mon Sep 17 00:00:00 2001 From: Drozdov Artyom Date: Thu, 9 Jan 2014 18:18:57 +0400 Subject: [PATCH 1/6] add: support for generics in beans add: bounds for extending --- .../com/sun/codemodel/JSuperWildcard.java | 76 +++++++++++++++++++ .../handler/BeanHandler.java | 2 + .../helper/APTCodeModelHelper.java | 43 +++++++---- .../holder/BaseGeneratedClassHolder.java | 11 ++- .../androidannotations/ebean/EBeanTest.java | 8 +- .../ebean/SomeActivity.java | 3 + .../ebean/SomeGenericBean.java | 20 +++++ .../ebean/SomeGenericBeanExt.java | 7 ++ 8 files changed, 153 insertions(+), 17 deletions(-) create mode 100644 AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java create mode 100644 AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java create mode 100644 AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBeanExt.java diff --git a/AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java b/AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java new file mode 100644 index 0000000000..e81ed86030 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java @@ -0,0 +1,76 @@ +package com.sun.codemodel; + +import java.util.Iterator; +import java.util.List; + +// TODO : At his time (01/2013), it is not possible to handle the +// super bound because code model does not offer a way to model +// statement like " ? super X" +// (see http://java.net/jira/browse/CODEMODEL-11) +// +// This class is a hack against it. +// So, if it will be fixed in code model - just remove this +public class JSuperWildcard extends JClass { + + private final JClass bound; + + public JSuperWildcard(JClass bound) { + super(bound.owner()); + this.bound = bound; + } + + public String name() { + return "? super "+ bound.name(); + } + + public String fullName() { + return "? super "+ bound.fullName(); + } + + public JPackage _package() { + return null; + } + + /** + * Returns the class bound of this variable. + * + *

+ * If no bound is given, this method returns {@link Object}. + */ + public JClass _extends() { + if(bound !=null) + return bound; + else + return owner().ref(Object.class); + } + + /** + * Returns the interface bounds of this variable. + */ + public Iterator _implements() { + return bound._implements(); + } + + public boolean isInterface() { + return false; + } + + public boolean isAbstract() { + return false; + } + + protected JClass substituteParams(JTypeVar[] variables, List bindings) { + JClass nb = bound.substituteParams(variables,bindings); + if(nb== bound) + return this; + else + return nb.wildcard(); + } + + public void generate(JFormatter f) { + if(bound._extends()==null) + f.p("?"); // instead of "? extends Object" + else + f.p("? super").g(bound); + } +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/handler/BeanHandler.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/handler/BeanHandler.java index e41320009a..6cb75dccd3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/handler/BeanHandler.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/handler/BeanHandler.java @@ -22,6 +22,7 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EBean; @@ -60,6 +61,7 @@ public void process(Element element, EComponentHolder holder) throws Exception { TypeMirror elementType = annotationHelper.extractAnnotationClassParameter(element); if (elementType == null) { elementType = element.asType(); + elementType = holder.processingEnvironment().getTypeUtils().erasure(elementType); } String fieldName = element.getSimpleName().toString(); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java index c99c7abc5f..e974376cd5 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -27,10 +27,12 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Types; import org.androidannotations.holder.EComponentHolder; import org.androidannotations.holder.GeneratedClassHolder; @@ -47,6 +49,7 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JStatement; +import com.sun.codemodel.JSuperWildcard; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; @@ -73,12 +76,17 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { return declaredClass; } else if (type instanceof WildcardType) { - // TODO : At his time (01/2013), it is not possible to handle the - // super bound because code model does not offer a way to model - // statement like " ? super X" - // (see http://java.net/jira/browse/CODEMODEL-11) WildcardType wildcardType = (WildcardType) type; + TypeMirror bound = wildcardType.getExtendsBound(); + if (bound == null) { + bound = wildcardType.getSuperBound(); + if (bound == null) { + throw new IllegalArgumentException("There are should be 'extends' or 'super' in the wildcard"); + } + return superWildcard(typeMirrorToJClass(bound, holder)); + } + TypeMirror extendsBound = wildcardType.getExtendsBound(); if (extendsBound == null) { @@ -97,6 +105,10 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { } } + private JClass superWildcard(final JClass bound) { + return new JSuperWildcard(bound); + } + public static class Parameter { public final String name; public final JClass jClass; @@ -109,13 +121,20 @@ public Parameter(String name, JClass jClass) { public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, GeneratedClassHolder holder) { + DeclaredType generatedClass = (DeclaredType) holder.getAnnotatedElement().asType(); + Types typeUtils = holder.processingEnvironment().getTypeUtils(); + ExecutableType generalisedElement = (ExecutableType) typeUtils.asMemberOf(generatedClass, executableElement); + String methodName = executableElement.getSimpleName().toString(); - JClass returnType = typeMirrorToJClass(executableElement.getReturnType(), holder); + JClass returnType = typeMirrorToJClass(generalisedElement.getReturnType(), holder); List parameters = new ArrayList(); - for (VariableElement parameter : executableElement.getParameters()) { - String parameterName = parameter.getSimpleName().toString(); - JClass parameterClass = typeMirrorToJClass(parameter.asType(), holder); + for (int i = 0; i < executableElement.getParameters().size(); i++) { + VariableElement parameter = executableElement.getParameters().get(i); + TypeMirror parameterType = generalisedElement.getParameterTypes().get(i); + + String parameterName = parameter.getSimpleName().toString(); + JClass parameterClass = typeMirrorToJClass(parameterType, holder); parameters.add(new Parameter(parameterName, parameterClass)); } @@ -128,11 +147,9 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene JMethod method = holder.getGeneratedClass().method(JMod.PUBLIC, returnType, methodName); method.annotate(Override.class); - for (VariableElement parameter : executableElement.getParameters()) { - String parameterName = parameter.getSimpleName().toString(); - JClass parameterClass = typeMirrorToJClass(parameter.asType(), holder); - method.param(JMod.FINAL, parameterClass, parameterName); - } + for (Parameter parameter : parameters) { + method.param(JMod.FINAL, parameter.jClass, parameter.name); + } for (TypeMirror superThrownType : executableElement.getThrownTypes()) { JClass thrownType = typeMirrorToJClass(superThrownType, holder); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java index 91880f0b29..1b0c6153b3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java @@ -20,6 +20,9 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import org.androidannotations.helper.ModelConstants; import org.androidannotations.process.ProcessHolder; @@ -43,9 +46,13 @@ public BaseGeneratedClassHolder(ProcessHolder processHolder, TypeElement annotat protected void setGeneratedClass() throws Exception { String annotatedComponentQualifiedName = annotatedElement.getQualifiedName().toString(); - String subComponentQualifiedName = annotatedComponentQualifiedName + ModelConstants.GENERATION_SUFFIX; - JClass annotatedComponent = codeModel().directClass(annotatedComponentQualifiedName); + String subComponentQualifiedName = annotatedComponentQualifiedName + ModelConstants.GENERATION_SUFFIX; + JClass annotatedComponent = codeModel().directClass(annotatedElement.asType().toString()); + generatedClass = codeModel()._class(PUBLIC | FINAL, subComponentQualifiedName, ClassType.CLASS); + for (TypeParameterElement typeParam : annotatedElement.getTypeParameters()) { + generatedClass.generify(typeParam.getSimpleName().toString()); + } generatedClass._extends(annotatedComponent); } diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/EBeanTest.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/EBeanTest.java index 3a95fcc88c..3a175116b1 100644 --- a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/EBeanTest.java +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/EBeanTest.java @@ -30,7 +30,11 @@ public void setup() { @Test public void activity_subclass_in_manifest_compiles() { - assertCompilationSuccessful(compileFiles(SomeActivity.class, SomeImplementation.class)); - } + assertCompilationSuccessful(compileFiles( + SomeActivity.class, + SomeImplementation.class, + SomeGenericBean.class, + SomeGenericBeanExt.class)); + } } diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeActivity.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeActivity.java index d00cf5ab3c..f0e8903e0c 100644 --- a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeActivity.java +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeActivity.java @@ -28,4 +28,7 @@ public class SomeActivity extends Activity { @Bean(SomeImplementation.class) SomeInterface someInterface; + @Bean + SomeGenericBean objectSomeGenericBean; + } diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java new file mode 100644 index 0000000000..da70cfcba3 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java @@ -0,0 +1,20 @@ +package org.androidannotations.ebean; + +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.EBean; + +import java.util.List; + +@EBean +public class SomeGenericBean { + + @Background + void someMethod(List list){ + + } + + void someOtherMethod(List list){ + + } + +} diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBeanExt.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBeanExt.java new file mode 100644 index 0000000000..34288ff656 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBeanExt.java @@ -0,0 +1,7 @@ +package org.androidannotations.ebean; + +import org.androidannotations.annotations.EBean; + +@EBean +public class SomeGenericBeanExt extends SomeGenericBean { +} From ac5ada61f5eef445de9e4e538e0c04adda0ed679 Mon Sep 17 00:00:00 2001 From: Drozdov Artyom Date: Thu, 9 Jan 2014 18:32:57 +0400 Subject: [PATCH 2/6] add: some extra tests --- .../androidannotations/ebean/SomeGenericBean.java | 5 +++++ .../test15/ActivityWithGenerics.java | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java index da70cfcba3..50a3ccde48 100644 --- a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java @@ -17,4 +17,9 @@ void someOtherMethod(List list){ } + @Background + void someParameterizedMetnod(List lst, List lst2) { + + } + } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java index dde57ea81b..ab8febf576 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java @@ -26,10 +26,14 @@ @EActivity public class ActivityWithGenerics extends Activity { - // @UiThread - // > void emptyUiMethod(T param, S param2) { - // // Not possible due to Codemodel's constraints - // } + // @UiThread + // > void emptyUiMethod(T param, S param2) { + // // Not possible due to Codemodel's constraints + // } + + @UiThread + void emptyUiMethod(List param, List param2) { + } @UiThread void emptyUiMethod(T param) { From e7de0657d90443b172c5052063a0a7186f375740 Mon Sep 17 00:00:00 2001 From: Drozdov Artyom Date: Thu, 9 Jan 2014 20:41:26 +0400 Subject: [PATCH 3/6] fix: integrated with method generics params --- .../helper/APTCodeModelHelper.java | 30 +++++++------------ .../ebean/SomeGenericBean.java | 9 ++++-- .../test15/ActivityWithGenerics.java | 7 ++--- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java index e974376cd5..6cfad032f9 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -23,13 +23,13 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Types; @@ -123,15 +123,15 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene DeclaredType generatedClass = (DeclaredType) holder.getAnnotatedElement().asType(); Types typeUtils = holder.processingEnvironment().getTypeUtils(); - ExecutableType generalisedElement = (ExecutableType) typeUtils.asMemberOf(generatedClass, executableElement); + ExecutableType executableType = (ExecutableType) typeUtils.asMemberOf(generatedClass, executableElement); String methodName = executableElement.getSimpleName().toString(); - JClass returnType = typeMirrorToJClass(generalisedElement.getReturnType(), holder); + JClass returnType = typeMirrorToJClass(executableType.getReturnType(), holder); List parameters = new ArrayList(); for (int i = 0; i < executableElement.getParameters().size(); i++) { VariableElement parameter = executableElement.getParameters().get(i); - TypeMirror parameterType = generalisedElement.getParameterTypes().get(i); + TypeMirror parameterType = executableType.getParameterTypes().get(i); String parameterName = parameter.getSimpleName().toString(); JClass parameterClass = typeMirrorToJClass(parameterType, holder); @@ -147,6 +147,13 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene JMethod method = holder.getGeneratedClass().method(JMod.PUBLIC, returnType, methodName); method.annotate(Override.class); + System.out.println(executableType.getTypeVariables()); + for (TypeVariable typeParameter : executableType.getTypeVariables()) { + TypeMirror bound = typeParameter.getUpperBound(); + JClass jClassBounds = typeMirrorToJClass(bound, holder); + method.generify(typeParameter.toString(), jClassBounds); + } + for (Parameter parameter : parameters) { method.param(JMod.FINAL, parameter.jClass, parameter.name); } @@ -156,21 +163,6 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene method._throws(thrownType); } - for (TypeParameterElement typeParameter : executableElement.getTypeParameters()) { - List bounds = typeParameter.getBounds(); - - JClass jClassBounds; - if (bounds.isEmpty()) { - jClassBounds = holder.classes().OBJECT; - } else { - // Currently Codemodel can't generate generics with multiple - // classes like this . - // So we only take the first class - jClassBounds = typeMirrorToJClass(bounds.get(0), holder); - } - method.generify(typeParameter.toString(), jClassBounds); - } - callSuperMethod(method, holder, method.body()); return method; diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java index 50a3ccde48..e4ba9a0f86 100644 --- a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/ebean/SomeGenericBean.java @@ -2,6 +2,7 @@ import org.androidannotations.annotations.Background; import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.UiThread; import java.util.List; @@ -10,16 +11,18 @@ public class SomeGenericBean { @Background void someMethod(List list){ - } void someOtherMethod(List list){ - } @Background - void someParameterizedMetnod(List lst, List lst2) { + void someParameterizedMethod(List lst, List lst2) { + } + @UiThread + void emptyUiMethod(List param, List param2) { } + } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java index ab8febf576..01d67ec572 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ActivityWithGenerics.java @@ -26,10 +26,9 @@ @EActivity public class ActivityWithGenerics extends Activity { - // @UiThread - // > void emptyUiMethod(T param, S param2) { - // // Not possible due to Codemodel's constraints - // } + //@UiThread + //> void emptyUiMethod(T param, S param2) { + //} @UiThread void emptyUiMethod(List param, List param2) { From dfa270dac627630819c74a3092b8dc33b43f5161 Mon Sep 17 00:00:00 2001 From: Drozdov Artyom Date: Thu, 9 Jan 2014 20:55:41 +0400 Subject: [PATCH 4/6] fix: removed debug info fix: allowing wildcard --- .../java/org/androidannotations/helper/APTCodeModelHelper.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java index 6cfad032f9..d351cbea64 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -82,7 +82,7 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { if (bound == null) { bound = wildcardType.getSuperBound(); if (bound == null) { - throw new IllegalArgumentException("There are should be 'extends' or 'super' in the wildcard"); + return holder.classes().OBJECT.wildcard(); } return superWildcard(typeMirrorToJClass(bound, holder)); } @@ -147,7 +147,6 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene JMethod method = holder.getGeneratedClass().method(JMod.PUBLIC, returnType, methodName); method.annotate(Override.class); - System.out.println(executableType.getTypeVariables()); for (TypeVariable typeParameter : executableType.getTypeVariables()) { TypeMirror bound = typeParameter.getUpperBound(); JClass jClassBounds = typeMirrorToJClass(bound, holder); From 993a6801a391c03fee6e1b5f3bddb4acd3b90579 Mon Sep 17 00:00:00 2001 From: Drozdov Artyom Date: Sat, 11 Jan 2014 13:23:12 +0400 Subject: [PATCH 5/6] fix: work around for buggy eclipse JDT-APT implementation of Types.asMemberOf --- .../helper/APTCodeModelHelper.java | 79 +++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java index d351cbea64..708bf19168 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -18,18 +18,21 @@ import java.io.StringWriter; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Types; @@ -56,6 +59,10 @@ public class APTCodeModelHelper { public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { + return typeMirrorToJClass(type, holder, Collections.emptyMap()); + } + + public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder, Map substitute) { if (type instanceof DeclaredType) { DeclaredType declaredType = (DeclaredType) type; @@ -68,7 +75,7 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { List typeArgumentJClasses = new ArrayList(); for (TypeMirror typeArgument : typeArguments) { - typeArgumentJClasses.add(typeMirrorToJClass(typeArgument, holder)); + typeArgumentJClasses.add(typeMirrorToJClass(typeArgument, holder, substitute)); } if (typeArgumentJClasses.size() > 0) { declaredClass = declaredClass.narrow(typeArgumentJClasses); @@ -84,7 +91,7 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { if (bound == null) { return holder.classes().OBJECT.wildcard(); } - return superWildcard(typeMirrorToJClass(bound, holder)); + return superWildcard(typeMirrorToJClass(bound, holder, substitute)); } TypeMirror extendsBound = wildcardType.getExtendsBound(); @@ -92,15 +99,19 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { if (extendsBound == null) { return holder.classes().OBJECT.wildcard(); } else { - return typeMirrorToJClass(extendsBound, holder).wildcard(); + return typeMirrorToJClass(extendsBound, holder, substitute).wildcard(); } } else if (type instanceof ArrayType) { ArrayType arrayType = (ArrayType) type; - JClass refClass = typeMirrorToJClass(arrayType.getComponentType(), holder); + JClass refClass = typeMirrorToJClass(arrayType.getComponentType(), holder, substitute); return refClass.array(); } else { + TypeMirror substituted = substitute.get(type.toString()); + if (substituted != null) { + return typeMirrorToJClass(substituted, holder, substitute); + } return holder.refClass(type.toString()); } } @@ -119,22 +130,60 @@ public Parameter(String name, JClass jClass) { } } + private Map getActualTypes(Types typeUtils, DeclaredType baseClass, TypeMirror actualClass) { + List superTypes = new ArrayList(); + superTypes.add(actualClass); + while(!superTypes.isEmpty()) { + TypeMirror x = superTypes.remove(0); + if (typeUtils.isSameType(typeUtils.erasure(x), typeUtils.erasure(baseClass))) { + DeclaredType type = (DeclaredType) x; + Map actualTypes = new HashMap(); + for (int i = 0; i < type.getTypeArguments().size(); i++) { + TypeMirror actualArg = type.getTypeArguments().get(i); + TypeMirror formalArg = baseClass.getTypeArguments().get(i); + if (!typeUtils.isSameType(actualArg, formalArg)) { + actualTypes.put(formalArg.toString(), actualArg); + } + } + return actualTypes; + } + superTypes.addAll(typeUtils.directSupertypes(x)); + } + return Collections.emptyMap(); + } + public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, GeneratedClassHolder holder) { + TypeMirror generatedClass = holder.getAnnotatedElement().asType(); + DeclaredType originalClass = (DeclaredType) executableElement.getEnclosingElement().asType(); - DeclaredType generatedClass = (DeclaredType) holder.getAnnotatedElement().asType(); Types typeUtils = holder.processingEnvironment().getTypeUtils(); - ExecutableType executableType = (ExecutableType) typeUtils.asMemberOf(generatedClass, executableElement); + + Map actualTypes = getActualTypes(typeUtils, originalClass, generatedClass); + Map methodTypes = new LinkedHashMap(); + + for (TypeParameterElement typeParameter : executableElement.getTypeParameters()) { + List bounds = typeParameter.getBounds(); + JClass jClassBounds; + if (bounds.isEmpty()) { + jClassBounds = holder.classes().OBJECT; + } else { + //TODO resolve bounds + jClassBounds = typeMirrorToJClass(bounds.get(0), holder, actualTypes); + } + methodTypes.put(typeParameter.toString(), jClassBounds); + } + + actualTypes.keySet().removeAll(methodTypes.keySet()); String methodName = executableElement.getSimpleName().toString(); - JClass returnType = typeMirrorToJClass(executableType.getReturnType(), holder); + JClass returnType = typeMirrorToJClass(executableElement.getReturnType(), holder, actualTypes); List parameters = new ArrayList(); for (int i = 0; i < executableElement.getParameters().size(); i++) { VariableElement parameter = executableElement.getParameters().get(i); - TypeMirror parameterType = executableType.getParameterTypes().get(i); String parameterName = parameter.getSimpleName().toString(); - JClass parameterClass = typeMirrorToJClass(parameterType, holder); + JClass parameterClass = typeMirrorToJClass(parameter.asType(), holder, actualTypes); parameters.add(new Parameter(parameterName, parameterClass)); } @@ -147,10 +196,8 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene JMethod method = holder.getGeneratedClass().method(JMod.PUBLIC, returnType, methodName); method.annotate(Override.class); - for (TypeVariable typeParameter : executableType.getTypeVariables()) { - TypeMirror bound = typeParameter.getUpperBound(); - JClass jClassBounds = typeMirrorToJClass(bound, holder); - method.generify(typeParameter.toString(), jClassBounds); + for (Map.Entry typeDeclaration : methodTypes.entrySet()) { + method.generify(typeDeclaration.getKey(), typeDeclaration.getValue()); } for (Parameter parameter : parameters) { @@ -158,7 +205,7 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, Gene } for (TypeMirror superThrownType : executableElement.getThrownTypes()) { - JClass thrownType = typeMirrorToJClass(superThrownType, holder); + JClass thrownType = typeMirrorToJClass(superThrownType, holder, actualTypes); method._throws(thrownType); } From eb80ac30d1870331bd9b4285511c0c4f5210737f Mon Sep 17 00:00:00 2001 From: Drozdov Artyom Date: Sat, 11 Jan 2014 23:26:38 +0400 Subject: [PATCH 6/6] fix: case for parametrized bean with type bounds, added tests for such cases --- .../helper/APTCodeModelHelper.java | 35 +++++++++++-------- .../holder/BaseGeneratedClassHolder.java | 8 ++++- .../EComponentWithViewSupportHolder.java | 3 -- .../holder/EIntentServiceHolder.java | 3 -- .../androidannotations/holder/RestHolder.java | 4 --- .../test15/ebean/SubtypedGenericBean.java | 29 +++++++++++++++ .../test15/ebean/SubtypedGenericBeanExt.java | 9 +++++ .../test15/ebean/SubtypedGenericBeanExt2.java | 9 +++++ .../ebean/SubtypedGenericBeanExtExt.java | 9 +++++ 9 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBean.java create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt.java create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt2.java create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExtExt.java diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java index 708bf19168..a120b4f023 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -62,7 +62,7 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder) { return typeMirrorToJClass(type, holder, Collections.emptyMap()); } - public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder, Map substitute) { + private JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder, Map substitute) { if (type instanceof DeclaredType) { DeclaredType declaredType = (DeclaredType) type; @@ -109,7 +109,7 @@ public JClass typeMirrorToJClass(TypeMirror type, GeneratedClassHolder holder, M return refClass.array(); } else { TypeMirror substituted = substitute.get(type.toString()); - if (substituted != null) { + if (substituted != null && type != substituted) { return typeMirrorToJClass(substituted, holder, substitute); } return holder.refClass(type.toString()); @@ -130,9 +130,9 @@ public Parameter(String name, JClass jClass) { } } - private Map getActualTypes(Types typeUtils, DeclaredType baseClass, TypeMirror actualClass) { + private Map getActualTypes(Types typeUtils, DeclaredType baseClass, TypeMirror annotatedClass) { List superTypes = new ArrayList(); - superTypes.add(actualClass); + superTypes.add(annotatedClass); while(!superTypes.isEmpty()) { TypeMirror x = superTypes.remove(0); if (typeUtils.isSameType(typeUtils.erasure(x), typeUtils.erasure(baseClass))) { @@ -152,24 +152,31 @@ private Map getActualTypes(Types typeUtils, DeclaredType bas return Collections.emptyMap(); } + public JClass typeBoundsToJClass(GeneratedClassHolder holder, List bounds) { + return typeBoundsToJClass(holder, bounds, Collections.emptyMap()); + } + + private JClass typeBoundsToJClass(GeneratedClassHolder holder, List bounds, Map actualTypes) { + if (bounds.isEmpty()) { + return holder.classes().OBJECT; + } else { + //TODO resolve bounds + return typeMirrorToJClass(bounds.get(0), holder, actualTypes); + } + } + public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, GeneratedClassHolder holder) { - TypeMirror generatedClass = holder.getAnnotatedElement().asType(); - DeclaredType originalClass = (DeclaredType) executableElement.getEnclosingElement().asType(); + TypeMirror annotatedClass = holder.getAnnotatedElement().asType(); + DeclaredType baseClass = (DeclaredType) executableElement.getEnclosingElement().asType(); Types typeUtils = holder.processingEnvironment().getTypeUtils(); - Map actualTypes = getActualTypes(typeUtils, originalClass, generatedClass); + Map actualTypes = getActualTypes(typeUtils, baseClass, annotatedClass); Map methodTypes = new LinkedHashMap(); for (TypeParameterElement typeParameter : executableElement.getTypeParameters()) { List bounds = typeParameter.getBounds(); - JClass jClassBounds; - if (bounds.isEmpty()) { - jClassBounds = holder.classes().OBJECT; - } else { - //TODO resolve bounds - jClassBounds = typeMirrorToJClass(bounds.get(0), holder, actualTypes); - } + JClass jClassBounds = typeBoundsToJClass(holder, bounds, actualTypes); methodTypes.put(typeParameter.toString(), jClassBounds); } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java index 1b0c6153b3..af9fc6e21e 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/BaseGeneratedClassHolder.java @@ -24,7 +24,10 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; +import org.androidannotations.helper.APTCodeModelHelper; import org.androidannotations.helper.ModelConstants; +import org.androidannotations.logger.Logger; +import org.androidannotations.logger.LoggerFactory; import org.androidannotations.process.ProcessHolder; import com.sun.codemodel.ClassType; @@ -37,10 +40,12 @@ public abstract class BaseGeneratedClassHolder implements GeneratedClassHolder { protected final ProcessHolder processHolder; protected JDefinedClass generatedClass; protected final TypeElement annotatedElement; + protected final APTCodeModelHelper codeModelHelper; public BaseGeneratedClassHolder(ProcessHolder processHolder, TypeElement annotatedElement) throws Exception { this.processHolder = processHolder; this.annotatedElement = annotatedElement; + this.codeModelHelper = new APTCodeModelHelper(); setGeneratedClass(); } @@ -51,7 +56,8 @@ protected void setGeneratedClass() throws Exception { generatedClass = codeModel()._class(PUBLIC | FINAL, subComponentQualifiedName, ClassType.CLASS); for (TypeParameterElement typeParam : annotatedElement.getTypeParameters()) { - generatedClass.generify(typeParam.getSimpleName().toString()); + JClass bound = codeModelHelper.typeBoundsToJClass(this, typeParam.getBounds()); + generatedClass.generify(typeParam.getSimpleName().toString(), bound); } generatedClass._extends(annotatedComponent); } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EComponentWithViewSupportHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EComponentWithViewSupportHolder.java index 55b84f58d8..333d77af29 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EComponentWithViewSupportHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EComponentWithViewSupportHolder.java @@ -32,7 +32,6 @@ import org.androidannotations.api.view.HasViews; import org.androidannotations.api.view.OnViewChangedListener; import org.androidannotations.api.view.OnViewChangedNotifier; -import org.androidannotations.helper.APTCodeModelHelper; import org.androidannotations.helper.ViewNotifierHelper; import org.androidannotations.process.ProcessHolder; @@ -47,7 +46,6 @@ public abstract class EComponentWithViewSupportHolder extends EComponentHolder { - private APTCodeModelHelper codeModelHelper; protected ViewNotifierHelper viewNotifierHelper; private JBlock onViewChangedBody; private JVar onViewChangedHasViewsParam; @@ -60,7 +58,6 @@ public abstract class EComponentWithViewSupportHolder extends EComponentHolder { public EComponentWithViewSupportHolder(ProcessHolder processHolder, TypeElement annotatedElement) throws Exception { super(processHolder, annotatedElement); - codeModelHelper = new APTCodeModelHelper(); viewNotifierHelper = new ViewNotifierHelper(this); } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EIntentServiceHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EIntentServiceHolder.java index 488f291129..e0bd9970c2 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EIntentServiceHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EIntentServiceHolder.java @@ -19,7 +19,6 @@ import javax.lang.model.element.TypeElement; -import org.androidannotations.helper.APTCodeModelHelper; import org.androidannotations.helper.AndroidManifest; import org.androidannotations.process.ProcessHolder; @@ -31,8 +30,6 @@ public class EIntentServiceHolder extends EServiceHolder { - private APTCodeModelHelper codeModelHelper = new APTCodeModelHelper(); - private JVar onHandleIntentIntent; private JMethod onHandleIntentMethod; private JBlock onHandleIntentBody; diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/RestHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/RestHolder.java index e124153654..0d357eb880 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/RestHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/RestHolder.java @@ -29,7 +29,6 @@ import javax.lang.model.type.TypeKind; import org.androidannotations.api.rest.RestErrorHandler; -import org.androidannotations.helper.APTCodeModelHelper; import org.androidannotations.helper.CanonicalNameConstants; import org.androidannotations.helper.ModelConstants; import org.androidannotations.process.ProcessHolder; @@ -44,8 +43,6 @@ public class RestHolder extends BaseGeneratedClassHolder { - private APTCodeModelHelper codeModelHelper; - private JMethod init; private JFieldVar rootUrlField; private JFieldVar restTemplateField; @@ -56,7 +53,6 @@ public class RestHolder extends BaseGeneratedClassHolder { public RestHolder(ProcessHolder processHolder, TypeElement annotatedElement) throws Exception { super(processHolder, annotatedElement); - codeModelHelper = new APTCodeModelHelper(); implementMethods(); } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBean.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBean.java new file mode 100644 index 0000000000..8a122f77bd --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBean.java @@ -0,0 +1,29 @@ +package org.androidannotations.test15.ebean; + +import com.squareup.otto.Produce; + +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.UiThread; + +import java.util.List; + +@EBean +public class SubtypedGenericBean, T extends Number> { + + @Background + void backgroundMethod(T param, S param2) { + + } + + @Produce + public T genericMethod() { + return null; + } + + @UiThread + > void uiMethod(S method) { + + } + +} \ No newline at end of file diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt.java new file mode 100644 index 0000000000..c82fdb8e07 --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt.java @@ -0,0 +1,9 @@ +package org.androidannotations.test15.ebean; + +import org.androidannotations.annotations.EBean; + +import java.util.List; + +@EBean +public class SubtypedGenericBeanExt> extends SubtypedGenericBean { +} diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt2.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt2.java new file mode 100644 index 0000000000..c3d477c9c3 --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExt2.java @@ -0,0 +1,9 @@ +package org.androidannotations.test15.ebean; + +import org.androidannotations.annotations.EBean; + +import java.util.List; + +@EBean +public class SubtypedGenericBeanExt2 extends SubtypedGenericBean, Double> { +} diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExtExt.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExtExt.java new file mode 100644 index 0000000000..5ded2123aa --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ebean/SubtypedGenericBeanExtExt.java @@ -0,0 +1,9 @@ +package org.androidannotations.test15.ebean; + +import org.androidannotations.annotations.EBean; + +import java.util.ArrayList; + +@EBean +public class SubtypedGenericBeanExtExt extends SubtypedGenericBeanExt> { +}