From be954c9abb5a283b6e92b1caa6910f9dbe74a656 Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 23 May 2013 20:20:30 +0200 Subject: [PATCH 01/10] Implement onHandleIntent for @EIntentService according to issue #572 --- .../annotations/EIntentService.java | 29 ++++ .../annotations/ServiceAction.java | 32 ++++ .../AndroidAnnotationProcessor.java | 12 ++ .../helper/CanonicalNameConstants.java | 1 + .../helper/ValidatorHelper.java | 10 ++ .../processing/EBeanHolder.java | 3 + .../processing/EIntentServiceProcessor.java | 107 ++++++++++++ .../processing/ServiceActionProcessor.java | 159 ++++++++++++++++++ .../validation/EIntentServiceValidator.java | 57 +++++++ .../validation/ServiceActionValidator.java | 55 ++++++ 10 files changed, 465 insertions(+) create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EIntentServiceProcessor.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ServiceActionValidator.java diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java new file mode 100644 index 0000000000..a58d4b860b --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2010-2013 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use this annotation to enhance an Android Service + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface EIntentService { +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java new file mode 100644 index 0000000000..5090e62ce1 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2010-2013 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use this annotation to enhance an Android Service + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface ServiceAction { + + String value() default ""; + +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java index 4f7de7646c..0380f9c465 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java @@ -52,6 +52,7 @@ import org.androidannotations.annotations.EApplication; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.EIntentService; import org.androidannotations.annotations.EProvider; import org.androidannotations.annotations.EReceiver; import org.androidannotations.annotations.EService; @@ -82,6 +83,7 @@ import org.androidannotations.annotations.SeekBarProgressChange; import org.androidannotations.annotations.SeekBarTouchStart; import org.androidannotations.annotations.SeekBarTouchStop; +import org.androidannotations.annotations.ServiceAction; import org.androidannotations.annotations.SystemService; import org.androidannotations.annotations.TextChange; import org.androidannotations.annotations.Touch; @@ -141,6 +143,7 @@ import org.androidannotations.processing.EApplicationProcessor; import org.androidannotations.processing.EBeanProcessor; import org.androidannotations.processing.EFragmentProcessor; +import org.androidannotations.processing.EIntentServiceProcessor; import org.androidannotations.processing.EProviderProcessor; import org.androidannotations.processing.EReceiverProcessor; import org.androidannotations.processing.EServiceProcessor; @@ -178,6 +181,7 @@ import org.androidannotations.processing.SeekBarProgressChangeProcessor; import org.androidannotations.processing.SeekBarTouchStartProcessor; import org.androidannotations.processing.SeekBarTouchStopProcessor; +import org.androidannotations.processing.ServiceActionProcessor; import org.androidannotations.processing.SharedPrefProcessor; import org.androidannotations.processing.SubscribeProcessor; import org.androidannotations.processing.SystemServiceProcessor; @@ -212,6 +216,7 @@ import org.androidannotations.validation.EApplicationValidator; import org.androidannotations.validation.EBeanValidator; import org.androidannotations.validation.EFragmentValidator; +import org.androidannotations.validation.EIntentServiceValidator; import org.androidannotations.validation.EProviderValidator; import org.androidannotations.validation.EReceiverValidator; import org.androidannotations.validation.EServiceValidator; @@ -249,6 +254,7 @@ import org.androidannotations.validation.SeekBarProgressChangeValidator; import org.androidannotations.validation.SeekBarTouchStartValidator; import org.androidannotations.validation.SeekBarTouchStopValidator; +import org.androidannotations.validation.ServiceActionValidator; import org.androidannotations.validation.SharedPrefValidator; import org.androidannotations.validation.SubscribeValidator; import org.androidannotations.validation.SystemServiceValidator; @@ -385,6 +391,7 @@ private ModelValidator buildModelValidator(IRClass rClass, AndroidSystemServices modelValidator.register(new EApplicationValidator(processingEnv, androidManifest)); modelValidator.register(new EActivityValidator(processingEnv, rClass, androidManifest)); modelValidator.register(new EServiceValidator(processingEnv, androidManifest)); + modelValidator.register(new EIntentServiceValidator(processingEnv, androidManifest)); modelValidator.register(new EReceiverValidator(processingEnv, androidManifest)); modelValidator.register(new EProviderValidator(processingEnv, androidManifest)); modelValidator.register(new EFragmentValidator(processingEnv, rClass)); @@ -439,6 +446,7 @@ private ModelValidator buildModelValidator(IRClass rClass, AndroidSystemServices modelValidator.register(new SeekBarProgressChangeValidator(processingEnv, rClass)); modelValidator.register(new SeekBarTouchStartValidator(processingEnv, rClass)); modelValidator.register(new SeekBarTouchStopValidator(processingEnv, rClass)); + modelValidator.register(new ServiceActionValidator(processingEnv)); /* * Any view injection or listener binding should occur before * AfterViewsValidator @@ -481,6 +489,7 @@ private ModelProcessor buildModelProcessor(IRClass rClass, AndroidSystemServices modelProcessor.register(new EApplicationProcessor()); modelProcessor.register(new EActivityProcessor(processingEnv, rClass)); modelProcessor.register(new EServiceProcessor()); + modelProcessor.register(new EIntentServiceProcessor()); modelProcessor.register(new EReceiverProcessor()); modelProcessor.register(new EProviderProcessor()); modelProcessor.register(new EFragmentProcessor(processingEnv, rClass)); @@ -535,6 +544,7 @@ private ModelProcessor buildModelProcessor(IRClass rClass, AndroidSystemServices modelProcessor.register(new SeekBarProgressChangeProcessor(processingEnv, rClass)); modelProcessor.register(new SeekBarTouchStartProcessor(processingEnv, rClass)); modelProcessor.register(new SeekBarTouchStopProcessor(processingEnv, rClass)); + modelProcessor.register(new ServiceActionProcessor(processingEnv)); /* * Any view injection or listener binding should occur before * AfterViewsProcessor @@ -650,6 +660,8 @@ public Set getSupportedAnnotationTypes() { Bean.class, // AfterInject.class, // EService.class, // + EIntentService.class, // + ServiceAction.class, // EReceiver.class, // EProvider.class, // Trace.class, // 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 587607d52f..15a10b31c3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java @@ -91,6 +91,7 @@ public final class CanonicalNameConstants { public static final String MOTION_EVENT = "android.view.MotionEvent"; public static final String HANDLER = "android.os.Handler"; public static final String SERVICE = "android.app.Service"; + public static final String INTENT_SERVICE = "android.app.IntentService"; public static final String BROADCAST_RECEIVER = "android.content.BroadcastReceiver"; public static final String CONTENT_PROVIDER = "android.content.ContentProvider"; public static final String SQLITE_DATABASE = "android.database.sqlite.SQLiteDatabase"; diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java index 06762a8b1c..a9cb52e708 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java @@ -53,6 +53,7 @@ import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.EIntentService; import org.androidannotations.annotations.Trace; import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.rest.Delete; @@ -183,6 +184,11 @@ public void enclosingElementHasEFragment(Element element, AnnotationElements val hasClassAnnotation(element, enclosingElement, validatedElements, EFragment.class, valid); } + public void enclosingElementHasEIntentService(Element element, AnnotationElements validatedElements, IsValid valid) { + Element enclosingElement = element.getEnclosingElement(); + hasClassAnnotation(element, enclosingElement, validatedElements, EIntentService.class, valid); + } + public void hasEActivity(Element element, AnnotationElements validatedElements, IsValid valid) { hasClassAnnotation(element, element, validatedElements, EActivity.class, valid); } @@ -472,6 +478,10 @@ public void extendsService(Element element, IsValid valid) { extendsType(element, CanonicalNameConstants.SERVICE, valid); } + public void extendsIntentService(Element element, IsValid valid) { + extendsType(element, CanonicalNameConstants.INTENT_SERVICE, valid); + } + public void extendsReceiver(Element element, IsValid valid) { extendsType(element, CanonicalNameConstants.BROADCAST_RECEIVER, valid); } 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 cf1792c191..7ca53b6c71 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java @@ -130,6 +130,9 @@ public class EBeanHolder { private ViewChangedHolder viewChangedHolder; + public JVar onHandleIntentIntent; + public JBlock onHandleIntentBody; + /** * Only defined in beans that implement {@link HasViews} */ diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EIntentServiceProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EIntentServiceProcessor.java new file mode 100644 index 0000000000..476a317e3e --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EIntentServiceProcessor.java @@ -0,0 +1,107 @@ +/** + * Copyright (C) 2010-2013 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 static com.sun.codemodel.JExpr._this; +import static com.sun.codemodel.JMod.FINAL; +import static com.sun.codemodel.JMod.PRIVATE; +import static com.sun.codemodel.JMod.PUBLIC; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import org.androidannotations.annotations.EIntentService; +import org.androidannotations.helper.APTCodeModelHelper; +import org.androidannotations.helper.ModelConstants; + +import com.sun.codemodel.ClassType; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JVar; + +public class EIntentServiceProcessor extends EServiceProcessor { + + private final APTCodeModelHelper aptCodeModelHelper; + + public EIntentServiceProcessor() { + aptCodeModelHelper = new APTCodeModelHelper(); + } + + @Override + public String getTarget() { + return EIntentService.class.getName(); + } + + @Override + public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHolder) throws Exception { + + TypeElement typeElement = (TypeElement) element; + + String annotatedComponentQualifiedName = typeElement.getQualifiedName().toString(); + + String generatedComponentQualifiedName = annotatedComponentQualifiedName + ModelConstants.GENERATION_SUFFIX; + + JDefinedClass generatedClass = codeModel._class(PUBLIC | FINAL, generatedComponentQualifiedName, ClassType.CLASS); + + EBeanHolder holder = eBeansHolder.create(element, EIntentService.class, generatedClass); + + JClass annotatedComponent = codeModel.directClass(annotatedComponentQualifiedName); + + holder.generatedClass._extends(annotatedComponent); + + holder.contextRef = _this(); + + JMethod init = holder.generatedClass.method(PRIVATE, codeModel.VOID, "init_"); + holder.initBody = init.body(); + { + // onCreate + JMethod onCreate = holder.generatedClass.method(PUBLIC, codeModel.VOID, "onCreate"); + onCreate.annotate(Override.class); + JBlock onCreateBody = onCreate.body(); + onCreateBody.invoke(init); + onCreateBody.invoke(JExpr._super(), onCreate); + } + + { + // onHandleIntent + JMethod onHandleIntent = holder.generatedClass.method(PUBLIC, codeModel.VOID, "onHandleIntent"); + JVar intent = onHandleIntent.param(eBeansHolder.classes().INTENT, "intent"); + onHandleIntent.annotate(Override.class); + JBlock onHandleIntentBody = onHandleIntent.body(); + aptCodeModelHelper.callSuperMethod(onHandleIntent, holder, onHandleIntentBody); + + holder.onHandleIntentIntent = intent; + holder.onHandleIntentBody = onHandleIntentBody; + } + + { + /* + * Setting to null shouldn't be a problem as long as we don't allow + * + * @App and @Extra on this component + */ + holder.initIfActivityBody = null; + holder.initActivityRef = null; + } + + aptCodeModelHelper.addServiceIntentBuilder(codeModel, holder); + + } +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java new file mode 100644 index 0000000000..5682c4fe56 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2010-2013 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 static com.sun.codemodel.JExpr._null; +import static com.sun.codemodel.JExpr.lit; +import static com.sun.codemodel.JMod.FINAL; +import static com.sun.codemodel.JMod.PUBLIC; +import static com.sun.codemodel.JMod.STATIC; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; + +import org.androidannotations.annotations.ServiceAction; +import org.androidannotations.helper.APTCodeModelHelper; +import org.androidannotations.helper.CaseHelper; +import org.androidannotations.processing.EBeansHolder.Classes; + +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JExpression; +import com.sun.codemodel.JFieldVar; +import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JPrimitiveType; +import com.sun.codemodel.JType; +import com.sun.codemodel.JTypeVar; +import com.sun.codemodel.JVar; + +public class ServiceActionProcessor implements DecoratingElementProcessor { + + private final APTCodeModelHelper helper = new APTCodeModelHelper(); + private final ProcessingEnvironment processingEnv; + + public ServiceActionProcessor(ProcessingEnvironment processingEnv) { + this.processingEnv = processingEnv; + } + + @Override + public String getTarget() { + return ServiceAction.class.getName(); + } + + @Override + public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { + ExecutableElement executableElement = (ExecutableElement) element; + + String methodName = element.getSimpleName().toString(); + + TypeMirror elementType = element.asType(); + + Classes classes = holder.classes(); + + // Action field + ServiceAction annotation = element.getAnnotation(ServiceAction.class); + String extraKey = annotation.value(); + if (extraKey.isEmpty()) { + extraKey = methodName; + } + + String staticFieldName; + if (methodName.startsWith("action")) { + staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(methodName); + } else { + staticFieldName = CaseHelper.camelCaseToUpperSnakeCase("action_" + methodName); + } + JFieldVar actionKeyField = holder.generatedClass.field(PUBLIC | STATIC | FINAL, classes.STRING, staticFieldName, lit(extraKey)); + + if (holder.onHandleIntentBody != null) { + JBlock actionBlock = holder.onHandleIntentBody.block(); + + // getAction + JInvocation getActionInvok = holder.onHandleIntentIntent.invoke("getAction"); + JVar actionVar = actionBlock.decl(classes.STRING, "action", getActionInvok); + + // If action match, call the method + JBlock callActionBlock = actionBlock._if(actionKeyField.invoke("equals").arg(actionVar))._then(); + JInvocation callActionInvok = JExpr._super().invoke(methodName); + + // For each method params, we get back value from extras and put it + // in super calls + List methodParameters = executableElement.getParameters(); + if (methodParameters.size() > 0) { + if (holder.cast == null) { + generateCastMethod(codeModel, holder); + } + + // Extras + JVar extras = callActionBlock.decl(classes.BUNDLE, "extras"); + extras.init(holder.onHandleIntentIntent.invoke("getExtras")); + JBlock extrasNotNullBlock = callActionBlock._if(extras.ne(_null()))._then(); + + List extraFields = new ArrayList(); + + // Extras params + for (VariableElement param : methodParameters) { + holder.onHandleIntentIntent.invoke("getStringExtra"); + + String paramName = param.getSimpleName().toString(); + String extraParamName = paramName + "Extra"; + JClass extraParamClass = helper.typeMirrorToJClass(param.asType(), holder); + boolean isPrimitive = false; // param.getKind().isPrimitive(); + + JExpression extraInvok; + if (isPrimitive) { + JPrimitiveType primitiveType = JType.parse(codeModel, elementType.toString()); + JClass wrapperType = primitiveType.boxify(); + extraInvok = JExpr.cast(wrapperType, extras.invoke("get").arg(paramName)); + } else { + extraInvok = JExpr.invoke(holder.cast).arg(extras.invoke("get").arg(paramName)); + } + JVar extraField = extrasNotNullBlock.decl(extraParamClass, extraParamName, extraInvok); + extraFields.add(extraField); + } + + for (JVar extraField : extraFields) { + callActionInvok.arg(extraField); + } + extrasNotNullBlock.add(callActionInvok); + } else { + callActionBlock.add(callActionInvok); + } + } + + } + + private void generateCastMethod(JCodeModel codeModel, EBeanHolder holder) { + JType objectType = codeModel._ref(Object.class); + JMethod method = holder.generatedClass.method(JMod.PRIVATE, objectType, "cast_"); + JTypeVar genericType = method.generify("T"); + method.type(genericType); + JVar objectParam = method.param(objectType, "object"); + method.annotate(SuppressWarnings.class).param("value", "unchecked"); + method.body()._return(JExpr.cast(genericType, objectParam)); + holder.cast = method; + } +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java new file mode 100644 index 0000000000..1710c23678 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2010-2013 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.validation; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; + +import org.androidannotations.annotations.EIntentService; +import org.androidannotations.helper.AndroidManifest; +import org.androidannotations.helper.TargetAnnotationHelper; +import org.androidannotations.helper.ValidatorHelper; +import org.androidannotations.model.AnnotationElements; + +public class EIntentServiceValidator implements ElementValidator { + + private final ValidatorHelper validatorHelper; + private final AndroidManifest androidManifest; + + public EIntentServiceValidator(ProcessingEnvironment processingEnv, AndroidManifest androidManifest) { + this.androidManifest = androidManifest; + TargetAnnotationHelper annotationHelper = new TargetAnnotationHelper(processingEnv, getTarget()); + validatorHelper = new ValidatorHelper(annotationHelper); + } + + @Override + public String getTarget() { + return EIntentService.class.getName(); + } + + @Override + public boolean validate(Element element, AnnotationElements validatedElements) { + + IsValid valid = new IsValid(); + + validatorHelper.extendsIntentService(element, valid); + + validatorHelper.isNotFinal(element, valid); + + validatorHelper.componentRegistered(element, androidManifest, valid); + + return valid.isValid(); + } + +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ServiceActionValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ServiceActionValidator.java new file mode 100644 index 0000000000..3c218cc756 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ServiceActionValidator.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2010-2013 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.validation; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; + +import org.androidannotations.annotations.ServiceAction; +import org.androidannotations.helper.TargetAnnotationHelper; +import org.androidannotations.helper.ValidatorHelper; +import org.androidannotations.model.AnnotationElements; + +public class ServiceActionValidator implements ElementValidator { + + private ValidatorHelper validatorHelper; + + public ServiceActionValidator(ProcessingEnvironment processingEnv) { + TargetAnnotationHelper annotationHelper = new TargetAnnotationHelper(processingEnv, getTarget()); + validatorHelper = new ValidatorHelper(annotationHelper); + } + + @Override + public String getTarget() { + return ServiceAction.class.getName(); + } + + @Override + public boolean validate(Element element, AnnotationElements validatedElements) { + IsValid valid = new IsValid(); + + validatorHelper.enclosingElementHasEIntentService(element, validatedElements, valid); + + ExecutableElement executableElement = (ExecutableElement) element; + validatorHelper.returnTypeIsVoid(executableElement, valid); + + validatorHelper.isNotPrivate(element, valid); + + return valid.isValid(); + } + +} From 0e05794a534a72e60d991dc06439d51513d12965 Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 23 May 2013 21:21:06 +0200 Subject: [PATCH 02/10] Add @ServiceAction methods in IntentBuilder --- .../processing/ServiceActionProcessor.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java index 5682c4fe56..5c6c2327ae 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java @@ -16,10 +16,14 @@ package org.androidannotations.processing; import static com.sun.codemodel.JExpr._null; +import static com.sun.codemodel.JExpr.cast; import static com.sun.codemodel.JExpr.lit; import static com.sun.codemodel.JMod.FINAL; import static com.sun.codemodel.JMod.PUBLIC; import static com.sun.codemodel.JMod.STATIC; +import static org.androidannotations.helper.CanonicalNameConstants.PARCELABLE; +import static org.androidannotations.helper.CanonicalNameConstants.SERIALIZABLE; +import static org.androidannotations.helper.CanonicalNameConstants.STRING; import java.util.ArrayList; import java.util.List; @@ -28,7 +32,10 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import org.androidannotations.annotations.ServiceAction; import org.androidannotations.helper.APTCodeModelHelper; @@ -144,6 +151,62 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { } } + /* + * holder.intentBuilderClass may be null if the annotated component is + * an abstract activity + */ + if (holder.intentBuilderClass != null) { + // flags() + JMethod method = holder.intentBuilderClass.method(PUBLIC, holder.intentBuilderClass, methodName); + JBlock body = method.body(); + + // setAction + body.invoke(holder.intentField, "setAction").arg(actionKeyField); + + // For each method params, we get put value into extras + List methodParameters = executableElement.getParameters(); + if (methodParameters.size() > 0) { + + // Extras params + for (VariableElement param : methodParameters) { + String paramName = param.getSimpleName().toString(); + String extraParamName = paramName + "Extra"; + + boolean castToSerializable = false; + boolean castToParcelable = false; + TypeMirror extraType = param.asType(); + if (extraType.getKind() == TypeKind.DECLARED) { + Elements elementUtils = processingEnv.getElementUtils(); + Types typeUtils = processingEnv.getTypeUtils(); + TypeMirror parcelableType = elementUtils.getTypeElement(PARCELABLE).asType(); + if (!typeUtils.isSubtype(extraType, parcelableType)) { + TypeMirror stringType = elementUtils.getTypeElement(STRING).asType(); + if (!typeUtils.isSubtype(extraType, stringType)) { + castToSerializable = true; + } + } else { + TypeMirror serializableType = elementUtils.getTypeElement(SERIALIZABLE).asType(); + if (typeUtils.isSubtype(extraType, serializableType)) { + castToParcelable = true; + } + } + } + JClass paramClass = helper.typeMirrorToJClass(extraType, holder); + JVar extraParam = method.param(paramClass, extraParamName); + JInvocation invocation = body.invoke(holder.intentField, "putExtra").arg(paramName); + if (castToSerializable) { + invocation.arg(cast(classes.SERIALIZABLE, extraParam)); + } else if (castToParcelable) { + invocation.arg(cast(classes.PARCELABLE, extraParam)); + } else { + invocation.arg(extraParam); + } + } + + } + body._return(JExpr._this()); + } + } private void generateCastMethod(JCodeModel codeModel, EBeanHolder holder) { From c9a53d3e7a537d70d778d546ce059a25f96636ac Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 23 May 2013 21:38:46 +0200 Subject: [PATCH 03/10] Add functional tests --- .../functional-test-1-5/AndroidManifest.xml | 16 ++-- .../eintentservice/MyIntentService.java | 54 ++++++++++++++ .../eservice/MySimpleIntentService.java | 73 +++++++++++++++++++ 3 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eservice/MySimpleIntentService.java diff --git a/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml b/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml index 9ac6259141..1190c0f3f1 100644 --- a/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml +++ b/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml @@ -1,6 +1,5 @@ - - + + @@ -92,6 +90,12 @@ + + - + \ No newline at end of file diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java new file mode 100644 index 0000000000..540e948a42 --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2010-2013 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.eintentservice; + +import org.androidannotations.annotations.EIntentService; +import org.androidannotations.annotations.ServiceAction; + +import android.app.IntentService; +import android.content.Intent; + +@EIntentService +public class MyIntentService extends IntentService { + + // @Bean + // EnhancedClass dependency; + + public MyIntentService() { + super(MyIntentService.class.getSimpleName()); + } + + @ServiceAction + void actionOne() { + + } + + @ServiceAction + void myActionTwo(String valueString) { + + } + + @ServiceAction + void actionThree(String valueString, long valueLong) { + + } + + @Override + protected void onHandleIntent(Intent intent) { + // Do nothing here + } + +} diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eservice/MySimpleIntentService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eservice/MySimpleIntentService.java new file mode 100644 index 0000000000..5f299bc4e2 --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eservice/MySimpleIntentService.java @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2010-2013 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.eservice; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.content.Intent; +import android.util.Log; +import android.widget.Toast; + +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EService; +import org.androidannotations.annotations.OrmLiteDao; +import org.androidannotations.annotations.SystemService; +import org.androidannotations.annotations.Trace; +import org.androidannotations.annotations.UiThread; +import org.androidannotations.test15.ebean.EnhancedClass; +import org.androidannotations.test15.ormlite.DatabaseHelper; +import org.androidannotations.test15.ormlite.User; +import org.androidannotations.test15.ormlite.UserDao; + +@EService +public class MySimpleIntentService extends IntentService { + + @SystemService + NotificationManager notificationManager; + + @Bean + EnhancedClass dependency; + + @OrmLiteDao(helper = DatabaseHelper.class, model = User.class) + UserDao userDao; + + public MySimpleIntentService() { + super(MySimpleIntentService.class.getSimpleName()); + } + + @Override + protected void onHandleIntent(Intent intent) { + // Do some stuff... + + showToast(); + workInBackground(); + } + + @Trace + @UiThread + void showToast() { + Toast.makeText(getApplicationContext(), "Hello World!", + Toast.LENGTH_LONG).show(); + } + + @Trace + @Background + void workInBackground() { + Log.d(MySimpleIntentService.class.getSimpleName(), "Doing some background work."); + } + +} From a8686e6433458aceac93cbeed7d9d73a27d2ef8b Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 23 May 2013 21:41:20 +0200 Subject: [PATCH 04/10] Allow injection of bean and other AA annotations in @EIntentService --- .../java/org/androidannotations/helper/ModelConstants.java | 3 ++- .../test15/eintentservice/MyIntentService.java | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ModelConstants.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ModelConstants.java index 8abc7ff15b..8962e7091c 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ModelConstants.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ModelConstants.java @@ -24,6 +24,7 @@ import org.androidannotations.annotations.EApplication; import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.EIntentService; import org.androidannotations.annotations.EProvider; import org.androidannotations.annotations.EReceiver; import org.androidannotations.annotations.EService; @@ -40,7 +41,7 @@ public abstract class ModelConstants { public static final List> VALID_ENHANCED_VIEW_SUPPORT_ANNOTATIONS = asList(EActivity.class, EViewGroup.class, EView.class, EBean.class, EFragment.class); @SuppressWarnings("unchecked") - public static final List> VALID_ENHANCED_COMPONENT_ANNOTATIONS = asList(EApplication.class, EActivity.class, EViewGroup.class, EView.class, EBean.class, EService.class, EReceiver.class, EProvider.class, EFragment.class); + public static final List> VALID_ENHANCED_COMPONENT_ANNOTATIONS = asList(EApplication.class, EActivity.class, EViewGroup.class, EView.class, EBean.class, EService.class, EIntentService.class, EReceiver.class, EProvider.class, EFragment.class); private ModelConstants() { } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java index 540e948a42..dbaec6c539 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java @@ -15,8 +15,10 @@ */ package org.androidannotations.test15.eintentservice; +import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EIntentService; import org.androidannotations.annotations.ServiceAction; +import org.androidannotations.test15.ebean.EnhancedClass; import android.app.IntentService; import android.content.Intent; @@ -24,8 +26,8 @@ @EIntentService public class MyIntentService extends IntentService { - // @Bean - // EnhancedClass dependency; + @Bean + EnhancedClass dependency; public MyIntentService() { super(MyIntentService.class.getSimpleName()); From 883af2e41c3e6633ee10171bee76cc109f7a31c9 Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 23 May 2013 22:00:43 +0200 Subject: [PATCH 05/10] Fix a bug with primitives and stop execution as soon as an action match --- .../processing/ServiceActionProcessor.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java index 5c6c2327ae..68788969e2 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java @@ -73,11 +73,8 @@ public String getTarget() { @Override public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { ExecutableElement executableElement = (ExecutableElement) element; - String methodName = element.getSimpleName().toString(); - TypeMirror elementType = element.asType(); - Classes classes = holder.classes(); // Action field @@ -128,11 +125,11 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { String paramName = param.getSimpleName().toString(); String extraParamName = paramName + "Extra"; JClass extraParamClass = helper.typeMirrorToJClass(param.asType(), holder); - boolean isPrimitive = false; // param.getKind().isPrimitive(); + boolean isPrimitive = param.asType().getKind().isPrimitive(); JExpression extraInvok; if (isPrimitive) { - JPrimitiveType primitiveType = JType.parse(codeModel, elementType.toString()); + JPrimitiveType primitiveType = JType.parse(codeModel, param.asType().toString()); JClass wrapperType = primitiveType.boxify(); extraInvok = JExpr.cast(wrapperType, extras.invoke("get").arg(paramName)); } else { @@ -149,6 +146,8 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { } else { callActionBlock.add(callActionInvok); } + + callActionBlock._return(); } /* From 9d1b3ca03bcd794f60758b17f324172545e8f655 Mon Sep 17 00:00:00 2001 From: Damien Date: Fri, 24 May 2013 11:11:17 +0200 Subject: [PATCH 06/10] Refactoring common code between ServiceActionProcessor and ExtraProcessor Refactoring three parts : addCastMethod, addIntentBuilderPutExtraMethod and camelCaseToUpperSnakeCase --- .../helper/APTCodeModelHelper.java | 51 +++++++++++++++ .../androidannotations/helper/CaseHelper.java | 10 +++ .../processing/ExtraProcessor.java | 60 ++---------------- .../processing/ServiceActionProcessor.java | 62 ++----------------- .../helper/CaseHelperTest.java | 26 ++++++++ 5 files changed, 95 insertions(+), 114 deletions(-) create mode 100644 AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/helper/CaseHelperTest.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 1648c76447..37f5b02892 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -23,18 +23,25 @@ import static com.sun.codemodel.JMod.PUBLIC; import static com.sun.codemodel.JMod.STATIC; import static javax.lang.model.element.ElementKind.CONSTRUCTOR; +import static org.androidannotations.helper.CanonicalNameConstants.PARCELABLE; +import static org.androidannotations.helper.CanonicalNameConstants.SERIALIZABLE; +import static org.androidannotations.helper.CanonicalNameConstants.STRING; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; 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 javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import org.androidannotations.processing.EBeanHolder; import org.androidannotations.processing.EBeansHolder.Classes; @@ -56,6 +63,7 @@ import com.sun.codemodel.JStatement; import com.sun.codemodel.JTryBlock; import com.sun.codemodel.JType; +import com.sun.codemodel.JTypeVar; import com.sun.codemodel.JVar; public class APTCodeModelHelper { @@ -383,4 +391,47 @@ private void addIntentBuilder(JCodeModel codeModel, EBeanHolder holder, boolean } } } + + public JInvocation addIntentBuilderPutExtraMethod(JCodeModel codeModel, EBeanHolder holder, APTCodeModelHelper helper, ProcessingEnvironment processingEnv, JMethod method, TypeMirror elementType, String paramName, String extraName) { + boolean castToSerializable = false; + boolean castToParcelable = false; + if (elementType.getKind() == TypeKind.DECLARED) { + Elements elementUtils = processingEnv.getElementUtils(); + Types typeUtils = processingEnv.getTypeUtils(); + TypeMirror parcelableType = elementUtils.getTypeElement(PARCELABLE).asType(); + if (!typeUtils.isSubtype(elementType, parcelableType)) { + TypeMirror stringType = elementUtils.getTypeElement(STRING).asType(); + if (!typeUtils.isSubtype(elementType, stringType)) { + castToSerializable = true; + } + } else { + TypeMirror serializableType = elementUtils.getTypeElement(SERIALIZABLE).asType(); + if (typeUtils.isSubtype(elementType, serializableType)) { + castToParcelable = true; + } + } + } + + JClass paramClass = helper.typeMirrorToJClass(elementType, holder); + JVar extraParam = method.param(paramClass, paramName); + JBlock body = method.body(); + JInvocation invocation = body.invoke(holder.intentField, "putExtra").arg(extraName); + if (castToSerializable) { + return invocation.arg(cast(holder.classes().SERIALIZABLE, extraParam)); + } else if (castToParcelable) { + return invocation.arg(cast(holder.classes().PARCELABLE, extraParam)); + } + return invocation.arg(extraParam); + } + + public void addCastMethod(JCodeModel codeModel, EBeanHolder holder) { + JType objectType = codeModel._ref(Object.class); + JMethod method = holder.generatedClass.method(JMod.PRIVATE, objectType, "cast_"); + JTypeVar genericType = method.generify("T"); + method.type(genericType); + JVar objectParam = method.param(objectType, "object"); + method.annotate(SuppressWarnings.class).param("value", "unchecked"); + method.body()._return(JExpr.cast(genericType, objectParam)); + holder.cast = method; + } } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CaseHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CaseHelper.java index 29324824d0..522c678c22 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CaseHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CaseHelper.java @@ -60,4 +60,14 @@ public static String camelCaseToUpperSnakeCase(String camelCase) { return camelCaseToSnakeCase(camelCase).toUpperCase(); } + public static String camelCaseToUpperSnakeCase(String prefix, String camelCase, String suffix) { + if (prefix != null && !camelCase.startsWith(prefix)) { + camelCase = prefix + "_" + camelCase; + } + if (suffix != null && !camelCase.toLowerCase().endsWith(suffix.toLowerCase())) { + camelCase = camelCase + "_" + suffix; + } + return camelCaseToUpperSnakeCase(camelCase); + } + } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ExtraProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ExtraProcessor.java index 2e92d3c51f..ae4a458801 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ExtraProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ExtraProcessor.java @@ -18,23 +18,16 @@ import static com.sun.codemodel.JExpr._null; import static com.sun.codemodel.JExpr._super; import static com.sun.codemodel.JExpr._this; -import static com.sun.codemodel.JExpr.cast; import static com.sun.codemodel.JExpr.invoke; import static com.sun.codemodel.JExpr.lit; import static com.sun.codemodel.JMod.FINAL; import static com.sun.codemodel.JMod.PRIVATE; import static com.sun.codemodel.JMod.PUBLIC; import static com.sun.codemodel.JMod.STATIC; -import static org.androidannotations.helper.CanonicalNameConstants.PARCELABLE; -import static org.androidannotations.helper.CanonicalNameConstants.SERIALIZABLE; -import static org.androidannotations.helper.CanonicalNameConstants.STRING; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; -import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; import org.androidannotations.annotations.Extra; import org.androidannotations.helper.APTCodeModelHelper; @@ -50,11 +43,9 @@ import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; -import com.sun.codemodel.JMod; import com.sun.codemodel.JPrimitiveType; import com.sun.codemodel.JTryBlock; import com.sun.codemodel.JType; -import com.sun.codemodel.JTypeVar; import com.sun.codemodel.JVar; public class ExtraProcessor implements DecoratingElementProcessor { @@ -87,19 +78,14 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { Classes classes = holder.classes(); if (!isPrimitive && holder.cast == null) { - generateCastMethod(codeModel, holder); + helper.addCastMethod(codeModel, holder); } if (holder.extras == null) { injectExtras(holder, codeModel); } - String staticFieldName; - if (fieldName.endsWith("Extra")) { - staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(fieldName); - } else { - staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(fieldName + "Extra"); - } + String staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(null, fieldName, "Extra"); JFieldVar extraKeyField = holder.generatedClass.field(PUBLIC | STATIC | FINAL, classes.STRING, staticFieldName, lit(extraKey)); @@ -137,53 +123,15 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { // flags() JMethod method = holder.intentBuilderClass.method(PUBLIC, holder.intentBuilderClass, fieldName); - boolean castToSerializable = false; - boolean castToParcelable = false; - TypeMirror extraType = elementType; - if (extraType.getKind() == TypeKind.DECLARED) { - Elements elementUtils = processingEnv.getElementUtils(); - Types typeUtils = processingEnv.getTypeUtils(); - TypeMirror parcelableType = elementUtils.getTypeElement(PARCELABLE).asType(); - if (!typeUtils.isSubtype(extraType, parcelableType)) { - TypeMirror stringType = elementUtils.getTypeElement(STRING).asType(); - if (!typeUtils.isSubtype(extraType, stringType)) { - castToSerializable = true; - } - } else { - TypeMirror serializableType = elementUtils.getTypeElement(SERIALIZABLE).asType(); - if (typeUtils.isSubtype(extraType, serializableType)) { - castToParcelable = true; - } - } - } - JClass paramClass = helper.typeMirrorToJClass(extraType, holder); - JVar extraParam = method.param(paramClass, fieldName); + helper.addIntentBuilderPutExtraMethod(codeModel, holder, helper, processingEnv, method, elementType, fieldName, staticFieldName); + JBlock body = method.body(); - JInvocation invocation = body.invoke(holder.intentField, "putExtra").arg(extraKeyField); - if (castToSerializable) { - invocation.arg(cast(classes.SERIALIZABLE, extraParam)); - } else if (castToParcelable) { - invocation.arg(cast(classes.PARCELABLE, extraParam)); - } else { - invocation.arg(extraParam); - } body._return(_this()); } } } - private void generateCastMethod(JCodeModel codeModel, EBeanHolder holder) { - JType objectType = codeModel._ref(Object.class); - JMethod method = holder.generatedClass.method(JMod.PRIVATE, objectType, "cast_"); - JTypeVar genericType = method.generify("T"); - method.type(genericType); - JVar objectParam = method.param(objectType, "object"); - method.annotate(SuppressWarnings.class).param("value", "unchecked"); - method.body()._return(JExpr.cast(genericType, objectParam)); - holder.cast = method; - } - /** * Adds call to injectExtras_() in onCreate and setIntent() methods. */ diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java index 68788969e2..305c572534 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ServiceActionProcessor.java @@ -16,14 +16,10 @@ package org.androidannotations.processing; import static com.sun.codemodel.JExpr._null; -import static com.sun.codemodel.JExpr.cast; import static com.sun.codemodel.JExpr.lit; import static com.sun.codemodel.JMod.FINAL; import static com.sun.codemodel.JMod.PUBLIC; import static com.sun.codemodel.JMod.STATIC; -import static org.androidannotations.helper.CanonicalNameConstants.PARCELABLE; -import static org.androidannotations.helper.CanonicalNameConstants.SERIALIZABLE; -import static org.androidannotations.helper.CanonicalNameConstants.STRING; import java.util.ArrayList; import java.util.List; @@ -32,10 +28,6 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; import org.androidannotations.annotations.ServiceAction; import org.androidannotations.helper.APTCodeModelHelper; @@ -50,10 +42,8 @@ import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; -import com.sun.codemodel.JMod; import com.sun.codemodel.JPrimitiveType; import com.sun.codemodel.JType; -import com.sun.codemodel.JTypeVar; import com.sun.codemodel.JVar; public class ServiceActionProcessor implements DecoratingElementProcessor { @@ -84,12 +74,8 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { extraKey = methodName; } - String staticFieldName; - if (methodName.startsWith("action")) { - staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(methodName); - } else { - staticFieldName = CaseHelper.camelCaseToUpperSnakeCase("action_" + methodName); - } + String staticFieldName = CaseHelper.camelCaseToUpperSnakeCase("action", methodName, null); + JFieldVar actionKeyField = holder.generatedClass.field(PUBLIC | STATIC | FINAL, classes.STRING, staticFieldName, lit(extraKey)); if (holder.onHandleIntentBody != null) { @@ -108,7 +94,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { List methodParameters = executableElement.getParameters(); if (methodParameters.size() > 0) { if (holder.cast == null) { - generateCastMethod(codeModel, holder); + helper.addCastMethod(codeModel, holder); } // Extras @@ -169,37 +155,8 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { // Extras params for (VariableElement param : methodParameters) { String paramName = param.getSimpleName().toString(); - String extraParamName = paramName + "Extra"; - boolean castToSerializable = false; - boolean castToParcelable = false; - TypeMirror extraType = param.asType(); - if (extraType.getKind() == TypeKind.DECLARED) { - Elements elementUtils = processingEnv.getElementUtils(); - Types typeUtils = processingEnv.getTypeUtils(); - TypeMirror parcelableType = elementUtils.getTypeElement(PARCELABLE).asType(); - if (!typeUtils.isSubtype(extraType, parcelableType)) { - TypeMirror stringType = elementUtils.getTypeElement(STRING).asType(); - if (!typeUtils.isSubtype(extraType, stringType)) { - castToSerializable = true; - } - } else { - TypeMirror serializableType = elementUtils.getTypeElement(SERIALIZABLE).asType(); - if (typeUtils.isSubtype(extraType, serializableType)) { - castToParcelable = true; - } - } - } - JClass paramClass = helper.typeMirrorToJClass(extraType, holder); - JVar extraParam = method.param(paramClass, extraParamName); - JInvocation invocation = body.invoke(holder.intentField, "putExtra").arg(paramName); - if (castToSerializable) { - invocation.arg(cast(classes.SERIALIZABLE, extraParam)); - } else if (castToParcelable) { - invocation.arg(cast(classes.PARCELABLE, extraParam)); - } else { - invocation.arg(extraParam); - } + helper.addIntentBuilderPutExtraMethod(codeModel, holder, helper, processingEnv, method, param.asType(), paramName, paramName); } } @@ -207,15 +164,4 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { } } - - private void generateCastMethod(JCodeModel codeModel, EBeanHolder holder) { - JType objectType = codeModel._ref(Object.class); - JMethod method = holder.generatedClass.method(JMod.PRIVATE, objectType, "cast_"); - JTypeVar genericType = method.generify("T"); - method.type(genericType); - JVar objectParam = method.param(objectType, "object"); - method.annotate(SuppressWarnings.class).param("value", "unchecked"); - method.body()._return(JExpr.cast(genericType, objectParam)); - holder.cast = method; - } } diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/helper/CaseHelperTest.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/helper/CaseHelperTest.java new file mode 100644 index 0000000000..15f1c60aa6 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/helper/CaseHelperTest.java @@ -0,0 +1,26 @@ +package org.androidannotations.helper; + +import junit.framework.Assert; + +import org.junit.Test; + +public class CaseHelperTest { + + @Test + public void testCamelCaseToUpperSnakeCaseString() throws Exception { + Assert.assertEquals("CAMEL_CASE", CaseHelper.camelCaseToUpperSnakeCase("camelCase")); + Assert.assertEquals("CAMEL_CASE", CaseHelper.camelCaseToUpperSnakeCase("camel_case")); + Assert.assertEquals("CAMEL_CASE", CaseHelper.camelCaseToUpperSnakeCase("Camel_Case")); + } + + @Test + public void testCamelCaseToUpperSnakeCaseStringStringString() throws Exception { + Assert.assertEquals("CAMEL_CASE", CaseHelper.camelCaseToUpperSnakeCase(null, "camelCase", null)); + Assert.assertEquals("PREFIX_CAMEL_CASE", CaseHelper.camelCaseToUpperSnakeCase("prefix", "camelCase", null)); + Assert.assertEquals("PREFIX_CAMEL_CASE", CaseHelper.camelCaseToUpperSnakeCase("prefix", "prefixCamelCase", null)); + Assert.assertEquals("CAMEL_CASE_SUFFIX", CaseHelper.camelCaseToUpperSnakeCase(null, "camelCase", "suffix")); + Assert.assertEquals("CAMEL_CASE_SUFFIX", CaseHelper.camelCaseToUpperSnakeCase(null, "camelCaseSuffix", "suffix")); + Assert.assertEquals("PREFIX_CAMEL_CASE_SUFFIX", CaseHelper.camelCaseToUpperSnakeCase("prefix", "camelCase", "suffix")); + } + +} From 5b975898a20161870517a17843394426aabb690c Mon Sep 17 00:00:00 2001 From: Damien Date: Sun, 26 May 2013 21:38:52 +0200 Subject: [PATCH 07/10] Fix javadocs and rename some variables --- .../annotations/EIntentService.java | 2 +- .../annotations/ServiceAction.java | 13 ++++++++++++- .../helper/APTCodeModelHelper.java | 12 ++++++------ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java index a58d4b860b..534d537fb3 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/EIntentService.java @@ -21,7 +21,7 @@ import java.lang.annotation.Target; /** - * Use this annotation to enhance an Android Service + * Use this annotation to enhance an Android IntentService */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java index 5090e62ce1..ede444ba3c 100644 --- a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/ServiceAction.java @@ -20,13 +20,24 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import android.app.IntentService; + /** - * Use this annotation to enhance an Android Service + * Should be used on a method that must respond to a specific action in an + * enhanced {@link IntentService}. The method name will be used as action name + * unless the {@link #value()} field is set. + *

+ * The method signature (with attributes) will be a part of the IntentBuilder + * generated for the {@link EIntentService}. */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface ServiceAction { + /** + * Define the action's name. If this field isn't set the annotated method + * name will be used. + */ String value() default ""; } 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 37f5b02892..87e0b7351a 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -392,7 +392,7 @@ private void addIntentBuilder(JCodeModel codeModel, EBeanHolder holder, boolean } } - public JInvocation addIntentBuilderPutExtraMethod(JCodeModel codeModel, EBeanHolder holder, APTCodeModelHelper helper, ProcessingEnvironment processingEnv, JMethod method, TypeMirror elementType, String paramName, String extraName) { + public JInvocation addIntentBuilderPutExtraMethod(JCodeModel codeModel, EBeanHolder holder, APTCodeModelHelper helper, ProcessingEnvironment processingEnv, JMethod method, TypeMirror elementType, String parameterName, String extraName) { boolean castToSerializable = false; boolean castToParcelable = false; if (elementType.getKind() == TypeKind.DECLARED) { @@ -412,16 +412,16 @@ public JInvocation addIntentBuilderPutExtraMethod(JCodeModel codeModel, EBeanHol } } - JClass paramClass = helper.typeMirrorToJClass(elementType, holder); - JVar extraParam = method.param(paramClass, paramName); + JClass parameterClass = helper.typeMirrorToJClass(elementType, holder); + JVar extraParameterVar = method.param(parameterClass, parameterName); JBlock body = method.body(); JInvocation invocation = body.invoke(holder.intentField, "putExtra").arg(extraName); if (castToSerializable) { - return invocation.arg(cast(holder.classes().SERIALIZABLE, extraParam)); + return invocation.arg(cast(holder.classes().SERIALIZABLE, extraParameterVar)); } else if (castToParcelable) { - return invocation.arg(cast(holder.classes().PARCELABLE, extraParam)); + return invocation.arg(cast(holder.classes().PARCELABLE, extraParameterVar)); } - return invocation.arg(extraParam); + return invocation.arg(extraParameterVar); } public void addCastMethod(JCodeModel codeModel, EBeanHolder holder) { From c1d25d339a6a2877a0c5c5439195ce5799cc7846 Mon Sep 17 00:00:00 2001 From: Damien Date: Sun, 26 May 2013 22:10:05 +0200 Subject: [PATCH 08/10] Add functional tests --- .../eintentservice/MyIntentServiceTest.java | 48 +++++++++++++++++ .../functional-test-1-5/AndroidManifest.xml | 3 ++ .../IntentServiceHandledAction.java | 53 +++++++++++++++++++ .../eintentservice/MyIntentService.java | 6 +-- 4 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java create mode 100644 AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java diff --git a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java new file mode 100644 index 0000000000..8c0213082d --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2010-2013 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.eintentservice; + +import static org.fest.assertions.Assertions.assertThat; + +import org.androidannotations.test15.AndroidAnnotationsTestRunner; +import org.androidannotations.test15.EmptyActivityWithoutLayout_; +import org.androidannotations.test15.eintentservice.IntentServiceHandledAction_.IntentBuilder_; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.content.Intent; + +@RunWith(AndroidAnnotationsTestRunner.class) +public class MyIntentServiceTest { + + @Test + public void testAction() { + IntentServiceHandledAction_.actionForTestHandled = false; + + // Simulate call to intent builder and retrieve the configured Intent + EmptyActivityWithoutLayout_ context = new EmptyActivityWithoutLayout_(); + IntentBuilder_ intentBuilder = IntentServiceHandledAction_.intent( + context).myActionForTests(); + Intent intent = intentBuilder.get(); + + // Simulate the creation of IntentService by Android + IntentServiceHandledAction_ intentServiceHandledAction = new IntentServiceHandledAction_(); + intentServiceHandledAction.onHandleIntent(intent); + + assertThat(IntentServiceHandledAction_.actionForTestHandled).isTrue(); + } + +} diff --git a/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml b/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml index 1190c0f3f1..46b24cea6f 100644 --- a/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml +++ b/AndroidAnnotations/functional-test-1-5/AndroidManifest.xml @@ -96,6 +96,9 @@ + \ No newline at end of file diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java new file mode 100644 index 0000000000..11708da32b --- /dev/null +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2010-2013 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.eintentservice; + +import org.androidannotations.annotations.EIntentService; +import org.androidannotations.annotations.ServiceAction; + +import android.app.IntentService; +import android.content.Intent; + +@EIntentService +public class IntentServiceHandledAction extends IntentService { + + public static boolean actionForTestHandled = false; + + public IntentServiceHandledAction() { + super(IntentServiceHandledAction.class.getSimpleName()); + } + + @ServiceAction + void myActionForTests() { + actionForTestHandled = true; + } + + @ServiceAction + void actionOne() { + actionForTestHandled = false; + } + + @ServiceAction + void myActionTwo() { + actionForTestHandled = false; + } + + @Override + protected void onHandleIntent(Intent intent) { + // Do nothing here + } + +} diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java index dbaec6c539..e4e3c9f067 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/MyIntentService.java @@ -34,16 +34,16 @@ public MyIntentService() { } @ServiceAction - void actionOne() { + void myAction() { } @ServiceAction - void myActionTwo(String valueString) { + void actionOne(String valueString) { } - @ServiceAction + @ServiceAction("myAction") void actionThree(String valueString, long valueLong) { } From 84130ab34511bfb5982cdb2baf84492595d08b7e Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 30 May 2013 00:02:03 +0200 Subject: [PATCH 09/10] Check if there is duplicate method names --- .../helper/AnnotationHelper.java | 6 ++++++ .../helper/ValidatorHelper.java | 21 +++++++++++++++++++ .../validation/EIntentServiceValidator.java | 3 +++ 3 files changed, 30 insertions(+) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/AnnotationHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/AnnotationHelper.java index 9530be82b6..3a937395f3 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/AnnotationHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/AnnotationHelper.java @@ -388,6 +388,12 @@ public boolean enclosingElementHasEnhancedComponentAnnotation(Element element) { return hasOneOfClassAnnotations(enclosingElement, VALID_ENHANCED_COMPONENT_ANNOTATIONS); } + public boolean hasOneOfClassAnnotations(Element element, Class validAnnotation) { + List> annotations = new ArrayList>(); + annotations.add(validAnnotation); + return hasOneOfClassAnnotations(element, annotations); + } + public boolean hasOneOfClassAnnotations(Element element, List> validAnnotations) { for (Class validAnnotation : validAnnotations) { if (element.getAnnotation(validAnnotation) != null) { diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java index a9cb52e708..a4fa288056 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java @@ -34,6 +34,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.TreeSet; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; @@ -54,6 +55,7 @@ import org.androidannotations.annotations.EBean; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.EIntentService; +import org.androidannotations.annotations.ServiceAction; import org.androidannotations.annotations.Trace; import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.rest.Delete; @@ -1105,4 +1107,23 @@ public void validateInterceptors(Element element, IsValid valid) { } } + public void hasNotMultipleAnnotatedMethodWithSameName(Element element, IsValid valid, Class annotation) { + Set actionNames = new TreeSet(); + + List enclosedElements = element.getEnclosedElements(); + for (Element enclosedElement : enclosedElements) { + if (enclosedElement.getKind() != ElementKind.METHOD || !annotationHelper.hasOneOfClassAnnotations(enclosedElement, annotation)) { + continue; + } + + String enclosedElementName = enclosedElement.getSimpleName().toString(); + if (actionNames.contains(enclosedElementName)) { + valid.invalidate(); + annotationHelper.printError(enclosedElement, "The " + TargetAnnotationHelper.annotationName(ServiceAction.class) + " annotated method must have unique name even if the signature is not the same"); + } else { + actionNames.add(enclosedElementName); + } + } + } + } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java index 1710c23678..af6822e277 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/EIntentServiceValidator.java @@ -19,6 +19,7 @@ import javax.lang.model.element.Element; import org.androidannotations.annotations.EIntentService; +import org.androidannotations.annotations.ServiceAction; import org.androidannotations.helper.AndroidManifest; import org.androidannotations.helper.TargetAnnotationHelper; import org.androidannotations.helper.ValidatorHelper; @@ -47,6 +48,8 @@ public boolean validate(Element element, AnnotationElements validatedElements) { validatorHelper.extendsIntentService(element, valid); + validatorHelper.hasNotMultipleAnnotatedMethodWithSameName(element, valid, ServiceAction.class); + validatorHelper.isNotFinal(element, valid); validatorHelper.componentRegistered(element, androidManifest, valid); From 2a60e05a7464dabc3bdbb9016850b60f70251ae8 Mon Sep 17 00:00:00 2001 From: Damien Date: Fri, 20 Sep 2013 17:47:31 +0200 Subject: [PATCH 10/10] Update unit test to make them more readable --- .../eintentservice/MyIntentServiceTest.java | 16 +++++++------- .../IntentServiceHandledAction.java | 21 ++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java index 8c0213082d..bd629074ab 100644 --- a/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java +++ b/AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/eintentservice/MyIntentServiceTest.java @@ -18,8 +18,8 @@ import static org.fest.assertions.Assertions.assertThat; import org.androidannotations.test15.AndroidAnnotationsTestRunner; +import org.androidannotations.test15.EmptyActivityWithoutLayout; import org.androidannotations.test15.EmptyActivityWithoutLayout_; -import org.androidannotations.test15.eintentservice.IntentServiceHandledAction_.IntentBuilder_; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,19 +30,19 @@ public class MyIntentServiceTest { @Test public void testAction() { - IntentServiceHandledAction_.actionForTestHandled = false; + IntentServiceHandledAction.actionForTestHandled = null; // Simulate call to intent builder and retrieve the configured Intent - EmptyActivityWithoutLayout_ context = new EmptyActivityWithoutLayout_(); - IntentBuilder_ intentBuilder = IntentServiceHandledAction_.intent( - context).myActionForTests(); - Intent intent = intentBuilder.get(); + EmptyActivityWithoutLayout context = new EmptyActivityWithoutLayout_(); + Intent intent = IntentServiceHandledAction_.intent(context) // + .MyActionOneParam("test") // + .get(); // Simulate the creation of IntentService by Android - IntentServiceHandledAction_ intentServiceHandledAction = new IntentServiceHandledAction_(); + IntentServiceHandledAction intentServiceHandledAction = new IntentServiceHandledAction_(); intentServiceHandledAction.onHandleIntent(intent); - assertThat(IntentServiceHandledAction_.actionForTestHandled).isTrue(); + assertThat(IntentServiceHandledAction_.actionForTestHandled).isEqualTo("test"); } } diff --git a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java index 11708da32b..b22a1f5621 100644 --- a/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java +++ b/AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/eintentservice/IntentServiceHandledAction.java @@ -15,8 +15,10 @@ */ package org.androidannotations.test15.eintentservice; +import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EIntentService; import org.androidannotations.annotations.ServiceAction; +import org.androidannotations.test15.ebean.EnhancedClass; import android.app.IntentService; import android.content.Intent; @@ -24,25 +26,28 @@ @EIntentService public class IntentServiceHandledAction extends IntentService { - public static boolean actionForTestHandled = false; + public static Object actionForTestHandled = null; + + @Bean + EnhancedClass dependency; public IntentServiceHandledAction() { super(IntentServiceHandledAction.class.getSimpleName()); } @ServiceAction - void myActionForTests() { - actionForTestHandled = true; + void myAction() { + actionForTestHandled = new Object(); } @ServiceAction - void actionOne() { - actionForTestHandled = false; + void MyActionOneParam(String valueString) { + actionForTestHandled = valueString; } - @ServiceAction - void myActionTwo() { - actionForTestHandled = false; + @ServiceAction("myAction") + void myActionTwoParams(String valueString, long valueLong) { + actionForTestHandled = valueString; } @Override