Skip to content

Commit 9c44975

Browse files
committed
Merge pull request androidannotations#377 from excilys/206_converters
RestTemplate converters set in @rest. Fixes androidannotations#206.
2 parents ed4239b + 4fe2eea commit 9c44975

File tree

23 files changed

+335
-74
lines changed

23 files changed

+335
-74
lines changed

AndroidAnnotations/androidannotations-api/src/main/java/com/googlecode/androidannotations/annotations/rest/Rest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
@Retention(RetentionPolicy.CLASS)
2424
@Target(ElementType.TYPE)
2525
public @interface Rest {
26-
String value() default "";
26+
String rootUrl() default "";
27+
Class<?>[] converters();
2728
}

AndroidAnnotations/androidannotations/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
<version>1.6_r2</version>
3737
<scope>test</scope>
3838
</dependency>
39+
<dependency>
40+
<!-- spring-android-rest-template is required to use the Rest API -->
41+
<groupId>org.springframework.android</groupId>
42+
<artifactId>spring-android-rest-template</artifactId>
43+
<version>1.0.0.RELEASE</version>
44+
<scope>test</scope>
45+
</dependency>
3946
</dependencies>
4047

4148
<build>

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/AndroidAnnotationProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ private ModelProcessor buildModelProcessor(IRClass rClass, AndroidSystemServices
539539
modelProcessor.register(new FragmentArgProcessor(processingEnv));
540540
modelProcessor.register(new SystemServiceProcessor(androidSystemServices));
541541
RestImplementationsHolder restImplementationHolder = new RestImplementationsHolder();
542-
modelProcessor.register(new RestProcessor(restImplementationHolder));
542+
modelProcessor.register(new RestProcessor(processingEnv, restImplementationHolder));
543543
modelProcessor.register(new GetProcessor(processingEnv, restImplementationHolder));
544544
modelProcessor.register(new PostProcessor(processingEnv, restImplementationHolder));
545545
modelProcessor.register(new PutProcessor(processingEnv, restImplementationHolder));

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/helper/AnnotationHelper.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ public AnnotationHelper(ProcessingEnvironment processingEnv) {
5858
* be a subtype of itself.
5959
*/
6060
public boolean isSubtype(TypeMirror potentialSubtype, TypeMirror potentialSupertype) {
61-
6261
return processingEnv.getTypeUtils().isSubtype(potentialSubtype, potentialSupertype);
6362
}
6463

@@ -115,6 +114,10 @@ public boolean isPrivate(Element element) {
115114
return element.getModifiers().contains(Modifier.PRIVATE);
116115
}
117116

117+
public boolean isPublic(Element element) {
118+
return element.getModifiers().contains(Modifier.PUBLIC);
119+
}
120+
118121
public boolean isAbstract(Element element) {
119122
return element.getModifiers().contains(Modifier.ABSTRACT);
120123
}
@@ -308,17 +311,45 @@ public String actionName(Class<? extends Annotation> target) {
308311
return target.getSimpleName() + "ed";
309312
}
310313

311-
public DeclaredType extractAnnotationClassParameter(Element element, Class<? extends Annotation> target) {
314+
public List<DeclaredType> extractAnnotationClassArrayParameter(Element element, Class<? extends Annotation> target, String methodName) {
315+
AnnotationMirror annotationMirror = findAnnotationMirror(element, target);
316+
317+
Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();
318+
319+
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) {
320+
/*
321+
* "methodName" is unset when the default value is used
322+
*/
323+
if (methodName.equals(entry.getKey().getSimpleName().toString())) {
324+
325+
AnnotationValue annotationValue = entry.getValue();
312326

327+
@SuppressWarnings("unchecked")
328+
List<AnnotationValue> annotationClassArray = (List<AnnotationValue>) annotationValue.getValue();
329+
330+
List<DeclaredType> result = new ArrayList<DeclaredType>(annotationClassArray.size());
331+
332+
for (AnnotationValue annotationClassValue : annotationClassArray) {
333+
result.add((DeclaredType) annotationClassValue.getValue());
334+
}
335+
336+
return result;
337+
}
338+
}
339+
340+
return null;
341+
}
342+
343+
public DeclaredType extractAnnotationClassParameter(Element element, Class<? extends Annotation> target, String methodName) {
313344
AnnotationMirror annotationMirror = findAnnotationMirror(element, target);
314345

315346
Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();
316347

317348
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) {
318349
/*
319-
* "value" is unset when the default value is used
350+
* "methodName" is unset when the default value is used
320351
*/
321-
if ("value".equals(entry.getKey().getSimpleName().toString())) {
352+
if (methodName.equals(entry.getKey().getSimpleName().toString())) {
322353

323354
AnnotationValue annotationValue = entry.getValue();
324355

@@ -331,4 +362,8 @@ public DeclaredType extractAnnotationClassParameter(Element element, Class<? ext
331362
return null;
332363
}
333364

365+
public DeclaredType extractAnnotationClassParameter(Element element, Class<? extends Annotation> target) {
366+
return extractAnnotationClassParameter(element, target, "value");
367+
}
368+
334369
}

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/helper/CanonicalNameConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public final class CanonicalNameConstants {
9999
public static final String HTTP_METHOD = "org.springframework.http.HttpMethod";
100100
public static final String HTTP_ENTITY = "org.springframework.http.HttpEntity";
101101
public static final String REST_TEMPLATE = "org.springframework.web.client.RestTemplate";
102+
public static final String HTTP_MESSAGE_CONVERTER = "org.springframework.http.converter.HttpMessageConverter";
102103

103104
/*
104105
* RoboGuice

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/helper/ValidatorHelper.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_INFO;
2121
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_VERBOSE;
2222
import static com.googlecode.androidannotations.helper.AndroidConstants.LOG_WARN;
23+
import static com.googlecode.androidannotations.helper.CanonicalNameConstants.HTTP_MESSAGE_CONVERTER;
2324
import static com.googlecode.androidannotations.helper.ModelConstants.GENERATION_SUFFIX;
2425
import static java.util.Arrays.asList;
2526

@@ -860,7 +861,7 @@ public void hasCorrectDefaultAnnotation(ExecutableElement method) {
860861
checkDefaultAnnotation(method, DefaultString.class, "String", new DefaultAnnotationCondition() {
861862
@Override
862863
public boolean correctReturnType(TypeMirror returnType) {
863-
return returnType.toString().equals("java.lang.String");
864+
return returnType.toString().equals(CanonicalNameConstants.STRING);
864865
}
865866
});
866867
}
@@ -1123,4 +1124,37 @@ public void componentRegistered(Element element, AndroidManifest androidManifest
11231124

11241125
}
11251126

1127+
public void validateConverters(Element element, IsValid valid) {
1128+
TypeMirror httpMessageConverterType = annotationHelper.typeElementFromQualifiedName(HTTP_MESSAGE_CONVERTER).asType();
1129+
TypeMirror httpMessageConverterTypeErased = annotationHelper.getTypeUtils().erasure(httpMessageConverterType);
1130+
List<DeclaredType> converters = annotationHelper.extractAnnotationClassArrayParameter(element, annotationHelper.getTarget(), "converters");
1131+
for (DeclaredType converterType : converters) {
1132+
TypeMirror erasedConverterType = annotationHelper.getTypeUtils().erasure(converterType);
1133+
if (annotationHelper.isSubtype(erasedConverterType, httpMessageConverterTypeErased)) {
1134+
Element converterElement = converterType.asElement();
1135+
if (converterElement.getKind().isClass()) {
1136+
if (!annotationHelper.isAbstract(converterElement)) {
1137+
List<ExecutableElement> constructors = ElementFilter.constructorsIn(converterElement.getEnclosedElements());
1138+
for (ExecutableElement constructor : constructors) {
1139+
if (annotationHelper.isPublic(constructor) && constructor.getParameters().isEmpty()) {
1140+
return;
1141+
}
1142+
}
1143+
valid.invalidate();
1144+
annotationHelper.printAnnotationError(element, "The converter class must have a public no argument constructor");
1145+
} else {
1146+
valid.invalidate();
1147+
annotationHelper.printAnnotationError(element, "The converter class must not be abstract");
1148+
}
1149+
} else {
1150+
valid.invalidate();
1151+
annotationHelper.printAnnotationError(element, "The converter class must be a class");
1152+
}
1153+
} else {
1154+
valid.invalidate();
1155+
annotationHelper.printAnnotationError(element, "The converter class must be a subtype of " + HTTP_MESSAGE_CONVERTER);
1156+
}
1157+
}
1158+
1159+
}
11261160
}

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/processing/EBeansHolder.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ public class Classes {
149149
public EBeansHolder(JCodeModel codeModel) {
150150
this.codeModel = codeModel;
151151
classes = new Classes();
152+
refClass(CanonicalNameConstants.STRING);
153+
preloadJavaLangClasses();
154+
}
155+
156+
private void preloadJavaLangClasses() {
157+
loadedClasses.put(String.class.getName(), refClass(String.class));
158+
loadedClasses.put(Object.class.getName(), refClass(Object.class));
152159
}
153160

154161
public EBeanHolder create(Element element, Class<? extends Annotation> eBeanAnnotation, JDefinedClass generatedClass) {

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/processing/rest/RestProcessor.java

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,45 @@
1515
*/
1616
package com.googlecode.androidannotations.processing.rest;
1717

18+
import static com.googlecode.androidannotations.helper.CanonicalNameConstants.REST_TEMPLATE;
19+
import static com.googlecode.androidannotations.helper.CanonicalNameConstants.STRING;
20+
import static com.sun.codemodel.JExpr._new;
1821
import static com.sun.codemodel.JExpr._this;
22+
import static com.sun.codemodel.JExpr.invoke;
23+
import static com.sun.codemodel.JExpr.lit;
1924

2025
import java.lang.annotation.Annotation;
2126
import java.util.List;
2227

28+
import javax.annotation.processing.ProcessingEnvironment;
2329
import javax.lang.model.element.Element;
2430
import javax.lang.model.element.ExecutableElement;
2531
import javax.lang.model.element.TypeElement;
2632
import javax.lang.model.element.VariableElement;
33+
import javax.lang.model.type.DeclaredType;
2734
import javax.lang.model.type.TypeKind;
2835
import javax.lang.model.util.ElementFilter;
2936

3037
import com.googlecode.androidannotations.annotations.rest.Rest;
38+
import com.googlecode.androidannotations.helper.AnnotationHelper;
3139
import com.googlecode.androidannotations.helper.ModelConstants;
3240
import com.googlecode.androidannotations.processing.EBeansHolder;
3341
import com.googlecode.androidannotations.processing.GeneratingElementProcessor;
3442
import com.sun.codemodel.ClassType;
43+
import com.sun.codemodel.JBlock;
3544
import com.sun.codemodel.JClass;
3645
import com.sun.codemodel.JCodeModel;
37-
import com.sun.codemodel.JExpr;
3846
import com.sun.codemodel.JMethod;
3947
import com.sun.codemodel.JMod;
4048
import com.sun.codemodel.JVar;
4149

4250
public class RestProcessor implements GeneratingElementProcessor {
4351

44-
private static final String SPRING_REST_TEMPLATE_QUALIFIED_NAME = "org.springframework.web.client.RestTemplate";
45-
private static final String JAVA_STRING_QUALIFIED_NAME = "java.lang.String";
4652
private final RestImplementationsHolder restImplementationHolder;
53+
private AnnotationHelper annotationHelper;
4754

48-
public RestProcessor(RestImplementationsHolder restImplementationHolder) {
55+
public RestProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) {
56+
annotationHelper = new AnnotationHelper(processingEnv);
4957
this.restImplementationHolder = restImplementationHolder;
5058
}
5159

@@ -71,23 +79,35 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo
7179
holder.restImplementationClass._implements(interfaceClass);
7280

7381
// RestTemplate field
74-
JClass restTemplateClass = eBeansHolder.refClass(SPRING_REST_TEMPLATE_QUALIFIED_NAME);
82+
JClass restTemplateClass = eBeansHolder.refClass(REST_TEMPLATE);
7583
holder.restTemplateField = holder.restImplementationClass.field(JMod.PRIVATE, restTemplateClass, "restTemplate");
7684

7785
// RootUrl field
78-
JClass stringClass = eBeansHolder.refClass(JAVA_STRING_QUALIFIED_NAME);
86+
JClass stringClass = eBeansHolder.refClass(STRING);
7987
holder.rootUrlField = holder.restImplementationClass.field(JMod.PRIVATE, stringClass, "rootUrl");
8088

81-
// Default constructor
82-
JMethod defaultConstructor = holder.restImplementationClass.constructor(JMod.PUBLIC);
83-
defaultConstructor.body().assign(holder.restTemplateField, JExpr._new(restTemplateClass));
84-
defaultConstructor.body().assign(holder.rootUrlField, JExpr.lit(typeElement.getAnnotation(Rest.class).value()));
89+
{
90+
// Constructor
91+
JMethod constructor = holder.restImplementationClass.constructor(JMod.PUBLIC);
92+
JBlock constructorBody = constructor.body();
93+
constructorBody.assign(holder.restTemplateField, _new(restTemplateClass));
94+
95+
{
96+
// Converters
97+
List<DeclaredType> converters = annotationHelper.extractAnnotationClassArrayParameter(element, getTarget(), "converters");
98+
for (DeclaredType converterType : converters) {
99+
JClass converterClass = eBeansHolder.refClass(converterType.toString());
100+
constructorBody.add(invoke(holder.restTemplateField, "getMessageConverters").invoke("add").arg(_new(converterClass)));
101+
}
102+
}
103+
constructorBody.assign(holder.rootUrlField, lit(typeElement.getAnnotation(Rest.class).rootUrl()));
104+
}
85105

86106
// Implement getRestTemplate method
87107
List<? extends Element> enclosedElements = typeElement.getEnclosedElements();
88108
List<ExecutableElement> methods = ElementFilter.methodsIn(enclosedElements);
89109
for (ExecutableElement method : methods) {
90-
if (method.getParameters().size() == 0 && method.getReturnType().toString().equals(SPRING_REST_TEMPLATE_QUALIFIED_NAME)) {
110+
if (method.getParameters().size() == 0 && method.getReturnType().toString().equals(REST_TEMPLATE)) {
91111
String methodName = method.getSimpleName().toString();
92112
JMethod getRestTemplateMethod = holder.restImplementationClass.method(JMod.PUBLIC, restTemplateClass, methodName);
93113
getRestTemplateMethod.annotate(Override.class);
@@ -100,7 +120,7 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo
100120
List<? extends VariableElement> parameters = method.getParameters();
101121
if (parameters.size() == 1 && method.getReturnType().getKind() == TypeKind.VOID) {
102122
VariableElement firstParameter = parameters.get(0);
103-
if (firstParameter.asType().toString().equals(SPRING_REST_TEMPLATE_QUALIFIED_NAME)) {
123+
if (firstParameter.asType().toString().equals(REST_TEMPLATE)) {
104124
String methodName = method.getSimpleName().toString();
105125
JMethod setRestTemplateMethod = holder.restImplementationClass.method(JMod.PUBLIC, codeModel.VOID, methodName);
106126
setRestTemplateMethod.annotate(Override.class);
@@ -118,7 +138,7 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo
118138
List<? extends VariableElement> parameters = method.getParameters();
119139
if (parameters.size() == 1 && method.getReturnType().getKind() == TypeKind.VOID) {
120140
VariableElement firstParameter = parameters.get(0);
121-
if (firstParameter.asType().toString().equals(JAVA_STRING_QUALIFIED_NAME) && method.getSimpleName().toString().equals("setRootUrl")) {
141+
if (firstParameter.asType().toString().equals(STRING) && method.getSimpleName().toString().equals("setRootUrl")) {
122142
JMethod setRootUrlMethod = holder.restImplementationClass.method(JMod.PUBLIC, codeModel.VOID, method.getSimpleName().toString());
123143
setRootUrlMethod.annotate(Override.class);
124144

AndroidAnnotations/androidannotations/src/main/java/com/googlecode/androidannotations/validation/rest/RestValidator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public Class<? extends Annotation> getTarget() {
4444

4545
@Override
4646
public boolean validate(Element element, AnnotationElements validatedElements) {
47-
4847
IsValid valid = new IsValid();
4948

5049
TypeElement typeElement = (TypeElement) element;
@@ -61,6 +60,8 @@ public boolean validate(Element element, AnnotationElements validatedElements) {
6160

6261
validatorHelper.unannotatedMethodReturnsRestTemplate(typeElement, valid);
6362

63+
validatorHelper.validateConverters(element, valid);
64+
6465
return valid.isValid();
6566
}
6667

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.googlecode.androidannotations.rest;
2+
3+
import org.springframework.http.converter.AbstractHttpMessageConverter;
4+
5+
public abstract class AbstractConverter<T> extends AbstractHttpMessageConverter<T> {
6+
7+
}

0 commit comments

Comments
 (0)