diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java index ee2e3eee84..5982e7e25d 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java @@ -549,14 +549,14 @@ private ModelProcessor buildModelProcessor(IRClass rClass, AndroidSystemServices modelProcessor.register(new ExtraProcessor(processingEnv)); modelProcessor.register(new FragmentArgProcessor(processingEnv)); modelProcessor.register(new SystemServiceProcessor(androidSystemServices)); - RestImplementationsHolder restImplementationHolder = new RestImplementationsHolder(); - modelProcessor.register(new RestProcessor(processingEnv, restImplementationHolder)); - modelProcessor.register(new GetProcessor(processingEnv, restImplementationHolder)); - modelProcessor.register(new PostProcessor(processingEnv, restImplementationHolder)); - modelProcessor.register(new PutProcessor(processingEnv, restImplementationHolder)); - modelProcessor.register(new DeleteProcessor(processingEnv, restImplementationHolder)); - modelProcessor.register(new HeadProcessor(processingEnv, restImplementationHolder)); - modelProcessor.register(new OptionsProcessor(processingEnv, restImplementationHolder)); + RestImplementationsHolder restImplementationsHolder = new RestImplementationsHolder(); + modelProcessor.register(new RestProcessor(processingEnv, restImplementationsHolder)); + modelProcessor.register(new GetProcessor(processingEnv, restImplementationsHolder)); + modelProcessor.register(new PostProcessor(processingEnv, restImplementationsHolder)); + modelProcessor.register(new PutProcessor(processingEnv, restImplementationsHolder)); + modelProcessor.register(new DeleteProcessor(processingEnv, restImplementationsHolder)); + modelProcessor.register(new HeadProcessor(processingEnv, restImplementationsHolder)); + modelProcessor.register(new OptionsProcessor(processingEnv, restImplementationsHolder)); modelProcessor.register(new AppProcessor()); modelProcessor.register(new OptionsMenuProcessor(processingEnv, rClass)); modelProcessor.register(new OptionsItemProcessor(processingEnv, rClass)); 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 f8e79f5f50..83d9546267 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -34,9 +34,11 @@ import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.WildcardType; import org.androidannotations.processing.EBeanHolder; import org.androidannotations.processing.EBeansHolder.Classes; + import com.sun.codemodel.JBlock; import com.sun.codemodel.JCatchBlock; import com.sun.codemodel.JClass; @@ -78,6 +80,16 @@ public JClass typeMirrorToJClass(TypeMirror type, EBeanHolder 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 extendsBound = wildcardType.getExtendsBound(); + + return typeMirrorToJClass(extendsBound, holder).wildcard(); } else if (type instanceof ArrayType) { ArrayType arrayType = (ArrayType) type; diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java index df038444fb..fc4d8aa281 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java @@ -17,7 +17,10 @@ import java.net.URI; import java.sql.SQLException; +import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Set; public final class CanonicalNameConstants { @@ -25,8 +28,12 @@ public final class CanonicalNameConstants { /* * Java */ + public static final String OBJECT = Object.class.getCanonicalName(); public static final String URI = URI.class.getCanonicalName(); + public static final String MAP = Map.class.getCanonicalName(); public static final String SET = Set.class.getCanonicalName(); + public static final String LIST = List.class.getCanonicalName(); + public static final String COLLECTION = Collection.class.getCanonicalName(); public static final String COLLECTIONS = Collections.class.getCanonicalName(); public static final String STRING = String.class.getCanonicalName(); public static final String CHAR_SEQUENCE = CharSequence.class.getCanonicalName(); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/Option.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/Option.java index 9d06e14cc5..7f3573fc35 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/Option.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/Option.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * Licensed 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.androidannotations.helper; /** diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java index 62dbeb33a6..a80f3733ce 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java @@ -140,6 +140,10 @@ public JClass refClass(Class clazz) { return eBeansHolder.refClass(clazz); } + public JDefinedClass definedClass(String fullyQualifiedClassName) { + return eBeansHolder.definedClass(fullyQualifiedClassName); + } + public void generateApiClass(Element originatingElement, Class apiClass) { eBeansHolder.generateApiClass(originatingElement, apiClass); } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java index 090d001e82..afd39e6924 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java @@ -30,6 +30,7 @@ import org.androidannotations.helper.CanonicalNameConstants; import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JDefinedClass; @@ -178,6 +179,10 @@ public EBeanHolder getEBeanHolder(Element element) { return eBeanHolders.get(element); } + public JClass refClass(Class clazz) { + return codeModel.ref(clazz); + } + public JClass refClass(String fullyQualifiedClassName) { int arrayCounter = 0; @@ -200,8 +205,33 @@ public JClass refClass(String fullyQualifiedClassName) { return refClass; } - public JClass refClass(Class clazz) { - return codeModel.ref(clazz); + public JDefinedClass definedClass(String fullyQualifiedClassName) { + JDefinedClass refClass = (JDefinedClass) loadedClasses.get(fullyQualifiedClassName); + if (refClass == null) { + try { + refClass = codeModel._class(fullyQualifiedClassName); + } catch (JClassAlreadyExistsException e) { + refClass = (JDefinedClass) refClass(fullyQualifiedClassName); + } + loadedClasses.put(fullyQualifiedClassName, refClass); + } + return refClass; + } + + /** + * Return a unique JClass reference by using {@link JCodeModel#ref(String)} + * and keeping a buffer. + * + * @param fullyQualifiedClassName + * @return + */ + JClass uniqueClass(String fullyQualifiedClassName) { + JClass refClass = loadedClasses.get(fullyQualifiedClassName); + if (refClass == null) { + refClass = codeModel.directClass(fullyQualifiedClassName); + loadedClasses.put(fullyQualifiedClassName, refClass); + } + return refClass; } public JCodeModel codeModel() { diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/DeleteProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/DeleteProcessor.java index 24d20051c9..a75803b49f 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/DeleteProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/DeleteProcessor.java @@ -23,18 +23,13 @@ import org.androidannotations.annotations.rest.Delete; import org.androidannotations.processing.EBeanHolder; -import com.sun.codemodel.JBlock; + import com.sun.codemodel.JCodeModel; -import com.sun.codemodel.JExpr; -import com.sun.codemodel.JInvocation; -import com.sun.codemodel.JVar; public class DeleteProcessor extends MethodProcessor { - private EBeanHolder holder; - - public DeleteProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { - super(processingEnv, restImplementationHolder); + public DeleteProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { + super(processingEnv, restImplementationsHolder); } @Override @@ -45,7 +40,6 @@ public Class getTarget() { @Override public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws Exception { - this.holder = holder; ExecutableElement executableElement = (ExecutableElement) element; Delete deleteAnnotation = element.getAnnotation(Delete.class); @@ -54,25 +48,4 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t generateRestTemplateCallBlock(new MethodProcessorHolder(holder, executableElement, urlSuffix, null, null, codeModel)); } - @Override - protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - } - - @Override - protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - - } - - @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall; - } - - @Override - protected JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement) { - return generateHttpHeadersVar(holder, body, executableElement); - } - } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java new file mode 100644 index 0000000000..55fececf57 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetPostProcessor.java @@ -0,0 +1,283 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * Licensed 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.androidannotations.processing.rest; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.WildcardType; + +import org.androidannotations.helper.APTCodeModelHelper; +import org.androidannotations.helper.CanonicalNameConstants; +import org.androidannotations.processing.EBeanHolder; + +import com.sun.codemodel.JClass; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JPackage; + +public abstract class GetPostProcessor extends MethodProcessor { + + protected EBeanHolder holder; + + /** + * Will be use to generate specific classes + */ + protected JPackage restClientPackage; + + protected APTCodeModelHelper helper; + + public GetPostProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { + super(processingEnv, restImplementationsHolder); + helper = new APTCodeModelHelper(); + } + + @Override + public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { + this.holder = holder; + restClientPackage = restImplementationsHolder.getEnclosingHolder(element).restImplementationClass._package(); + + String urlSuffix = retrieveUrlSuffix(element); + + ExecutableElement executableElement = (ExecutableElement) element; + MethodProcessorHolder processorHolder = new MethodProcessorHolder(holder, executableElement, urlSuffix, null, null, codeModel); + + // Retrieve return type + TypeMirror returnType = executableElement.getReturnType(); + if (returnType.getKind() != TypeKind.VOID) { + retrieveReturnAndExpectedClasses(returnType, processorHolder); + } + + generateRestTemplateCallBlock(processorHolder); + } + + public abstract String retrieveUrlSuffix(Element element); + + /** + * Retrieve the expected and method return classes to use in generated code. + *

+ * If the annotated method return a ResponseEntity<T> then : + * + *

+	 * expectedClass = T.class, methodReturnClass = ResponseEntity<T>
+	 * 
+ * + * + * @param returnType + * @param processorHolder + */ + public void retrieveReturnAndExpectedClasses(TypeMirror returnType, MethodProcessorHolder processorHolder) { + String returnTypeString = returnType.toString(); + + JClass expectedClass = null; + JClass returnClass = helper.typeMirrorToJClass(returnType, holder); + + if (returnTypeString.startsWith(CanonicalNameConstants.RESPONSE_ENTITY)) { + DeclaredType declaredReturnType = (DeclaredType) returnType; + if (declaredReturnType.getTypeArguments().size() > 0) { + expectedClass = resolveExpectedClass(declaredReturnType.getTypeArguments().get(0)); + } else { + expectedClass = holder.refClass(CanonicalNameConstants.RESPONSE_ENTITY); + } + } else { + expectedClass = resolveExpectedClass(returnType); + } + + processorHolder.setExpectedClass(expectedClass); + processorHolder.setMethodReturnClass(returnClass); + } + + /** + * Resolve the expected class for the input type according to the following + * rules : + * + * + * @param expectedType + */ + private JClass resolveExpectedClass(TypeMirror expectedType) { + // is a class or an interface + if (expectedType.getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) expectedType; + + List typeArguments = declaredType.getTypeArguments(); + + // is NOT a generics, return directly + if (typeArguments.isEmpty()) { + return helper.typeMirrorToJClass(declaredType, holder); + } + + // is a generics, must generate a new super class + TypeElement declaredElement = (TypeElement) declaredType.asElement(); + + JClass baseClass = helper.typeMirrorToJClass(declaredType, holder).erasure(); + JClass decoratedExpectedClass = retrieveDecoratedExpectedClass(declaredType, declaredElement); + if (decoratedExpectedClass == null) { + decoratedExpectedClass = baseClass; + } + return decoratedExpectedClass; + } else if (expectedType.getKind() == TypeKind.ARRAY) { + ArrayType arrayType = (ArrayType) expectedType; + return resolveExpectedClass(arrayType.getComponentType()).array(); + } + + // is not a class nor an interface, return directly + return helper.typeMirrorToJClass(expectedType, holder); + } + + /** + * Recursive method used to find if one of the grand-parent of the + * enclosingJClass is {@link Map}, {@link Set} or + * {@link Collection}. + * + * @param declaredType + * @param currentClass + * @return + */ + private JClass retrieveDecoratedExpectedClass(DeclaredType declaredType, TypeElement typeElement) { + String classTypeBaseName = typeElement.toString(); + + // Looking for basic java.util interfaces to set a default + // implementation + String decoratedClassName = null; + + if (typeElement.getKind() == ElementKind.INTERFACE) { + if (classTypeBaseName.equals(CanonicalNameConstants.MAP)) { + decoratedClassName = LinkedHashMap.class.getCanonicalName(); + } else if (classTypeBaseName.equals(CanonicalNameConstants.SET)) { + decoratedClassName = TreeSet.class.getCanonicalName(); + } else if (classTypeBaseName.equals(CanonicalNameConstants.LIST)) { + decoratedClassName = ArrayList.class.getCanonicalName(); + } else if (classTypeBaseName.equals(CanonicalNameConstants.COLLECTION)) { + decoratedClassName = ArrayList.class.getCanonicalName(); + } + } else { + decoratedClassName = typeElement.getQualifiedName().toString(); + } + + if (decoratedClassName != null) { + // Configure the super class of the final decorated class + String decoratedClassNameSuffix = ""; + JClass decoratedSuperClass = holder.refClass(decoratedClassName); + for (TypeMirror typeArgument : declaredType.getTypeArguments()) { + if (typeArgument instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) typeArgument; + if (wildcardType.getExtendsBound() != null) { + typeArgument = wildcardType.getExtendsBound(); + } else if (wildcardType.getSuperBound() != null) { + typeArgument = wildcardType.getSuperBound(); + } + } + JClass narrowJClass = helper.typeMirrorToJClass(typeArgument, holder); + decoratedSuperClass = decoratedSuperClass.narrow(narrowJClass); + decoratedClassNameSuffix += plainName(narrowJClass); + } + + String decoratedFinalClassName = classTypeBaseName + "_" + decoratedClassNameSuffix; + decoratedFinalClassName = decoratedFinalClassName.replaceAll("\\[\\]", "s"); + decoratedFinalClassName = restClientPackage.name() + "." + decoratedFinalClassName; + JDefinedClass decoratedJClass = holder.definedClass(decoratedFinalClassName); + decoratedJClass._extends(decoratedSuperClass); + + return decoratedJClass; + } + + // Try to find the superclass and make a recursive call to the this + // method + TypeMirror enclosingSuperJClass = typeElement.getSuperclass(); + if (enclosingSuperJClass != null && enclosingSuperJClass.getKind() == TypeKind.DECLARED) { + DeclaredType declaredEnclosingSuperJClass = (DeclaredType) enclosingSuperJClass; + return retrieveDecoratedExpectedClass(declaredType, (TypeElement) declaredEnclosingSuperJClass.asElement()); + } + + // Falling back to the current enclosingJClass if Class can't be found + return null; + } + + protected String plainName(JClass jClass) { + String plainName = jClass.erasure().name(); + List typeParameters = jClass.getTypeParameters(); + if (typeParameters.size() > 0) { + plainName += "_"; + for (JClass typeParameter : typeParameters) { + plainName += plainName(typeParameter); + } + } + return plainName; + } + + @Override + protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { + return restCall.arg(generateHttpEntityVar(methodHolder)); + } + + @Override + protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { + JClass expectedClass = methodHolder.getExpectedClass(); + + if (expectedClass != null) { + return restCall.arg(expectedClass.dotclass()); + } else { + return restCall.arg(JExpr._null()); + } + } + + @Override + protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { + JClass generatedReturnType = methodHolder.getMethodReturnClass(); + if (generatedReturnType == null) { + return restCall; + } + + if (!generatedReturnType.fullName().startsWith(CanonicalNameConstants.RESPONSE_ENTITY)) { + restCall = JExpr.invoke(restCall, "getBody"); + } + + return restCall; + } + +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java index de16597a96..1db14ed9ad 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/GetProcessor.java @@ -19,24 +19,10 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; import org.androidannotations.annotations.rest.Get; -import org.androidannotations.helper.CanonicalNameConstants; -import org.androidannotations.processing.EBeanHolder; -import com.sun.codemodel.JBlock; -import com.sun.codemodel.JClass; -import com.sun.codemodel.JCodeModel; -import com.sun.codemodel.JExpr; -import com.sun.codemodel.JInvocation; -import com.sun.codemodel.JVar; -public class GetProcessor extends MethodProcessor { - - private EBeanHolder holder; +public class GetProcessor extends GetPostProcessor { public GetProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { super(processingEnv, restImplementationHolder); @@ -48,66 +34,9 @@ public Class getTarget() { } @Override - public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { - - this.holder = holder; - ExecutableElement executableElement = (ExecutableElement) element; - - TypeMirror returnType = executableElement.getReturnType(); - - JClass generatedReturnType = null; - String returnTypeString = returnType.toString(); - JClass expectedClass = null; - - if (returnType.getKind() != TypeKind.VOID) { - if (returnTypeString.startsWith(CanonicalNameConstants.RESPONSE_ENTITY)) { - DeclaredType declaredReturnedType = (DeclaredType) returnType; - TypeMirror typeParameter = declaredReturnedType.getTypeArguments().get(0); - expectedClass = holder.refClass(typeParameter.toString()); - generatedReturnType = holder.refClass(CanonicalNameConstants.RESPONSE_ENTITY).narrow(expectedClass); - } else { - generatedReturnType = holder.refClass(returnTypeString); - expectedClass = generatedReturnType; - } - } - + public String retrieveUrlSuffix(Element element) { Get getAnnotation = element.getAnnotation(Get.class); - String urlSuffix = getAnnotation.value(); - - generateRestTemplateCallBlock(new MethodProcessorHolder(holder, executableElement, urlSuffix, expectedClass, generatedReturnType, codeModel)); - } - - @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { - JClass expectedClass = methodHolder.getExpectedClass(); - JClass generatedReturnType = methodHolder.getGeneratedReturnType(); - - if (expectedClass == generatedReturnType) { - restCall = JExpr.invoke(restCall, "getBody"); - } - - return restCall; - } - - @Override - protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(generateHttpEntityVar(methodHolder)); - } - - @Override - protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { - JClass expectedClass = methodHolder.getExpectedClass(); - - if (expectedClass != null) { - return restCall.arg(expectedClass.dotclass()); - } else { - return restCall.arg(JExpr._null()); - } - } - - @Override - protected JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement) { - return generateHttpHeadersVar(holder, body, executableElement); + return getAnnotation.value(); } } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java index abd0c38afc..a9d9c11978 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/HeadProcessor.java @@ -24,19 +24,16 @@ import org.androidannotations.annotations.rest.Head; import org.androidannotations.processing.EBeanHolder; -import com.sun.codemodel.JBlock; + import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JExpr; import com.sun.codemodel.JInvocation; -import com.sun.codemodel.JVar; public class HeadProcessor extends MethodProcessor { - private EBeanHolder holder; - - public HeadProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { - super(processingEnv, restImplementationHolder); + public HeadProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { + super(processingEnv, restImplementationsHolder); } @Override @@ -47,7 +44,6 @@ public Class getTarget() { @Override public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws Exception { - this.holder = holder; ExecutableElement executableElement = (ExecutableElement) element; TypeMirror returnType = executableElement.getReturnType(); @@ -65,19 +61,4 @@ protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorH return JExpr.invoke(restCall, "getHeaders"); } - @Override - protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - } - - @Override - protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - } - - @Override - protected JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement) { - return generateHttpHeadersVar(holder, body, executableElement); - } - } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java index d140bf77c0..842b24b93b 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessor.java @@ -15,7 +15,6 @@ */ package org.androidannotations.processing.rest; -import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -23,15 +22,15 @@ import java.util.TreeMap; import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import org.androidannotations.annotations.rest.Accept; import org.androidannotations.helper.CanonicalNameConstants; import org.androidannotations.helper.RestAnnotationHelper; -import org.androidannotations.processing.EBeanHolder; import org.androidannotations.processing.DecoratingElementProcessor; +import org.androidannotations.processing.EBeanHolder; + import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; @@ -47,75 +46,98 @@ public abstract class MethodProcessor implements DecoratingElementProcessor { protected final RestImplementationsHolder restImplementationsHolder; protected final RestAnnotationHelper restAnnotationHelper; - public MethodProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { - restImplementationsHolder = restImplementationHolder; + public MethodProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { + this.restImplementationsHolder = restImplementationsHolder; restAnnotationHelper = new RestAnnotationHelper(processingEnv, getTarget()); } protected void generateRestTemplateCallBlock(MethodProcessorHolder methodHolder) { RestImplementationHolder holder = restImplementationsHolder.getEnclosingHolder(methodHolder.getElement()); ExecutableElement executableElement = (ExecutableElement) methodHolder.getElement(); + EBeanHolder eBeanHolder = methodHolder.getHolder(); JClass expectedClass = methodHolder.getExpectedClass(); - JClass generatedReturnType = methodHolder.getGeneratedReturnType(); + JClass methodReturnClass = methodHolder.getMethodReturnClass(); + // Creating method signature JMethod method; String methodName = executableElement.getSimpleName().toString(); - boolean methodReturnVoid = generatedReturnType == null && expectedClass == null; + boolean methodReturnVoid = methodReturnClass == null && expectedClass == null; if (methodReturnVoid) { method = holder.restImplementationClass.method(JMod.PUBLIC, void.class, methodName); } else { - method = holder.restImplementationClass.method(JMod.PUBLIC, methodHolder.getGeneratedReturnType(), methodName); + method = holder.restImplementationClass.method(JMod.PUBLIC, methodHolder.getMethodReturnClass(), methodName); } method.annotate(Override.class); + if (expectedClass != methodReturnClass && !methodReturnClass.fullName().startsWith(CanonicalNameConstants.RESPONSE_ENTITY)) { + method.annotate(SuppressWarnings.class).param("value", "unchecked"); + } + // Keep a reference on method's body JBlock body = method.body(); + methodHolder.setBody(body); - // exchange method call + // Keep a reference on method's parameters + TreeMap methodParams = extractMethodParamsVar(eBeanHolder, method, executableElement, holder); + methodHolder.setMethodParams(methodParams); + + // RestTemplate exchange() method call JInvocation restCall = JExpr.invoke(holder.restTemplateField, "exchange"); - // concat root url + suffix + // RestTemplate exchange() 1st arg : concat root url + suffix JInvocation concatCall = JExpr.invoke(holder.rootUrlField, "concat"); - // add url param + // RestTemplate exchange() 2nd arg : add url param restCall.arg(concatCall.arg(JExpr.lit(methodHolder.getUrlSuffix()))); - EBeanHolder eBeanHolder = methodHolder.getHolder(); + // RestTemplate exchange() 3rd arg : add HttpMethod type param JClass httpMethod = eBeanHolder.refClass(CanonicalNameConstants.HTTP_METHOD); + // add method type param String restMethodInCapitalLetters = getTarget().getSimpleName().toUpperCase(Locale.ENGLISH); - restCall.arg(httpMethod.staticRef(restMethodInCapitalLetters)); - - TreeMap methodParams = (TreeMap) generateMethodParamsVar(eBeanHolder, method, executableElement, holder); - - // update method holder - methodHolder.setBody(body); - methodHolder.setMethodParams(methodParams); - JVar hashMapVar = generateHashMapVar(methodHolder); + restCall.arg(httpMethod.staticRef(restMethodInCapitalLetters)); restCall = addHttpEntityVar(restCall, methodHolder); restCall = addResponseEntityArg(restCall, methodHolder); - boolean hasParametersInUrl = hashMapVar != null; - if (hasParametersInUrl) { + JVar hashMapVar = generateHashMapVar(methodHolder); + if (hashMapVar != null) { restCall.arg(hashMapVar); } - restCall = addResultCallMethod(restCall, methodHolder); - - insertRestCallInBody(body, restCall, methodReturnVoid); + insertRestCallInBody(body, restCall, methodHolder, methodReturnVoid); } - protected abstract JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder); + /** + * Add the HttpEntity attribute to restTemplate.exchange() method. By + * default, the value will be null (for DELETE, HEAD and + * OPTIONS method type) + */ + protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { + return restCall.arg(JExpr._null()); + } - protected abstract JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder); + /** + * Add the response type to restTemplate.exchange() method. This is used to + * bind the response into a specific Java object. + */ + protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { + return restCall.arg(JExpr._null()); + } - protected abstract JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder); + /** + * Add an extra method calls on the result of restTemplate.exchange(). By + * default, just return the result + */ + protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { + return restCall; + } - private void insertRestCallInBody(JBlock body, JInvocation restCall, boolean methodReturnVoid) { + private void insertRestCallInBody(JBlock body, JInvocation restCall, MethodProcessorHolder methodHolder, boolean methodReturnVoid) { if (methodReturnVoid) { body.add(restCall); } else { + restCall = addResultCallMethod(restCall, methodHolder); body._return(restCall); } } @@ -124,7 +146,7 @@ private JVar generateHashMapVar(MethodProcessorHolder methodHolder) { ExecutableElement element = (ExecutableElement) methodHolder.getElement(); JCodeModel codeModel = methodHolder.getCodeModel(); JBlock body = methodHolder.getBody(); - TreeMap methodParams = methodHolder.getMethodParams(); + Map methodParams = methodHolder.getMethodParams(); JVar hashMapVar = null; List urlVariables = restAnnotationHelper.extractUrlVariableNames(element); @@ -217,7 +239,7 @@ private String retrieveAcceptAnnotationValue(ExecutableElement executableElement } } - private Map generateMethodParamsVar(EBeanHolder eBeanHolder, JMethod method, ExecutableElement executableElement, RestImplementationHolder holder) { + private TreeMap extractMethodParamsVar(EBeanHolder eBeanHolder, JMethod method, ExecutableElement executableElement, RestImplementationHolder holder) { List params = executableElement.getParameters(); TreeMap methodParams = new TreeMap(); for (VariableElement parameter : params) { @@ -233,12 +255,4 @@ private Map generateMethodParamsVar(EBeanHolder eBeanHolder, JMeth return methodParams; } - protected abstract JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement); - - @Override - public abstract Class getTarget(); - - @Override - public abstract void process(Element element, JCodeModel codeModel, EBeanHolder eBeanHolder) throws Exception; - } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessorHolder.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessorHolder.java index 4d4e7da010..e34827ed6b 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessorHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/MethodProcessorHolder.java @@ -30,7 +30,7 @@ public class MethodProcessorHolder { private Element element; private String urlSuffix; private JClass expectedClass; - private JClass generatedReturnType; + private JClass methodReturnClass; private JCodeModel codeModel; private JBlock body; @@ -42,7 +42,7 @@ public MethodProcessorHolder(EBeanHolder holder, Element element, String urlSuff this.element = element; this.urlSuffix = urlSuffix; this.expectedClass = expectedClass; - this.generatedReturnType = generatedReturnType; + this.methodReturnClass = generatedReturnType; this.codeModel = codeModel; } @@ -58,8 +58,16 @@ public JClass getExpectedClass() { return expectedClass; } - public JClass getGeneratedReturnType() { - return generatedReturnType; + public void setExpectedClass(JClass expectedClass) { + this.expectedClass = expectedClass; + } + + public JClass getMethodReturnClass() { + return methodReturnClass; + } + + public void setMethodReturnClass(JClass methodReturnClass) { + this.methodReturnClass = methodReturnClass; } public JCodeModel getCodeModel() { diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java index d463d338c4..40e66de552 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/OptionsProcessor.java @@ -26,19 +26,16 @@ import org.androidannotations.annotations.rest.Options; import org.androidannotations.helper.CanonicalNameConstants; import org.androidannotations.processing.EBeanHolder; -import com.sun.codemodel.JBlock; + import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JExpr; import com.sun.codemodel.JInvocation; -import com.sun.codemodel.JVar; public class OptionsProcessor extends MethodProcessor { - private EBeanHolder holder; - - public OptionsProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { - super(processingEnv, restImplementationHolder); + public OptionsProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { + super(processingEnv, restImplementationsHolder); } @Override @@ -49,7 +46,6 @@ public Class getTarget() { @Override public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws Exception { - this.holder = holder; ExecutableElement executableElement = (ExecutableElement) element; TypeMirror returnType = executableElement.getReturnType(); @@ -75,19 +71,4 @@ protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorH return restCall; } - @Override - protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - } - - @Override - protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - } - - @Override - protected JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement) { - return generateHttpHeadersVar(holder, body, executableElement); - } - } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PostProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PostProcessor.java index d275d752d0..545652608e 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PostProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PostProcessor.java @@ -19,24 +19,10 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; import org.androidannotations.annotations.rest.Post; -import org.androidannotations.helper.CanonicalNameConstants; -import org.androidannotations.processing.EBeanHolder; -import com.sun.codemodel.JBlock; -import com.sun.codemodel.JClass; -import com.sun.codemodel.JCodeModel; -import com.sun.codemodel.JExpr; -import com.sun.codemodel.JInvocation; -import com.sun.codemodel.JVar; -public class PostProcessor extends MethodProcessor { - - private EBeanHolder holder; +public class PostProcessor extends GetPostProcessor { public PostProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { super(processingEnv, restImplementationHolder); @@ -48,73 +34,9 @@ public Class getTarget() { } @Override - public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { - - this.holder = holder; - ExecutableElement executableElement = (ExecutableElement) element; - - TypeMirror returnType = executableElement.getReturnType(); - - JClass generatedReturnType = null; - String returnTypeString = returnType.toString(); - JClass expectedClass = null; - - if (returnType.getKind() != TypeKind.VOID) { - if (returnTypeString.startsWith(CanonicalNameConstants.URI)) { - DeclaredType declaredReturnedType = (DeclaredType) returnType; - TypeMirror typeParameter = declaredReturnedType.getTypeArguments().get(0); - expectedClass = holder.refClass(typeParameter.toString()); - generatedReturnType = holder.refClass(CanonicalNameConstants.URI); - } else if (returnTypeString.startsWith(CanonicalNameConstants.RESPONSE_ENTITY)) { - DeclaredType declaredReturnedType = (DeclaredType) returnType; - TypeMirror typeParameter = declaredReturnedType.getTypeArguments().get(0); - expectedClass = holder.refClass(typeParameter.toString()); - generatedReturnType = holder.refClass(CanonicalNameConstants.RESPONSE_ENTITY).narrow(expectedClass); - } else { - generatedReturnType = holder.refClass(returnTypeString); - expectedClass = generatedReturnType; - } - } - - Post postAnnotation = element.getAnnotation(Post.class); - String urlSuffix = postAnnotation.value(); - - generateRestTemplateCallBlock(new MethodProcessorHolder(holder, executableElement, urlSuffix, expectedClass, generatedReturnType, codeModel)); - } - - @Override - protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(generateHttpEntityVar(methodHolder)); - } - - @Override - protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { - JClass expectedClass = methodHolder.getExpectedClass(); - - if (expectedClass != null) { - restCall.arg(expectedClass.dotclass()); - } else { - restCall.arg(JExpr._null()); - } - - return restCall; - } - - @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { - JClass expectedClass = methodHolder.getExpectedClass(); - JClass generatedReturnType = methodHolder.getGeneratedReturnType(); - - if (expectedClass == generatedReturnType && expectedClass != null) { - restCall = JExpr.invoke(restCall, "getBody"); - } - - return restCall; - } - - @Override - protected JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement) { - return generateHttpHeadersVar(holder, body, executableElement); + public String retrieveUrlSuffix(Element element) { + Post getAnnotation = element.getAnnotation(Post.class); + return getAnnotation.value(); } } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PutProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PutProcessor.java index 6dcb945b2a..691cd29c2a 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PutProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/PutProcessor.java @@ -23,18 +23,14 @@ import org.androidannotations.annotations.rest.Put; import org.androidannotations.processing.EBeanHolder; -import com.sun.codemodel.JBlock; + import com.sun.codemodel.JCodeModel; -import com.sun.codemodel.JExpr; import com.sun.codemodel.JInvocation; -import com.sun.codemodel.JVar; public class PutProcessor extends MethodProcessor { - private EBeanHolder holder; - - public PutProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { - super(processingEnv, restImplementationHolder); + public PutProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { + super(processingEnv, restImplementationsHolder); } @Override @@ -45,7 +41,6 @@ public Class getTarget() { @Override public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws Exception { - this.holder = holder; ExecutableElement executableElement = (ExecutableElement) element; Put putAnnotation = element.getAnnotation(Put.class); @@ -54,24 +49,9 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) t generateRestTemplateCallBlock(new MethodProcessorHolder(holder, executableElement, urlSuffix, null, null, codeModel)); } - @Override - protected JInvocation addResultCallMethod(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall; - } - @Override protected JInvocation addHttpEntityVar(JInvocation restCall, MethodProcessorHolder methodHolder) { return restCall.arg(generateHttpEntityVar(methodHolder)); } - @Override - protected JInvocation addResponseEntityArg(JInvocation restCall, MethodProcessorHolder methodHolder) { - return restCall.arg(JExpr._null()); - } - - @Override - protected JVar addHttpHeadersVar(JBlock body, ExecutableElement executableElement) { - return generateHttpHeadersVar(holder, body, executableElement); - } - } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java index 301cf1e19a..16cce5c74e 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/rest/RestProcessor.java @@ -50,12 +50,12 @@ public class RestProcessor implements GeneratingElementProcessor { - private final RestImplementationsHolder restImplementationHolder; + private final RestImplementationsHolder restImplementationsHolder; private AnnotationHelper annotationHelper; - public RestProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationHolder) { + public RestProcessor(ProcessingEnvironment processingEnv, RestImplementationsHolder restImplementationsHolder) { annotationHelper = new AnnotationHelper(processingEnv); - this.restImplementationHolder = restImplementationHolder; + this.restImplementationsHolder = restImplementationsHolder; } @Override @@ -66,7 +66,7 @@ public Class getTarget() { @Override public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHolder) throws Exception { - RestImplementationHolder holder = restImplementationHolder.create(element); + RestImplementationHolder holder = restImplementationsHolder.create(element); TypeElement typeElement = (TypeElement) element; String interfaceName = typeElement.getQualifiedName().toString(); @@ -152,5 +152,4 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo } } - } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/GetValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/GetValidator.java index 3d102b52f9..43384bd371 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/GetValidator.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/GetValidator.java @@ -58,10 +58,8 @@ public boolean validate(Element element, AnnotationElements validatedElements) { validatorHelper.throwsOnlyRestClientException(executableElement, valid); - validatorHelper.returnTypeNotGenericUnlessResponseEntity(executableElement, valid); - validatorHelper.doesNotReturnPrimitive(executableElement, valid); - + restAnnotationHelper.urlVariableNamesExistInParametersAndHasNoOneMoreParameter(executableElement, valid); return valid.isValid(); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/PostValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/PostValidator.java index 38d1695208..5ce032db10 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/PostValidator.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/rest/PostValidator.java @@ -57,10 +57,8 @@ public boolean validate(Element element, AnnotationElements validatedElements) { ExecutableElement executableElement = (ExecutableElement) element; validatorHelper.throwsOnlyRestClientException(executableElement, valid); - + validatorHelper.doesNotReturnPrimitive(executableElement, valid); - - validatorHelper.returnTypeNotGenericUnlessResponseEntity(executableElement, valid); restAnnotationHelper.urlVariableNamesExistInParametersAndHasOnlyOneMoreParameter(executableElement, valid); diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/processing/EBeansHolderTests.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/processing/EBeansHolderTests.java new file mode 100644 index 0000000000..4fe54998f4 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/processing/EBeansHolderTests.java @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * Licensed 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.androidannotations.processing; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.sun.codemodel.JClass; +import com.sun.codemodel.JCodeModel; + +public class EBeansHolderTests { + + private EBeansHolder eBeansHolder; + + public EBeansHolderTests() { + eBeansHolder = new EBeansHolder(new JCodeModel()); + } + + private void checkForFullyQualifiedClassName(String fullyQualifiedClassName) { + checkForFullyQualifiedClassName(fullyQualifiedClassName, fullyQualifiedClassName); + } + + private void checkForFullyQualifiedClassName(String fullyQualifiedClassName, String expetedClassName) { + JClass refClass = eBeansHolder.refClass(fullyQualifiedClassName); + Assert.assertEquals(expetedClassName, refClass.fullName()); + } + + @Test + public void testRefClass_primitive() { + checkForFullyQualifiedClassName("int"); + } + + @Test + public void testRefClass_generics_one_arg() { + checkForFullyQualifiedClassName("java.util.List"); + checkForFullyQualifiedClassName("java.util.List"); + checkForFullyQualifiedClassName("java.util.List[]"); + checkForFullyQualifiedClassName("java.util.List[][]"); + checkForFullyQualifiedClassName("java.util.List"); + checkForFullyQualifiedClassName("java.util.List[]"); + } + + @Test + public void testRefClass_generics_multiple_args() { + checkForFullyQualifiedClassName("java.util.Map"); + checkForFullyQualifiedClassName("java.util.Map[]"); + checkForFullyQualifiedClassName("java.util.Map[][]"); + checkForFullyQualifiedClassName("java.util.Map"); + checkForFullyQualifiedClassName("java.util.Map"); + checkForFullyQualifiedClassName("java.util.Map[]"); + + checkForFullyQualifiedClassName("java.util.Map", "java.util.Map"); + checkForFullyQualifiedClassName("java.util.Map[]", "java.util.Map[]"); + } + + @Test + public void testRefClass_generics_inner_args() { + checkForFullyQualifiedClassName("java.util.Map,java.lang.Integer>"); + checkForFullyQualifiedClassName("java.util.Map[],java.lang.Integer>"); + checkForFullyQualifiedClassName("java.util.Map[],java.lang.Integer>[]"); + + checkForFullyQualifiedClassName("java.util.Map < java.util.Set < java.lang.String > , java.lang.Integer >", "java.util.Map,java.lang.Integer>"); + } + + @Test + public void testRefClass_wildcards() { + checkForFullyQualifiedClassName("java.util.List", "java.util.List"); + checkForFullyQualifiedClassName("java.util.Map", "java.util.Map"); + checkForFullyQualifiedClassName("java.util.Map", "java.util.Map"); + } + + @Test + public void testRefClass_typevar_simple() { + checkForFullyQualifiedClassName("java.util.List"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRefClass_typevar_extends() { + checkForFullyQualifiedClassName("java.util.Set"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRefClass_typevar_multiple() { + checkForFullyQualifiedClassName("java.util.Map"); + } + +} diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rclass/RClassFinderTest.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rclass/RClassFinderTest.java index 6b20155d7c..1ddc92340d 100644 --- a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rclass/RClassFinderTest.java +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/rclass/RClassFinderTest.java @@ -1,3 +1,18 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * Licensed 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.androidannotations.rclass; import org.androidannotations.AndroidAnnotationProcessor; diff --git a/AndroidAnnotations/functional-test-1-5-tests/pom.xml b/AndroidAnnotations/functional-test-1-5-tests/pom.xml index fbfbbe1bd9..98033ef359 100644 --- a/AndroidAnnotations/functional-test-1-5-tests/pom.xml +++ b/AndroidAnnotations/functional-test-1-5-tests/pom.xml @@ -52,7 +52,12 @@ bcprov-jdk16 140 - + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.6 + diff --git a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java index a135d3472d..ccd3d9dbce 100644 --- a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java +++ b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/rest/MyServiceTest.java @@ -15,37 +15,132 @@ */ package org.androidannotations.test15.rest; +import static junit.framework.Assert.assertEquals; import static org.mockito.Matchers.startsWith; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import java.util.List; import java.util.Map; +import org.androidannotations.test15.AndroidAnnotationsTestRunner; +import org.apache.http.message.BasicHeader; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; -import org.androidannotations.test15.AndroidAnnotationsTestRunner; +import com.xtremelabs.robolectric.Robolectric; @RunWith(AndroidAnnotationsTestRunner.class) public class MyServiceTest { + private MyService_ myService = new MyService_(); + + private void addPendingResponse(String jsonResponse) { + Robolectric.addPendingHttpResponse(HttpStatus.OK.value(), jsonResponse.replaceAll("'", "\""), new BasicHeader("content-type", "application/json")); + } + @Test public void can_override_root_url() { MyService_ myService = new MyService_(); - + RestTemplate restTemplate = mock(RestTemplate.class); myService.setRestTemplate(restTemplate); - myService.setRootUrl("http://newRootUrl"); - + myService.removeEvent(42); - - verify(restTemplate).exchange(startsWith("http://newRootUrl"), Mockito. any(), Mockito.> any(), Mockito.> any(), Mockito.>any()); + verify(restTemplate).exchange(startsWith("http://newRootUrl"), Mockito. any(), Mockito.> any(), Mockito.> any(), Mockito.> any()); + } + + @Test + public void getEventsArray2() { + addPendingResponse("[{'id':1,'name':'event1'},{'id':2,'name':'event2'}]"); + ResponseEntity responseEntity = myService.getEventsArray2("test", 42); + Event[] events = responseEntity.getBody(); + + Event event1 = new Event(1, "event1"); + Event event2 = new Event(2, "event2"); + + assertEquals(2, events.length); + assertEquals(event1, events[0]); + assertEquals(event2, events[1]); + } + + @Test + public void getEventsGenericsList() { + addPendingResponse("[{'id':1,'name':'event1'},{'id':2,'name':'event2'}]"); + List events = myService.getEventsGenericsList("test", 42); + + Event event1 = new Event(1, "event1"); + Event event2 = new Event(2, "event2"); + + assertEquals(2, events.size()); + assertEquals(event1, events.get(0)); + assertEquals(event2, events.get(1)); } - + + @Test + public void getEventsGenericsArrayList() { + addPendingResponse("[[{'id':1,'name':'event1'},{'id':2,'name':'event2'}],[{'id':3,'name':'event3'}]]"); + List[] events = myService.getEventsGenericsArrayList("test", 42); + + Event event1 = new Event(1, "event1"); + Event event2 = new Event(2, "event2"); + Event event3 = new Event(3, "event3"); + + assertEquals(2, events.length); + assertEquals(event1, events[0].get(0)); + assertEquals(event2, events[0].get(1)); + assertEquals(event3, events[1].get(0)); + } + + @Test + public void getEventsGenericsListListEvent() { + addPendingResponse("[[{'id':1,'name':'event1'},{'id':2,'name':'event2'}],[{'id':3,'name':'event3'}]]"); + List> events = myService.getEventsGenericsListListEvent("test", 42); + + Event event1 = new Event(1, "event1"); + Event event2 = new Event(2, "event2"); + Event event3 = new Event(3, "event3"); + + assertEquals(2, events.size()); + assertEquals(event1, events.get(0).get(0)); + assertEquals(event2, events.get(0).get(1)); + assertEquals(event3, events.get(1).get(0)); + } + + @Test + public void getEventsGenericsListListEvents() { + addPendingResponse("[[[{'id':1,'name':'event1'}],[{'id':2,'name':'event2'}]],[[{'id':3,'name':'event3'}]]]"); + List> events = myService.getEventsGenericsListListEvents("test", 42); + + Event event1 = new Event(1, "event1"); + Event event2 = new Event(2, "event2"); + Event event3 = new Event(3, "event3"); + + assertEquals(2, events.size()); + assertEquals(event1, events.get(0).get(0)[0]); + assertEquals(event2, events.get(0).get(1)[0]); + assertEquals(event3, events.get(1).get(0)[0]); + } + + @Test + public void getEventsGenericsMap() { + addPendingResponse("{'event1':{'id':1,'name':'event1'},'event2':{'id':2,'name':'event2'}}"); + Map eventsMap = myService.getEventsGenericsMap("test", 42); + + Event event1 = new Event(1, "event1"); + Event event2 = new Event(2, "event2"); + + assertEquals(2, eventsMap.size()); + assertEquals(event1, eventsMap.get("event1")); + assertEquals(event2, eventsMap.get("event2")); + } + } diff --git a/AndroidAnnotations/functional-test-1-5/.factorypath b/AndroidAnnotations/functional-test-1-5/.factorypath index d285dd6e54..a9234a759b 100644 --- a/AndroidAnnotations/functional-test-1-5/.factorypath +++ b/AndroidAnnotations/functional-test-1-5/.factorypath @@ -1,4 +1,6 @@ - + + + diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/Event.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/Event.java index 2502aa0133..cff033cb1f 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/Event.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/Event.java @@ -17,4 +17,59 @@ public class Event { + private int id; + private String name; + + public Event() { + } + + public Event(int id, String name) { + this.id = id; + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Event other = (Event) obj; + if (id != other.id) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/GenericEvent.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/GenericEvent.java new file mode 100644 index 0000000000..81218ddb89 --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/GenericEvent.java @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2010-2012 eBusiness Information, Excilys Group + * + * Licensed 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.androidannotations.test15.rest; + +public class GenericEvent { + T value; +} diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java index e79a256c2c..d8a4c4b46c 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/rest/MyService.java @@ -15,6 +15,8 @@ */ package org.androidannotations.test15.rest; +import java.util.List; +import java.util.Map; import java.util.Set; import org.springframework.http.HttpHeaders; @@ -38,6 +40,8 @@ @Rest(rootUrl = "http://company.com/ajax/services", converters = { MappingJacksonHttpMessageConverter.class }) public interface MyService { + // *** GET *** + // url variables are mapped to method parameter names. @Get("/events/{year}/{location}") @Accept(MediaType.APPLICATION_JSON) @@ -54,8 +58,7 @@ public interface MyService { // The response can be a ResponseEntity @Get("/events/{year}/{location}") /* - * You may (or may not) declare throwing RestClientException (as a reminder, - * since it's a RuntimeException), but nothing else. + * You may (or may not) declare throwing RestClientException (as a reminder, since it's a RuntimeException), but nothing else. */ ResponseEntity getEvents2(String location, int year) throws RestClientException; @@ -68,6 +71,47 @@ ResponseEntity getEventsArray2(String location, int year) ResponseEntity getEventsArrayOfArrays2(String location, int year) throws RestClientException; + @Get("/events/{year}/{location}") + List getEventsGenericsList(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + List[] getEventsGenericsArrayList(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + List> getEventsGenericsListListEvent(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + List> getEventsGenericsListListEvents(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + List getEventsGenericsListArray(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + List getEventsGenericsListArrayArray(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + Set getEventsGenericsSet(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + GenericEvent getEventsGenericString(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + GenericEvent getEventsGenericInteger(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + GenericEvent> getEventsGenericListEvent(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + GenericEvent>> getEventsGenericsInception(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + Map getEventsGenericsMap(String location, int year) throws RestClientException; + + @Get("/events/{year}/{location}") + void getEventsVoid(String location, int year) throws RestClientException; + + // *** POST *** + // There should be max 1 parameter that is not mapped to an attribute. This // parameter will be used as the post entity. @Post("/events/") @@ -86,6 +130,25 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) @Post("/events/") ResponseEntity addEvent3(Event event); + @Post("/events/") + List addEventGenericsListWildcardExtends(Event event); + + @Post("/events/") + List addEventGenericsList(Event event); + + // TODO: Handle generics in params + // @Post("/events/") + // List addEventGenericsList(List events); + + @Post("/events/") + Set addEventGenericsSet(Event event); + + @Post("/events/") + GenericEvent>> addEventGenericsInception(Event event); + + @Post("/events/") + Map addEventGenericsMap(Event event); + /** * Output different then input */ @@ -99,16 +162,24 @@ ResponseEntity getEventsArrayOfArrays2(String location, int year) @Accept(MediaType.APPLICATION_JSON) ResponseEntity addEvent2(Event event, int year); + // *** PUT *** + @Put("/events/{id}") void updateEvent(Event event, int id); + // *** DELETE *** + // url variables are mapped to method parameter names. @Delete("/events/{id}") void removeEvent(long id); + // *** HEAD *** + @Head("/events/{year}/{location}") HttpHeaders getEventHeaders(String location, int year); + // *** OPTIONS *** + @Options("/events/{year}/{location}") Set getEventOptions(String location, int year);