From 6c3a7c55dde32e23738945d6eb46119772860f04 Mon Sep 17 00:00:00 2001 From: Artyom Drozdov Date: Sun, 3 Nov 2013 15:36:59 +0400 Subject: [PATCH 1/3] add: support for the execution thread control --- .../annotations/SupposeBackground.java | 14 +++ .../annotations/SupposeUiThread.java | 16 +++ .../AndroidAnnotationProcessor.java | 15 ++- .../api/BackgroundExecutor.java | 97 ++++++++++++++++++- .../SupposeBackgroundProcessor.java | 51 ++++++++++ .../processing/SupposeUiThreadProcessor.java | 41 ++++++++ .../validation/SupposeValidator.java | 53 ++++++++++ 7 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeBackground.java create mode 100644 AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeUiThread.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeBackgroundProcessor.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeUiThreadProcessor.java create mode 100644 AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/SupposeValidator.java diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeBackground.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeBackground.java new file mode 100644 index 0000000000..490f196ae4 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeBackground.java @@ -0,0 +1,14 @@ +package org.androidannotations.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface SupposeBackground { + + String[] serial() default {}; + +} diff --git a/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeUiThread.java b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeUiThread.java new file mode 100644 index 0000000000..f1a7dfae13 --- /dev/null +++ b/AndroidAnnotations/androidannotations-api/src/main/java/org/androidannotations/annotations/SupposeUiThread.java @@ -0,0 +1,16 @@ +package org.androidannotations.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Ensures that method is called from the UI thread. If it is not, then + * {@link java.lang.IllegalStateException} will be thrown (by default). + * //TODO how to change default + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface SupposeUiThread { +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java index 1f9c9109cd..dcc02aa92d 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java @@ -89,6 +89,8 @@ import org.androidannotations.annotations.SeekBarTouchStart; import org.androidannotations.annotations.SeekBarTouchStop; import org.androidannotations.annotations.ServiceAction; +import org.androidannotations.annotations.SupposeBackground; +import org.androidannotations.annotations.SupposeUiThread; import org.androidannotations.annotations.SystemService; import org.androidannotations.annotations.TextChange; import org.androidannotations.annotations.Touch; @@ -193,6 +195,8 @@ import org.androidannotations.processing.ServiceActionProcessor; import org.androidannotations.processing.SharedPrefProcessor; import org.androidannotations.processing.SubscribeProcessor; +import org.androidannotations.processing.SupposeBackgroundProcessor; +import org.androidannotations.processing.SupposeUiThreadProcessor; import org.androidannotations.processing.SystemServiceProcessor; import org.androidannotations.processing.TextChangeProcessor; import org.androidannotations.processing.TouchProcessor; @@ -255,6 +259,7 @@ import org.androidannotations.validation.OptionsMenuValidator; import org.androidannotations.validation.OrmLiteDaoValidator; import org.androidannotations.validation.PrefValidator; +import org.androidannotations.validation.SupposeValidator; import org.androidannotations.validation.ProduceValidator; import org.androidannotations.validation.ResValidator; import org.androidannotations.validation.RestServiceValidator; @@ -521,6 +526,8 @@ private ModelValidator buildModelValidator(IRClass rClass, AndroidSystemServices modelValidator.register(new ProduceValidator(processingEnv)); modelValidator.register(new RunnableValidator(UiThread.class.getName(), processingEnv)); modelValidator.register(new RunnableValidator(Background.class.getName(), processingEnv)); + modelValidator.register(new SupposeValidator(SupposeUiThread.class.getName(), processingEnv)); + modelValidator.register(new SupposeValidator(SupposeBackground.class.getName(), processingEnv)); modelValidator.register(new InstanceStateValidator(processingEnv)); modelValidator.register(new OrmLiteDaoValidator(processingEnv, rClass)); modelValidator.register(new HttpsClientValidator(processingEnv, rClass)); @@ -620,9 +627,11 @@ private ModelProcessor buildModelProcessor(IRClass rClass, AndroidSystemServices } modelProcessor.register(new SubscribeProcessor()); modelProcessor.register(new ProduceProcessor()); - modelProcessor.register(new UiThreadProcessor()); + modelProcessor.register(new UiThreadProcessor()); + modelProcessor.register(new SupposeUiThreadProcessor()); modelProcessor.register(new BackgroundProcessor()); - modelProcessor.register(new AfterInjectProcessor()); + modelProcessor.register(new SupposeBackgroundProcessor()); + modelProcessor.register(new AfterInjectProcessor()); modelProcessor.register(new InstanceStateProcessor(processingEnv)); modelProcessor.register(new HttpsClientProcessor(rClass)); modelProcessor.register(new OnActivityResultProcessor(processingEnv, rClass)); @@ -677,8 +686,10 @@ public Set getSupportedAnnotationTypes() { Touch.class, // ItemSelect.class, // UiThread.class, // + SupposeUiThread.class, Transactional.class, // Background.class, // + SupposeBackground.class, Extra.class, // SystemService.class, // SharedPref.class, // diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java index 450708c701..4cee3e842c 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java @@ -16,6 +16,7 @@ package org.androidannotations.api; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -25,6 +26,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import android.os.Looper; import android.util.Log; public class BackgroundExecutor { @@ -34,7 +36,32 @@ public class BackgroundExecutor { public static Executor DEFAULT_EXECUTOR = Executors.newScheduledThreadPool(2 * Runtime.getRuntime().availableProcessors()); private static Executor executor = DEFAULT_EXECUTOR; + public static final WrongThreadListener DEFAULT_WRONG_THREAD_LISTENER = new WrongThreadListener() { + @Override + public void onUiExpected() { + throw new IllegalStateException("Method invocation is expected from the UI thread"); + } + + @Override + public void onBgExpected(String... expectedSerials) { + if (expectedSerials.length == 0) { + throw new IllegalStateException("Method invocation is expected from a background thread, but it was called from the UI thread"); + } + throw new IllegalStateException("Method invocation is expected from one of serials " + Arrays.toString(expectedSerials) + ", but it was called from the UI thread"); + } + + @Override + public void onWrongBgSerial(String currentSerial, String... expectedSerials) { + if (currentSerial == null) { + currentSerial = "anonymous"; + } + throw new IllegalStateException("Method invocation is expected from one of serials " + Arrays.toString(expectedSerials) + ", but it was called from " + currentSerial + " serial"); + } + }; + private static WrongThreadListener wrongThreadListener = DEFAULT_WRONG_THREAD_LISTENER; + private static final List tasks = new ArrayList(); + private static final ThreadLocal currentSerial = new ThreadLocal(); /** * Execute a runnable after the given delay. @@ -181,9 +208,17 @@ public static void execute(Runnable runnable, String id, String serial) { * @param executor * the new executor */ - public static void setExecutor(Executor executor) { - BackgroundExecutor.executor = executor; - } + public static void setExecutor(Executor executor) { + BackgroundExecutor.executor = executor; + } + + /** + * Change the WrongThreadListener. + * @param listener the new WrongThreadListener + */ + public static void setWrongThreadListener(WrongThreadListener listener) { + BackgroundExecutor.wrongThreadListener = listener; + } /** * Cancel all tasks having the specified id. @@ -219,6 +254,50 @@ public static synchronized void cancelAll(String id, boolean mayInterruptIfRunni } } + /** + * Checks if current thread is UI and notifies + * {@link BackgroundExecutor.WrongThreadListener#onUiExpected()} if it doesn't. + */ + public static void checkUiThread() { + if (Looper.getMainLooper().getThread() != Thread.currentThread()) { + wrongThreadListener.onUiExpected(); + } + } + + /** + * Check if current thread is a background thread and, optionally, restrict it + * with passed serials. If no serials passed and current thread is UI, then + * {@link WrongThreadListener#onBgExpected(String...)} will be called. + * If current thread is not UI and serials list is empty, then method just returns. + * Otherwise, if method was called not during {@link Task} execution or the task has no + * serial, then {@link WrongThreadListener#onWrongBgSerial(String, String...)} will be called + * with null for the first parameter. If task has serial but passed serials don't contain that, + * then {@link WrongThreadListener#onWrongBgSerial(String, String...)} will be called with + * task's serial for the first parameter. + * + * @param serials (optional) list of allowed serials + */ + public static void checkBgThread(String... serials) { + if (Looper.getMainLooper().getThread() == Thread.currentThread()) { + wrongThreadListener.onBgExpected(serials); + return; + } + if (serials.length == 0) { + return; + } + String current = currentSerial.get(); + if (current == null) { + wrongThreadListener.onWrongBgSerial(null, serials); + return; + } + for (String serial : serials) { + if (serial.equals(current)) { + return; + } + } + wrongThreadListener.onWrongBgSerial(current, serials); + } + /** * Indicates whether a task with the specified serial has been * submitted to the executor. @@ -299,6 +378,7 @@ public void run() { } try { + currentSerial.set(serial); execute(); } finally { /* handle next tasks */ @@ -313,6 +393,7 @@ private void postExecute() { /* nothing to do */ return; } + currentSerial.set(null); synchronized (BackgroundExecutor.class) { /* execution complete */ tasks.remove(this); @@ -333,4 +414,14 @@ private void postExecute() { } + /** + * A callback interface to be notified when current thread, in which method has been invoked, + * is wrong. + * @see #setWrongThreadListener(WrongThreadListener) + */ + public static interface WrongThreadListener { + void onUiExpected(); + void onBgExpected(String... expectedSerials); + void onWrongBgSerial(String currentSerial, String... expectedSerials); + } } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeBackgroundProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeBackgroundProcessor.java new file mode 100644 index 0000000000..5d6de6df87 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeBackgroundProcessor.java @@ -0,0 +1,51 @@ +package org.androidannotations.processing; + +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JMethod; + +import org.androidannotations.annotations.SupposeBackground; +import org.androidannotations.api.BackgroundExecutor; +import org.androidannotations.helper.APTCodeModelHelper; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; + +import static com.sun.codemodel.JExpr.lit; + +public class SupposeBackgroundProcessor implements DecoratingElementProcessor { + + private static final String METHOD_CHECK_BG_THREAD = "checkBgThread"; + + private final APTCodeModelHelper helper = new APTCodeModelHelper(); + + @Override + public String getTarget() { + return SupposeBackground.class.getName(); + } + + @Override + public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws JClassAlreadyExistsException { + + ExecutableElement executableElement = (ExecutableElement) element; + + holder.generateApiClass(element, BackgroundExecutor.class); + + JMethod delegatingMethod = helper.overrideAnnotatedMethod(executableElement, holder); + helper.removeBody(delegatingMethod); + + JClass bgExecutor = holder.refClass(BackgroundExecutor.class); + + SupposeBackground annotation = element.getAnnotation(SupposeBackground.class); + String[] serial = annotation.serial(); + JInvocation invocation = bgExecutor.staticInvoke(METHOD_CHECK_BG_THREAD); + for (String s : serial) { + invocation.arg(lit(s)); + } + + delegatingMethod.body().add(invocation); + helper.callSuperMethod(delegatingMethod, holder, delegatingMethod.body()); + } +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeUiThreadProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeUiThreadProcessor.java new file mode 100644 index 0000000000..d89ddc9dd6 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/SupposeUiThreadProcessor.java @@ -0,0 +1,41 @@ +package org.androidannotations.processing; + +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JMethod; + +import org.androidannotations.annotations.SupposeUiThread; +import org.androidannotations.api.BackgroundExecutor; +import org.androidannotations.helper.APTCodeModelHelper; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; + +public class SupposeUiThreadProcessor implements DecoratingElementProcessor { + + private static final String METHOD_CHECK_UI_THREAD = "checkUiThread"; + + private final APTCodeModelHelper helper = new APTCodeModelHelper(); + + @Override + public String getTarget() { + return SupposeUiThread.class.getName(); + } + + @Override + public void process(Element element, JCodeModel codeModel, EBeanHolder holder) throws JClassAlreadyExistsException { + + ExecutableElement executableElement = (ExecutableElement) element; + + holder.generateApiClass(element, BackgroundExecutor.class); + + JMethod delegatingMethod = helper.overrideAnnotatedMethod(executableElement, holder); + helper.removeBody(delegatingMethod); + + JClass bgExecutor = holder.refClass(BackgroundExecutor.class); + + delegatingMethod.body().staticInvoke(bgExecutor, METHOD_CHECK_UI_THREAD); + helper.callSuperMethod(delegatingMethod, holder, delegatingMethod.body()); + } +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/SupposeValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/SupposeValidator.java new file mode 100644 index 0000000000..a0de0eadd5 --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/SupposeValidator.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.validation; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; + +import org.androidannotations.helper.TargetAnnotationHelper; +import org.androidannotations.helper.ValidatorHelper; +import org.androidannotations.model.AnnotationElements; + +public class SupposeValidator implements ElementValidator { + + private ValidatorHelper validatorHelper; + private String annotationName; + + public SupposeValidator(String annotationName, ProcessingEnvironment processingEnv) { + this.annotationName = annotationName; + TargetAnnotationHelper annotationHelper = new TargetAnnotationHelper(processingEnv, getTarget()); + validatorHelper = new ValidatorHelper(annotationHelper); + } + + @Override + public String getTarget() { + return annotationName; + } + + @Override + public boolean validate(Element element, AnnotationElements validatedElements) { + + IsValid valid = new IsValid(); + + validatorHelper.enclosingElementHasEnhancedComponentAnnotation(element, validatedElements, valid); + + validatorHelper.isNotPrivate(element, valid); + + return valid.isValid(); + } + +} From 8fbd0a09bcd448288e61a885c9c1216f9d5898da Mon Sep 17 00:00:00 2001 From: Artyom Drozdov Date: Thu, 21 Nov 2013 20:50:39 +0400 Subject: [PATCH 2/3] add: better support for generics --- .../com/sun/codemodel/JSuperWildcard.java | 69 +++++++++++++++++++ .../AndroidAnnotationProcessor.java | 2 +- .../helper/APTCodeModelHelper.java | 54 +++++++++------ .../processing/BeanProcessor.java | 6 +- .../processing/EBeanHolder.java | 4 ++ .../processing/EBeanProcessor.java | 15 +++- .../processing/EBeansHolder.java | 12 +++- .../processing/ModelProcessor.java | 5 +- .../ActivityWithBackgroundMethod.java | 8 +++ 9 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java diff --git a/AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java b/AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java new file mode 100644 index 0000000000..fbff4716da --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/com/sun/codemodel/JSuperWildcard.java @@ -0,0 +1,69 @@ +package com.sun.codemodel; + +import java.util.Iterator; +import java.util.List; + +public class JSuperWildcard extends JClass { + + private final JClass bound; + + public JSuperWildcard(JClass bound) { + super(bound.owner()); + this.bound = bound; + } + + public String name() { + return "? super "+ bound.name(); + } + + public String fullName() { + return "? super "+ bound.fullName(); + } + + public JPackage _package() { + return null; + } + + /** + * Returns the class bound of this variable. + * + *

+ * If no bound is given, this method returns {@link Object}. + */ + public JClass _extends() { + if(bound !=null) + return bound; + else + return owner().ref(Object.class); + } + + /** + * Returns the interface bounds of this variable. + */ + public Iterator _implements() { + return bound._implements(); + } + + public boolean isInterface() { + return false; + } + + public boolean isAbstract() { + return false; + } + + protected JClass substituteParams(JTypeVar[] variables, List bindings) { + JClass nb = bound.substituteParams(variables,bindings); + if(nb== bound) + return this; + else + return nb.wildcard(); + } + + public void generate(JFormatter f) { + if(bound._extends()==null) + f.p("?"); // instead of "? extends Object" + else + f.p("? super").g(bound); + } +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java index dcc02aa92d..62124ded06 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java @@ -550,7 +550,7 @@ private boolean traceActivated() { private ProcessResult processAnnotations(AnnotationElements validatedModel, IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) throws ProcessingException, Exception { timeStats.start("Process Annotations"); ModelProcessor modelProcessor = buildModelProcessor(rClass, androidSystemServices, androidManifest, validatedModel); - ProcessResult processResult = modelProcessor.process(validatedModel); + ProcessResult processResult = modelProcessor.process(processingEnv, validatedModel); timeStats.stop("Process Annotations"); return processResult; } 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 ac63fb71d3..f9b9f14fc0 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/APTCodeModelHelper.java @@ -37,6 +37,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; @@ -59,6 +60,7 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JStatement; +import com.sun.codemodel.JSuperWildcard; import com.sun.codemodel.JType; import com.sun.codemodel.JTypeVar; import com.sun.codemodel.JVar; @@ -86,15 +88,18 @@ 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(); + TypeMirror bound = wildcardType.getExtendsBound(); + if (bound == null) { + bound = wildcardType.getSuperBound(); + if (bound == null) { + throw new IllegalArgumentException("There are should be 'extends' or 'super' in the wildcard"); + } + return superWildcard(typeMirrorToJClass(bound, holder)); + } - return typeMirrorToJClass(extendsBound, holder).wildcard(); + return typeMirrorToJClass(bound, holder).wildcard(); } else if (type instanceof ArrayType) { ArrayType arrayType = (ArrayType) type; @@ -106,7 +111,11 @@ public JClass typeMirrorToJClass(TypeMirror type, EBeanHolder holder) { } } - public static class Parameter { + private JClass superWildcard(final JClass bound) { + return new JSuperWildcard(bound); + } + + public static class Parameter { public final String name; public final JClass jClass; @@ -118,16 +127,19 @@ public Parameter(String name, JClass jClass) { public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, EBeanHolder holder) { + TypeMirror gen = holder.beans().getOriginatingElements().getClassOriginatingElements(holder.generatedClass.fullName())[0].asType(); + ExecutableType generalisedElement = (ExecutableType) holder.beans().getProcessingEnv().getTypeUtils().asMemberOf((DeclaredType) gen, executableElement); + String methodName = executableElement.getSimpleName().toString(); - JClass returnType = typeMirrorToJClass(executableElement.getReturnType(), holder); + JClass returnType = typeMirrorToJClass(generalisedElement.getReturnType(), holder); List parameters = new ArrayList(); - for (VariableElement parameter : executableElement.getParameters()) { - String parameterName = parameter.getSimpleName().toString(); - JClass parameterClass = typeMirrorToJClass(parameter.asType(), holder); - parameters.add(new Parameter(parameterName, parameterClass)); - } + for (int i = 0; i < executableElement.getParameters().size(); i++) { + String parameterName = executableElement.getParameters().get(i).getSimpleName().toString(); + JClass parameterClass = typeMirrorToJClass(generalisedElement.getParameterTypes().get(i), holder); + parameters.add(new Parameter(parameterName, parameterClass)); + } JMethod existingMethod = findAlreadyGeneratedMethod(holder.generatedClass, methodName, parameters); @@ -139,12 +151,13 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, EBea method.annotate(Override.class); List methodParameters = new ArrayList(); - for (VariableElement parameter : executableElement.getParameters()) { - String parameterName = parameter.getSimpleName().toString(); - JClass parameterClass = typeMirrorToJClass(parameter.asType(), holder); - JVar param = method.param(JMod.FINAL, parameterClass, parameterName); - methodParameters.add(param); - } + for (int i = 0; i < executableElement.getParameters().size(); i++) { + String parameterName = executableElement.getParameters().get(i).getSimpleName().toString(); + JClass parameterClass = typeMirrorToJClass(generalisedElement.getParameterTypes().get(i), holder); + + JVar param = method.param(JMod.FINAL, parameterClass, parameterName); + methodParameters.add(param); + } for (TypeMirror superThrownType : executableElement.getThrownTypes()) { JClass thrownType = typeMirrorToJClass(superThrownType, holder); @@ -156,7 +169,7 @@ public JMethod overrideAnnotatedMethod(ExecutableElement executableElement, EBea return method; } - private JMethod findAlreadyGeneratedMethod(JDefinedClass definedClass, String methodName, List parameters) { + private JMethod findAlreadyGeneratedMethod(JDefinedClass definedClass, String methodName, List parameters) { method: for (JMethod method : definedClass.methods()) { if (method.name().equals(methodName) && method.params().size() == parameters.size()) { int i = 0; @@ -477,4 +490,5 @@ private JFieldVar addIntentBuilderFragmentConstructor(EBeanHolder holder, JClass constructorBody.assign(holder.intentField, _new(holder.classes().INTENT).arg(contextField).arg(holder.generatedClass.dotclass())); return fragmentField; } + } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/BeanProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/BeanProcessor.java index c148b0ce77..de4000ad77 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/BeanProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/BeanProcessor.java @@ -63,6 +63,10 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { String fieldName = element.getSimpleName().toString(); String typeQualifiedName = elementType.toString(); + int genericIndex = typeQualifiedName.indexOf("<"); + if (genericIndex >= 0) { + typeQualifiedName = typeQualifiedName.substring(0, genericIndex); + } JClass injectedClass = holder.refClass(typeQualifiedName + GENERATION_SUFFIX); @@ -77,7 +81,7 @@ public void process(Element element, JCodeModel codeModel, EBeanHolder holder) { body = body._if(beanField.eq(_null()))._then(); } - JInvocation getInstance = injectedClass.staticInvoke(GET_INSTANCE_METHOD_NAME).arg(holder.contextRef); + JInvocation getInstance = injectedClass.erasure().staticInvoke(GET_INSTANCE_METHOD_NAME).arg(holder.contextRef); body.assign(beanField, getInstance); } 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 7ca53b6c71..caacd5d762 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanHolder.java @@ -217,4 +217,8 @@ public void wrapInitWithNotifier() { resetPreviousNotifier(initBlock, previousNotifier); } + public EBeansHolder beans() { + return eBeansHolder; + } + } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java index 899a24500b..cf7b7e1505 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java @@ -28,6 +28,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.util.ElementFilter; import org.androidannotations.annotations.EBean; @@ -57,15 +58,25 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo TypeElement typeElement = (TypeElement) element; - String eBeanQualifiedName = typeElement.getQualifiedName().toString(); + StringBuilder eBeanQualifiedName = new StringBuilder(typeElement.getQualifiedName()); String generatedBeanQualifiedName = eBeanQualifiedName + GENERATION_SUFFIX; JDefinedClass generatedClass = codeModel._class(PUBLIC | FINAL, generatedBeanQualifiedName, ClassType.CLASS); + if (!typeElement.getTypeParameters().isEmpty()) { + eBeanQualifiedName.append("<"); + for (TypeParameterElement typeParam : typeElement.getTypeParameters()) { + generatedClass.generify(typeParam.getSimpleName().toString()); + eBeanQualifiedName.append(typeParam.getSimpleName()).append(", "); + } + int l = eBeanQualifiedName.length(); + eBeanQualifiedName.replace(l - 2, l, ">"); + } + EBeanHolder holder = eBeansHolder.create(element, EBean.class, generatedClass); - JClass eBeanClass = codeModel.directClass(eBeanQualifiedName); + JClass eBeanClass = codeModel.directClass(eBeanQualifiedName.toString()); holder.generatedClass._extends(eBeanClass); 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 e0d2a2f35e..83c4161678 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeansHolder.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import org.androidannotations.helper.CanonicalNameConstants; @@ -36,7 +37,11 @@ public class EBeansHolder { - public class Classes { + public ProcessingEnvironment getProcessingEnv() { + return processingEnv; + } + + public class Classes { /* * Java @@ -170,7 +175,10 @@ public class Classes { private final OriginatingElements originatingElements = new OriginatingElements(); - public EBeansHolder(JCodeModel codeModel) { + private final ProcessingEnvironment processingEnv; + + public EBeansHolder(ProcessingEnvironment processingEnv, JCodeModel codeModel) { + this.processingEnv = processingEnv; this.codeModel = codeModel; classes = new Classes(); refClass(CanonicalNameConstants.STRING); diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java index 9aad843c6a..5efa7ecd4f 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Set; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; @@ -60,11 +61,11 @@ public void register(GeneratingElementProcessor processor) { typeProcessors.add(processor); } - public ProcessResult process(AnnotationElements validatedModel) throws ProcessingException, Exception { + public ProcessResult process(ProcessingEnvironment processingEnv, AnnotationElements validatedModel) throws ProcessingException, Exception { JCodeModel codeModel = new JCodeModel(); - EBeansHolder eBeansHolder = new EBeansHolder(codeModel); + EBeansHolder eBeansHolder = new EBeansHolder(processingEnv, codeModel); for (GeneratingElementProcessor processor : typeProcessors) { String annotationName = processor.getTarget(); diff --git a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/generation/ActivityWithBackgroundMethod.java b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/generation/ActivityWithBackgroundMethod.java index bf48890dd8..12d0b2719e 100644 --- a/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/generation/ActivityWithBackgroundMethod.java +++ b/AndroidAnnotations/androidannotations/src/test/java/org/androidannotations/generation/ActivityWithBackgroundMethod.java @@ -17,9 +17,12 @@ import org.androidannotations.annotations.Background; import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.SupposeUiThread; import android.app.Activity; +import java.util.List; + @EActivity public class ActivityWithBackgroundMethod extends Activity { @@ -28,4 +31,9 @@ public void executingOnBackground() { } + @SupposeUiThread + void executingOnBackground(List arg) { + + } + } From e0dab57fed31b31b09691fde60fed1bf12208246 Mon Sep 17 00:00:00 2001 From: Artyom Drozdov Date: Thu, 21 Nov 2013 21:30:13 +0400 Subject: [PATCH 3/3] fix: StackOverflowError for singleton scope --- .../processing/EBeanProcessor.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java index cf7b7e1505..85ae9c49ef 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/EBeanProcessor.java @@ -105,6 +105,10 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo holder.initActivityRef = helper.castContextToActivity(holder, holder.initIfActivityBody); } + EBean eBeanAnnotation = element.getAnnotation(EBean.class); + EBean.Scope eBeanScope = eBeanAnnotation.scope(); + boolean hasSingletonScope = eBeanScope == EBean.Scope.Singleton; + { // Constructor @@ -124,13 +128,11 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo constructorBody.assign(contextField, constructorContextParam); - constructorBody.invoke(init); + if (!hasSingletonScope) { + constructorBody.invoke(init); + } } - EBean eBeanAnnotation = element.getAnnotation(EBean.class); - EBean.Scope eBeanScope = eBeanAnnotation.scope(); - boolean hasSingletonScope = eBeanScope == EBean.Scope.Singleton; - { // Factory method @@ -152,6 +154,7 @@ public void process(Element element, JCodeModel codeModel, EBeansHolder eBeansHo ._then(); JVar previousNotifier = holder.replacePreviousNotifierWithNull(creationBlock); creationBlock.assign(instanceField, _new(holder.generatedClass).arg(factoryMethodContextParam.invoke("getApplicationContext"))); + creationBlock.invoke(instanceField, init); holder.resetPreviousNotifier(creationBlock, previousNotifier); factoryMethodBody._return(instanceField);