From aa2162b433064971533f493ec1f7de63dcadfbe3 Mon Sep 17 00:00:00 2001 From: Gurappa Chenchugari Date: Tue, 23 Jun 2020 07:47:11 +0200 Subject: [PATCH 1/5] Additional changes in annotations source from EP project --- .../java/com/olku/annotations/AutoProxy.java | 89 ++++++++++++++++++- .../annotations/AutoProxyClassGenerator.java | 15 +++- .../java/com/olku/annotations/RetBool.java | 6 +- .../java/com/olku/annotations/RetNumber.java | 6 +- .../java/com/olku/annotations/Returns.java | 8 +- 5 files changed, 109 insertions(+), 15 deletions(-) diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java index fccf9f5..034b5ff 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java @@ -1,8 +1,12 @@ -package com.olku.annotations; +package net.easypark.annotations; + +import androidx.annotation.NonNull; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Map; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.CLASS; @@ -12,10 +16,16 @@ @Target(value = TYPE) public @interface AutoProxy { /** Type generator class. */ - Class value() default AutoProxy.Default.class; + Class value() default Common.class; + + /** Ask generator to compose static methods for simplified instance creation. */ + int flags() default Flags.NONE; + + /** Default Yield return policy. */ + String defaultYield() default Returns.THROWS; /** Represents DEFAULT class generator. CommonClassGenerator class in processors module. */ - abstract class Default implements AutoProxyClassGenerator { + abstract class Common implements AutoProxyClassGenerator { } /** Customize return value of the method if call was canceled by predicate. Only for PUBLIC methods. */ @@ -34,4 +44,77 @@ abstract class Default implements AutoProxyClassGenerator { @Target(value = ElementType.METHOD) @interface AfterCall { } + + /** Special code generation modifier flags. */ + @interface Flags { + /** Default value. */ + int NONE = 0x0000; + /** Compose static method for easier instance creation. */ + int CREATOR = 0x0001; + /** Compose afterCall(...) method for all methods in class. */ + int AFTER_CALL = 0x0002; + /** Compose callByName(...) method that maps string name to a method call. */ + int MAPPING = 0x004; + } + + /** + * Default implementation of annotation interface. Used for simplified extraction of default values of + * annotation during code generation. + */ + @SuppressWarnings("ClassExplicitlyAnnotation") + abstract class DefaultAutoProxy implements AutoProxy { + @Override + public final Class value() { + return Common.class; + } + + @Override + public final int flags() { + return Flags.NONE; + } + + @Override + public final String defaultYield() { + return Returns.THROWS; + } + + /** create instance with field-to-default mapping. */ + @NonNull + public static Map asMap() { + final Map map = new HashMap<>(); + + // map field name to default value + map.put("value", Common.class); + map.put("flags", AutoProxy.Flags.NONE); + map.put("defaultYield", Returns.THROWS); + + return map; + } + } + + /** Default implementation of Yield annotation interface. Used for simplifying default annotations values extracting. */ + @SuppressWarnings("ClassExplicitlyAnnotation") + abstract class DefaultYield implements Yield { + @Override + public final Class adapter() { + return Returns.class; + } + + @Override + public final String value() { + return Returns.THROWS; + } + + /** create instance with field-to-default mapping. */ + @NonNull + public static Map asMap() { + final Map map = new HashMap<>(); + + map.put("adapter", Returns.class); + map.put("value", Returns.THROWS); + + return map; + } + + } } diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java index 895c2df..8abd638 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java @@ -1,9 +1,12 @@ -package com.olku.annotations; - -import javax.annotation.processing.Filer; +package net.easypark.annotations; import androidx.annotation.NonNull; +import java.util.List; + +import javax.annotation.processing.Filer; +import javax.lang.model.element.Element; + /** interface that all custom class generators should support. */ public interface AutoProxyClassGenerator { /** Compose java class. */ @@ -12,4 +15,10 @@ public interface AutoProxyClassGenerator { /** Get errors captured during processing. */ @NonNull String getErrors(); + + @NonNull + String getName(); + + @NonNull + List getOriginating(); } diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java b/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java index 6adcde5..372f75e 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java @@ -1,8 +1,8 @@ -package com.olku.annotations; +package net.easypark.annotations; -import java.lang.annotation.Retention; +import androidx.annotation.*; -import androidx.annotation.StringDef; +import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.SOURCE; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java b/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java index 842ffaa..8cc5249 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java @@ -1,8 +1,8 @@ -package com.olku.annotations; +package net.easypark.annotations; -import java.lang.annotation.Retention; +import androidx.annotation.*; -import androidx.annotation.StringDef; +import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.SOURCE; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java b/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java index 23d5d19..4694156 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java @@ -1,9 +1,9 @@ -package com.olku.annotations; - -import java.lang.annotation.Retention; +package net.easypark.annotations; import androidx.annotation.StringDef; +import java.lang.annotation.Retention; + import static java.lang.annotation.RetentionPolicy.SOURCE; /** @@ -20,6 +20,8 @@ String NULL = "null"; /** Direct call with ignore of predicate method result. */ String DIRECT = "direct"; + /** Default value is reference on current instance. */ + String THIS = "this"; } From e5dc1800c15974676b51897322af6a4ca7677bff Mon Sep 17 00:00:00 2001 From: Gurappa Chenchugari Date: Tue, 23 Jun 2020 08:02:48 +0200 Subject: [PATCH 2/5] Additional changes in generators, processors, rx sources from EP project --- .../com/olku/generators/RetBoolGenerator.java | 9 +- .../olku/generators/RetNumberGenerator.java | 11 +- .../com/olku/generators/ReturnsGenerator.java | 16 +- .../java/com/olku/generators/ReturnsPoet.java | 6 +- .../olku/processors/AutoProxyProcessor.java | 40 +- .../olku/processors/CommonClassGenerator.java | 514 +++++++++++------- .../com/olku/processors/TypeProcessor.java | 64 +-- .../main/java/com/olku/annotations/RetRx.java | 6 +- .../com/olku/generators/JustRxGenerator.java | 8 +- .../com/olku/generators/RetRxGenerator.java | 9 +- 10 files changed, 389 insertions(+), 294 deletions(-) diff --git a/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java index 25b4a1c..f6f2edd 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java @@ -1,10 +1,11 @@ -package com.olku.generators; +package net.easypark.generators; + +import androidx.annotation.*; -import com.olku.annotations.RetBool; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import androidx.annotation.NonNull; +import net.easypark.annotations.RetBool; /** Compose return types for boolean. */ public class RetBoolGenerator implements ReturnsPoet { @@ -28,6 +29,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - /* package */ static final RetBoolGenerator INSTANCE = new RetBoolGenerator(); + static final RetBoolGenerator INSTANCE = new RetBoolGenerator(); } } diff --git a/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java index 0aa95f0..786aeb1 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java @@ -1,14 +1,15 @@ -package com.olku.generators; +package net.easypark.generators; + +import androidx.annotation.*; -import com.olku.annotations.RetNumber; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; +import net.easypark.annotations.RetNumber; + import java.util.Map; import java.util.TreeMap; -import androidx.annotation.NonNull; - /** Compose return types for boolean. */ public class RetNumberGenerator implements ReturnsPoet { private static final Map> PRIMITIVES = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); @@ -55,6 +56,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - /* package */ static final RetNumberGenerator INSTANCE = new RetNumberGenerator(); + static final RetNumberGenerator INSTANCE = new RetNumberGenerator(); } } diff --git a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java index c08834b..adfde48 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java @@ -1,10 +1,11 @@ -package com.olku.generators; +package net.easypark.generators; + +import androidx.annotation.NonNull; -import com.olku.annotations.Returns; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import androidx.annotation.NonNull; +import net.easypark.annotations.Returns; /** Compose return types for boolean. */ public class ReturnsGenerator implements ReturnsPoet { @@ -40,10 +41,17 @@ public boolean compose(@NonNull final Type returnType, return true; } + // Builders support, return instance for chained calls + if (Returns.THIS.equals(type)) { + builder.addComment("return current instance"); + builder.addStatement("return ($T)this", returnType); + return true; + } + return false; } private static final class Singleton { - /* package */ static final ReturnsGenerator INSTANCE = new ReturnsGenerator(); + static final ReturnsGenerator INSTANCE = new ReturnsGenerator(); } } diff --git a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java index 0f45192..bcfe748 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java @@ -1,10 +1,10 @@ -package com.olku.generators; +package net.easypark.generators; + +import androidx.annotation.*; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import androidx.annotation.NonNull; - /** Code generator interface. */ public interface ReturnsPoet { /** Compose return statement for provided method based on return type and modifier. */ diff --git a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java index 167612b..f7fb6b2 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java @@ -1,13 +1,16 @@ -package com.olku.processors; +package net.easypark.processors; import com.google.auto.service.AutoService; -import com.olku.annotations.AutoProxy; -import com.olku.annotations.AutoProxyClassGenerator; + +import net.easypark.annotations.AutoProxy; +import net.easypark.annotations.AutoProxyClassGenerator; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -26,7 +29,11 @@ import static javax.tools.Diagnostic.Kind.ERROR; import static javax.tools.Diagnostic.Kind.NOTE; -/** Annotation processor. Generate proxy class for interface. */ +/** + * Annotation processor. Generate proxy class for interface. + * + * @see Incremental Annotation Processing + */ @AutoService(Processor.class) @SuppressWarnings("unused") public class AutoProxyProcessor extends AbstractProcessor { @@ -36,6 +43,8 @@ public class AutoProxyProcessor extends AbstractProcessor { private Types typesUtil; private Elements elementsUtil; private Filer filer; + /** @see Support incremental annotation processing */ + private HashMap> mMapGeneratedFileToOriginatingElements = new LinkedHashMap<>(); @Override public synchronized void init(final ProcessingEnvironment pe) { @@ -56,11 +65,6 @@ public Set getSupportedAnnotationTypes() { return annotations; } - @Override - public Set getSupportedOptions() { - return Collections.singleton("org.gradle.annotation.processing.aggregating"); - } - @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); @@ -85,9 +89,14 @@ public boolean process(final Set annotations, final Round final AutoProxyClassGenerator generator = tp.generator(); - if (!generator.compose(filer)) { -// logger.printMessage(ERROR, generator.getErrors()); - logger.printMessage(NOTE, generator.getErrors()); + if (generator.compose(filer)) { + if (IS_DEBUG) logger.printMessage(NOTE, "--- file: " + generator.getName()); + if (IS_DEBUG) logger.printMessage(NOTE, "--- elements: " + generator.getOriginating().size()); + if (IS_DEBUG) logger.printMessage(NOTE, "--- elements: " + generator.getOriginating().get(0).getSimpleName()); + + mMapGeneratedFileToOriginatingElements.put(generator.getName(), generator.getOriginating()); + } else { + logger.printMessage(IS_DEBUG ? ERROR : NOTE, generator.getErrors()); } } catch (Throwable e) { e.printStackTrace(new PrintWriter(errors)); @@ -100,10 +109,13 @@ public boolean process(final Set annotations, final Round } if (failed > 0) { -// logger.printMessage(NOTE, errors.toString()); logger.printMessage(ERROR, errors.toString()); } return true; } + + public HashMap> getMapGeneratedFileToOriginatingElements() { + return mMapGeneratedFileToOriginatingElements; + } } diff --git a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java index f4952c2..87d1f14 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java @@ -1,24 +1,14 @@ -package com.olku.processors; +package net.easypark.processors; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.StringDef; - -import com.olku.annotations.AutoProxy; -import com.olku.annotations.AutoProxyClassGenerator; -import com.olku.annotations.RetBool; -import com.olku.annotations.RetNumber; -import com.olku.annotations.Returns; -import com.olku.generators.RetBoolGenerator; -import com.olku.generators.RetNumberGenerator; -import com.olku.generators.ReturnsGenerator; -import com.olku.generators.ReturnsPoet; + import com.squareup.javapoet.AnnotationSpec; -import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; @@ -26,17 +16,27 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; +import net.easypark.annotations.AutoProxy; +import net.easypark.annotations.AutoProxy.Flags; +import net.easypark.annotations.AutoProxyClassGenerator; +import net.easypark.annotations.RetBool; +import net.easypark.annotations.RetNumber; +import net.easypark.annotations.Returns; +import net.easypark.generators.RetBoolGenerator; +import net.easypark.generators.RetNumberGenerator; +import net.easypark.generators.ReturnsGenerator; +import net.easypark.generators.ReturnsPoet; + import java.io.PrintWriter; import java.io.StringWriter; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.processing.Filer; @@ -47,70 +47,53 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; +import rx.functions.Func2; import sun.reflect.annotation.AnnotationParser; import static javax.tools.Diagnostic.Kind.NOTE; -/** - * Common Proxy Class generator. Class designed for inheritance. - */ +/** Common Proxy Class generator. Class designed for inheritance. */ @SuppressWarnings("WeakerAccess") public class CommonClassGenerator implements AutoProxyClassGenerator { public static boolean IS_DEBUG = AutoProxyProcessor.IS_DEBUG; - - /** - * Pre-call / predicate method name. - */ + /** Represents Generic non-NULL value. */ + public static final Attribute.Compound GLOBAL_AFTER_CALL = new Attribute.Compound(null, com.sun.tools.javac.util.List.nil()); + /** Pre-call / predicate method name. */ protected static final String PREDICATE = "predicate"; - protected static final String AFTERCALL = "afterCall"; - /** - * Annotation type name that is used for constants definition. - */ - protected static final String METHODS = "Methods"; - - /** - * Data type for processing. - */ + /** Method for capturing results of call. */ + protected static final String AFTER_CALL = "afterCall"; + /** static method creator. */ + protected static final String CREATOR = "create"; + /** mapping method that allows to dispatch call to a proper method by it name. */ + protected static final String MAPPER = "dispatchByName"; + + /** Data type for processing. */ protected final TypeProcessor type; - /** - * Writer for captured errors. - */ + /** Writer for captured errors. */ protected final StringWriter errors = new StringWriter(); - /** - * Resolved super type name. - */ + /** Resolved super type name. */ protected final TypeName superType; - /** - * Is any 'after calls' annotations found. - */ - protected final AtomicBoolean afterCalls = new AtomicBoolean(); - /** - * List of method names. - */ - protected final Set knownMethods = new TreeSet(String.CASE_INSENSITIVE_ORDER); - - //region Constructor - - /** - * Main constructor. - * - * @param type reference on data type processor. - */ + /** Is any 'after calls' annotations found. */ + protected final AtomicBoolean isAnyAfterCalls = new AtomicBoolean(); + /** Lookup of "method name"-to-"arguments line". */ + protected final Map mappedCalls = new TreeMap<>(); + /** Result file. */ + protected JavaFile javaFile; + + /** Main constructor. */ public CommonClassGenerator(@NonNull final TypeProcessor type) { this.type = type; superType = TypeName.get(this.type.element.asType()); } - //endregion - - //region Code generator - /** - * {@inheritDoc} - */ @Override public boolean compose(@NonNull final Filer filer) { try { + // is generation flag for forced afterCall set + final boolean hasAfterCalls = ((this.type.annotation.flags() & Flags.AFTER_CALL) == Flags.AFTER_CALL); + isAnyAfterCalls.set(hasAfterCalls); + // compose class final FieldSpec[] members = createMembers(); final TypeSpec.Builder classSpec = createClass(members); @@ -123,35 +106,58 @@ public boolean compose(@NonNull final Filer filer) { createMethods(classSpec); // if any after call annotation found in class/methods - if (afterCalls.get()) { + if (isAnyAfterCalls.get()) { classSpec.addMethod(createAfterCall().build()); } - createNamesOfMethods(classSpec); + // if allowed creator method + if ((this.type.annotation.flags() & Flags.CREATOR) == Flags.CREATOR) { + classSpec.addMethod(createCreator().build()); + } + + // if allowed mapper method + if ((this.type.annotation.flags() & Flags.MAPPING) == Flags.MAPPING) { + classSpec.addMethod(createMapper().build()); + } + + classSpec.addType(createMethodsMapper().build()); + + classSpec.addOriginatingElement(type.element); // save class to disk - final JavaFile javaFile = JavaFile.builder(type.packageName.toString(), classSpec.build()).build(); + javaFile = JavaFile.builder(type.packageName.toString(), classSpec.build()).build(); javaFile.writeTo(filer); - } catch (final Throwable ignored) { - ignored.printStackTrace(new PrintWriter(errors)); + } catch (final Throwable ex) { + ex.printStackTrace(new PrintWriter(errors)); return false; } return true; } - /** - * {@inheritDoc} - */ @Override @NonNull public String getErrors() { return errors.toString(); } - //endregion - //region Implementation + @NonNull + @Override + public String getName() { + if (null == javaFile) return ""; + + return javaFile.toJavaFileObject().getName(); + } + + @NonNull + @Override + public List getOriginating() { + if (null == javaFile) return Collections.emptyList(); + + return javaFile.typeSpec.originatingElements; + } + @NonNull protected FieldSpec[] createMembers() { final List fields = new ArrayList<>(); @@ -170,10 +176,19 @@ protected TypeSpec.Builder createClass(@NonNull final FieldSpec... members) { // TODO: mimic annotations of the super type + // @javax.annotation.Generated("AutoProxy Auto Generated Code") + builder.addAnnotation(AnnotationSpec.builder(javax.annotation.Generated.class) + .addMember("value", "$S", "AutoProxy Auto Generated Code") + .build()); + if (ElementKind.INTERFACE == type.element.getKind()) { builder.addSuperinterface(superType); + + copyTypeGenericVariables(builder); } else if (ElementKind.CLASS == type.element.getKind()) { builder.superclass(superType); + + copyTypeGenericVariables(builder); } else { final String message = "Unsupported data type: " + type.element.getKind() + ", " + type.elementType; errors.write(message + "\n"); @@ -188,6 +203,32 @@ protected TypeSpec.Builder createClass(@NonNull final FieldSpec... members) { return builder; } + /** copy generic parameters */ + private void copyTypeGenericVariables(final TypeSpec.Builder builder) { + if (!(superType instanceof ParameterizedTypeName)) return; + + ParameterizedTypeName ptn = (ParameterizedTypeName) superType; + + for (final TypeName typeName : ptn.typeArguments) { + if (!(typeName instanceof TypeVariableName)) continue; + + builder.addTypeVariable((TypeVariableName) typeName); + } + } + + /** copy generic parameters for method. */ + private void copyMethodGenericVariables(final MethodSpec.Builder builder) { + if (!(superType instanceof ParameterizedTypeName)) return; + + ParameterizedTypeName ptn = (ParameterizedTypeName) superType; + + for (final TypeName typeName : ptn.typeArguments) { + if (!(typeName instanceof TypeVariableName)) continue; + + builder.addTypeVariable((TypeVariableName) typeName); + } + } + @NonNull protected MethodSpec.Builder createConstructor() { final ParameterSpec.Builder param = ParameterSpec.builder(superType, "instance", Modifier.FINAL) @@ -201,12 +242,7 @@ protected MethodSpec.Builder createConstructor() { return builder; } - /** - * Create methods for provided class. - * - * @param classSpec instance of class specification builder - * @throws Exception give a change to exceptions in depth to deliver the real cause - */ + /** Compose proxy method for provided method definition. */ protected void createMethods(@NonNull final TypeSpec.Builder classSpec) throws Exception { // compose methods RuntimeException runtimeError = null; @@ -228,26 +264,15 @@ protected void createMethods(@NonNull final TypeSpec.Builder classSpec) throws E } } - /** - * Create predicate method declaration. - * - * @return instance of the method builder. - */ + /** Create predicate method declaration. */ @NonNull protected MethodSpec.Builder createPredicate() { // TODO: resolve potential name conflict - final String methodName = PREDICATE; - final MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName); + final MethodSpec.Builder builder = MethodSpec.methodBuilder(PREDICATE); builder.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT); builder.returns(boolean.class); - - final ParameterSpec pMethodNames = ParameterSpec.builder(String.class, "methodName", Modifier.FINAL) - .addAnnotation(AnnotationSpec.builder(ClassName.bestGuess(METHODS)).build()) - .addAnnotation(AnnotationSpec.builder(NonNull.class).build()) - .build(); - - builder.addParameter(pMethodNames); + builder.addParameter(String.class, "methodName", Modifier.FINAL); // varargs builder.varargs(true); @@ -256,38 +281,180 @@ protected MethodSpec.Builder createPredicate() { return builder; } - /** - * Create afterCall method declaration. - * - * @return instance of the method builder. - */ + /** Create afterCall method declaration. */ @NonNull protected MethodSpec.Builder createAfterCall() { - final MethodSpec.Builder builder = MethodSpec.methodBuilder(AFTERCALL); + final MethodSpec.Builder builder = MethodSpec.methodBuilder(AFTER_CALL); builder.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT); builder.addTypeVariable(TypeVariableName.get("R", Object.class)); builder.returns(TypeVariableName.get("R")); - final ParameterSpec pMethodNames = ParameterSpec.builder(String.class, "methodName", Modifier.FINAL) - .addAnnotation(AnnotationSpec.builder(ClassName.bestGuess(METHODS)).build()) - .addAnnotation(AnnotationSpec.builder(NonNull.class).build()) - .build(); - builder.addParameter(pMethodNames); + builder.addParameter(String.class, "methodName", Modifier.FINAL); builder.addParameter(TypeVariableName.get("R"), "result", Modifier.FINAL); return builder; } + /** Special static method for simplified class instance creation. */ + @NonNull + protected MethodSpec.Builder createCreator() { +// Output: +// public static UiChange creator(@NonNull final UiChange instance, Func2 action){ +// return new Proxy_UiChange(instance) { +// @Override +// public boolean predicate(final String methodName, final Object... args) { +// return action.call(methodName, args); +// } +// @Override +// public R afterCall(final String methodName, final R result) { +// return result; +// }; +// }; +// } + + final MethodSpec.Builder builder = MethodSpec.methodBuilder(CREATOR); + builder.addModifiers(Modifier.PUBLIC, Modifier.STATIC); + + copyMethodGenericVariables(builder); + builder.returns(superType); + + builder.addParameter(superType, "instance", Modifier.FINAL); + builder.addParameter(ParameterizedTypeName.get(Func2.class, String.class, Object[].class, Boolean.class), "action", Modifier.FINAL); + + builder.addCode("" + + "return new $L(instance) {\n" + + " @Override\n" + + " public boolean predicate(final String methodName, final Object... args) {\n" + + " return action.call(methodName, args);\n" + + " }\n" + (!isAnyAfterCalls.get() ? "" : + " @Override\n" + + " public R afterCall(final String methodName, final R result) {\n" + + " return result;\n" + + " };\n") + + "};\n", "Proxy_" + type.flatClassName); + + return builder; + } + + @NonNull + protected MethodSpec.Builder createMapper() { + final MethodSpec.Builder builder = MethodSpec.methodBuilder(MAPPER); + builder.addModifiers(Modifier.PUBLIC); + + builder.addTypeVariable(TypeVariableName.get("R", Object.class)); + builder.returns(TypeVariableName.get("R")); + + builder.addParameter(String.class, "methodName", Modifier.FINAL); + + // varargs + builder.varargs(true); + builder.addParameter(Object[].class, "args", Modifier.FINAL); + + for (final String name : mappedCalls.keySet()) { + final Symbol.MethodSymbol ms = mappedCalls.get(name); + final Type returnType = ms.getReturnType(); + final boolean hasReturn = returnType.getKind() != TypeKind.VOID; + final String methodName = ms.getSimpleName().toString(); + final String params = composeCallParamsFromArray(ms, "args"); + + builder.beginControlFlow("if(M.$L.equals(methodName))", name); + if (hasReturn) { + builder.addStatement("return (R)this.inner.$N($L)", methodName, params); + } else { + builder.addStatement("this.inner.$N($L)", methodName, params); + builder.addStatement("return (R)null"); + } + builder.endControlFlow(); + } + + // fallback + builder.addCode("return (R)null;\n"); + + return builder; + } + + /** Compose inner interface with all method names. */ + @NonNull + protected TypeSpec.Builder createMethodsMapper() { + final TypeSpec.Builder builder = TypeSpec.interfaceBuilder("M") + .addModifiers(Modifier.PUBLIC); + + for (final String name : mappedCalls.keySet()) { + final Symbol.MethodSymbol ms = mappedCalls.get(name); + final String methodName = ms.getSimpleName().toString(); + + builder.addField(FieldSpec.builder(String.class, name) + .addJavadoc("{@link #$L($L)}", methodName, composeCallParamsTypes(ms)) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .initializer("$S", name) + .build()); + } + return builder; + } + + /** Compose list of method parameters data types, comma separated. */ + @NonNull + private String composeCallParamsTypes(@NonNull final Symbol.MethodSymbol ms) { + String delimiter = ""; + final StringBuilder result = new StringBuilder(); + + final com.sun.tools.javac.util.List parameters = ms.getParameters(); + + for (int i = 0, len = parameters.size(); i < len; i++) { + final Symbol.VarSymbol param = parameters.get(i); + final TypeName paramType = TypeName.get(param.asType()); + + // compose parameters list for forwarding + result.append(delimiter).append(paramType.toString()); + delimiter = ", "; + } + + return result.toString(); + } + + /** Compose extracting of method parameters from vararg array with data type casting. */ + @NonNull + private String composeCallParamsFromArray(@NonNull final Symbol.MethodSymbol ms, @NonNull final String arrayName) { + String delimiter = ""; + final StringBuilder result = new StringBuilder(); + + final com.sun.tools.javac.util.List parameters = ms.getParameters(); + + for (int i = 0, len = parameters.size(); i < len; i++) { + final Symbol.VarSymbol param = parameters.get(i); + + // mimic parameter of the method: name, type, modifiers + final TypeName paramType = TypeName.get(param.asType()); + final String parameterName = param.name.toString(); + final String parameterExtract = String.format(Locale.US, "(%s)%s[%d] /*%s*/", paramType.toString(), arrayName, i, parameterName); + + // compose parameters list for forwarding + result.append(delimiter).append(parameterExtract); + delimiter = ", "; + } + + return result.toString(); + } + + /** Create override method of the proxy class. */ @NonNull protected MethodSpec.Builder createMethod(final Symbol.MethodSymbol ms) throws Exception { +// Output: +// @NonNull +// public final UiChange translateX(final int diff) { +// if (!predicate( M.translateX_diff, diff )) { +// // return current instance +// return (UiChange)this; +// } +// return this.inner.translateX(diff); +// } + final String methodName = ms.getSimpleName().toString(); final MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName); - this.knownMethods.add(methodName); - builder.addModifiers(Modifier.FINAL, Modifier.PUBLIC); // extract annotations of return type / method. copy all, except @Yield & @AfterCall @@ -295,7 +462,7 @@ protected MethodSpec.Builder createMethod(final Symbol.MethodSymbol ms) throws E // extract our own annotations final Attribute.Compound yield = findYieldMethodAnnotation(ms); - final Attribute.Compound after = findAfterMethodAnnotation(ms); + final Attribute.Compound after = withGlobalOverride(findAfterMethodAnnotation(ms)); // extract return type final Type returnType = ms.getReturnType(); @@ -305,11 +472,15 @@ protected MethodSpec.Builder createMethod(final Symbol.MethodSymbol ms) throws E // extract parameters final StringBuilder arguments = mimicParameters(builder, ms); + // method name with unique signature + final String uniqueMethodName = methodName + asMethodNamePart(arguments); + mappedCalls.put(uniqueMethodName, ms); + // extract throws mimicThrows(builder, ms); - builder.beginControlFlow("if (!$L( $L.$L$L ))", PREDICATE, - METHODS, toConstantName(methodName), + // expected: if (!predicate( M.translateX_diff, diff )) + builder.beginControlFlow("if (!$L( M.$L$L ))", PREDICATE, uniqueMethodName, (arguments.length() == 0 ? "" : ", ") + arguments); // generate default return value @@ -326,75 +497,40 @@ METHODS, toConstantName(methodName), if (null == after) { builder.addStatement((hasReturn ? "return " : "") + "this.inner.$N($L)", methodName, arguments); } else { - afterCalls.set(true); + isAnyAfterCalls.set(true); if (hasReturn) { - builder.addStatement("return $L($L.$L, this.inner.$N($L))", AFTERCALL, - METHODS, toConstantName(methodName), - methodName, arguments); + builder.addStatement("return $L($S, this.inner.$N($L))", AFTER_CALL, uniqueMethodName, methodName, arguments); } else { builder.addStatement("this.inner.$N($L)", methodName, arguments); - builder.addStatement("$L($S, null)", AFTERCALL, methodName); + builder.addStatement("$L($S, null)", AFTER_CALL, uniqueMethodName); } } return builder; } - /** - * Compose constants annotation type. - * - * @param classSpec generated class specification. - */ - protected void createNamesOfMethods(@NonNull final TypeSpec.Builder classSpec) { - final TypeSpec.Builder typeMethods = TypeSpec.annotationBuilder(METHODS) - .addModifiers(Modifier.PUBLIC); - - final List constants = new ArrayList<>(knownMethods.size()); - final StringBuilder format = new StringBuilder().append("{"); + @NonNull + private String asMethodNamePart(@NonNull final StringBuilder arguments) { + return ((arguments.length() > 0) ? "_" : "") + // delimiter + arguments.toString().replaceAll(", ", "_"); + } - String prefix = ""; - for (String methodName : knownMethods) { - constants.add(METHODS + "." + toConstantName(methodName)); - format.append(prefix).append("$L"); + /** Extract afterCall method annotation with respect to global AutoProxy flags. */ + @Nullable + private Attribute.Compound withGlobalOverride(@Nullable final Attribute.Compound afterMethodAnnotation) { + if (null != afterMethodAnnotation) return afterMethodAnnotation; - final FieldSpec field = FieldSpec - .builder(String.class, toConstantName(methodName), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("$S", methodName) - .build(); + final boolean hasAfterCalls = ((this.type.annotation.flags() & Flags.AFTER_CALL) == Flags.AFTER_CALL); - typeMethods.addField(field); - prefix = ", "; + if (hasAfterCalls) { + return GLOBAL_AFTER_CALL; } - format.append("}"); // close array - - typeMethods.addAnnotation(AnnotationSpec.builder(StringDef.class) - .addMember("value", format.toString(), constants.toArray()) - .build()); - - classSpec.addType(typeMethods.build()); + return null; } - /** - * Convert provided name to CONSTANT name. - * - * @param name name of the method that should be converted to suitable constant field name. - * @return name suitable for Constant field declaration - */ - @NonNull - protected String toConstantName(@NonNull final String name) { - return name.toUpperCase(Locale.US); - } - - /** - * Compose default value return if proxy do not allows call to inner instance. - * - * @param builder instance of poet method builder - * @param returnType expected return type - * @param yield yield information for default behavior generating - * @throws Exception allow exception from depth to be raised on higher level - */ + /** Compose default value return if proxy do not allows call to inner instance. */ protected void createYieldPart(@NonNull final MethodSpec.Builder builder, @NonNull final Type returnType, @Nullable final Attribute.Compound yield) throws Exception { @@ -438,9 +574,7 @@ private boolean isRetNumberValue(String value) { @NonNull protected AutoProxy.Yield extractYield(@Nullable final Attribute.Compound yield) throws Exception { // default values of Yield - final Map map = new HashMap<>(); - map.put("value", Returns.THROWS); - map.put("adapter", Returns.class); + final Map map = AutoProxy.DefaultYield.asMap(); // overrides if (null != yield) { @@ -459,22 +593,17 @@ protected AutoProxy.Yield extractYield(@Nullable final Attribute.Compound yield) map.put(key, value); } + } else { // apply global configuration + if (IS_DEBUG) type.logger.printMessage(NOTE, "used global config: " + this.type.annotation.defaultYield()); + + map.put("value", this.type.annotation.defaultYield()); } // new instance return (AutoProxy.Yield) AnnotationParser.annotationForMap(AutoProxy.Yield.class, map); } - //endregion - - //region Helpers - /** - * Mimic annotations of the method, but exclude @Yield annotation during processing. - * - * @param builder instance of poet builder used for composing method - * @param ms reference on instance of a method information - * @throws Exception method can fail in depth, allow raising of exception on top - */ + /** Mimic annotations of the method, but exclude @Yield annotation during processing. */ public static void mimicMethodAnnotations(@NonNull final MethodSpec.Builder builder, @NonNull final Symbol.MethodSymbol ms) throws Exception { if (ms.hasAnnotations()) { @@ -512,12 +641,7 @@ public static Attribute.Compound findYieldMethodAnnotation(@NonNull final Symbol return null; } - /** - * Compose exceptions throwing signature. - * - * @param builder instance of poet method builder - * @param ms reference on source method information - */ + /** Compose exceptions throwing signature. */ public static void mimicThrows(@NonNull final MethodSpec.Builder builder, @NonNull final Symbol.MethodSymbol ms) { for (final Type typeThrown : ms.getThrownTypes()) { @@ -525,14 +649,7 @@ public static void mimicThrows(@NonNull final MethodSpec.Builder builder, } } - /** - * Compose method parameters that mimic original code. - * - * @param builder reference on poet class instance that used for method composing - * @param ms reference on method signature details, symbols, parameters - * @return reference on string builder with enumerated parameters - * @throws Exception can fail during mimicing signature of the method - */ + /** Compose method parameters that mimic original code. */ @NonNull public static StringBuilder mimicParameters(@NonNull final MethodSpec.Builder builder, @NonNull final Symbol.MethodSymbol ms) throws Exception { @@ -572,13 +689,7 @@ public static StringBuilder mimicParameters(@NonNull final MethodSpec.Builder bu return arguments; } - /** - * Compose annotation spec from mirror the original code. - * - * @param am instance of compound attribute that contains class information - * @return instance of annotation builder or NULL - * @throws Exception can potentially raise exception - */ + /** Compose annotation spec from mirror the original code. */ @Nullable public static AnnotationSpec.Builder mimicAnnotation(@NonNull final Attribute.Compound am) throws Exception { final Class clazz; @@ -586,7 +697,7 @@ public static AnnotationSpec.Builder mimicAnnotation(@NonNull final Attribute.Co try { clazz = extractClass(am); return AnnotationSpec.builder(clazz); - } catch (final Throwable ignored) { + } catch (Throwable ignored) { // Not all annotations can be extracted, annotations marked as @Retention(SOURCE) // cannot be extracted by our code } @@ -594,13 +705,7 @@ public static AnnotationSpec.Builder mimicAnnotation(@NonNull final Attribute.Co return null; } - /** - * Extract reflection Class<?> information from compound. - * - * @param am reference on compound attribute that represents class - * @return found class reflection information - * @throws ClassNotFoundException provided wrong class reference - */ + /** Extract reflection Class<?> information from compound. */ @NonNull public static Class extractClass(@NonNull final Attribute.Compound am) throws ClassNotFoundException { final TypeElement te = (TypeElement) am.getAnnotationType().asElement(); @@ -608,13 +713,7 @@ public static Class extractClass(@NonNull final Attribute.Compound am) throws return extractClass(te); } - /** - * Extract reflection Class<?> information from type element. - * - * @param te reference on type element - * @return found class reflection information - * @throws ClassNotFoundException provided wrong class name. - */ + /** Extract reflection Class<?> information from type element. */ @NonNull public static Class extractClass(@NonNull final TypeElement te) throws ClassNotFoundException { final Name name; @@ -640,5 +739,4 @@ public static Class extractClass(@NonNull final TypeElement te) throws ClassN final String innerFix2 = className.substring(0, dot) + "$" + className.substring(dot + 1); return Class.forName(innerFix2).asSubclass(Annotation.class); } - //endregion } diff --git a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java index af5cfb7..f61e64f 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java @@ -1,14 +1,17 @@ -package com.olku.processors; +package net.easypark.processors; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; -import com.olku.annotations.AutoProxy; -import com.olku.annotations.AutoProxyClassGenerator; import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; +import net.easypark.annotations.AutoProxy; +import net.easypark.annotations.AutoProxyClassGenerator; + import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -26,9 +29,6 @@ import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import sun.reflect.annotation.AnnotationParser; import static javax.tools.Diagnostic.Kind.NOTE; @@ -46,12 +46,7 @@ public class TypeProcessor { final Messager logger; final ArrayList methods; - /** - * Main constructor. - * - * @param element reference on code element that we process now. - * @param logger instance of logger for debug information - */ + /** Main constructor. */ public TypeProcessor(@NonNull final Element element, @NonNull final Messager logger) { this.element = element; this.logger = logger; @@ -59,7 +54,7 @@ public TypeProcessor(@NonNull final Element element, @NonNull final Messager log elementName = element.getSimpleName(); flatClassName = flatName(element); - final Symbol.PackageSymbol packageInfo = (Symbol.PackageSymbol) findPackage(element); + final PackageElement packageInfo = findPackage(element); packageName = packageInfo.getQualifiedName(); elementType = element.asType(); @@ -68,13 +63,7 @@ public TypeProcessor(@NonNull final Element element, @NonNull final Messager log methods = new ArrayList<>(); } - /** - * Compose flat name for provided class element. Nested classes will be divided by '$' symbol. - * - * @param classInfo reference on class element - * @return flatten name of the class. - */ - @NonNull + /** Compose flat name for provided class element. Nested classes will be divided by '$' symbol. */ public String flatName(@NonNull final Element classInfo) { StringBuilder builder = new StringBuilder(); @@ -84,7 +73,7 @@ public String flatName(@NonNull final Element classInfo) { while (null != start && !(start instanceof PackageElement)) { builder.insert(0, start.getSimpleName() + divider); - start = ((Symbol) start).owner; + start = start.getEnclosingElement(); divider = "$"; } @@ -92,18 +81,13 @@ public String flatName(@NonNull final Element classInfo) { return builder.toString(); } - /** - * Find package name for provided class element. - * - * @param classInfo reference on class information. - * @return found package name element or raise runtime error. - */ + /** Find package name for provided class element. */ @NonNull public PackageElement findPackage(@NonNull final Element classInfo) { Element start = classInfo; while (null != start && !(start instanceof PackageElement)) { - start = ((Symbol) start).owner; + start = start.getEnclosingElement(); } if (null != start) @@ -112,11 +96,7 @@ public PackageElement findPackage(@NonNull final Element classInfo) { throw new AssertionError("Cannot find a package name for class. " + classInfo); } - /** - * Extract methods from all inheritance methods. - * - * @param typeUtils reference on type information. - */ + /** Extract methods from all inheritance methods. */ public void extractMethods(@NonNull final Types typeUtils) { final Set elements = inheritance(typeUtils, (TypeElement) element); @@ -204,11 +184,7 @@ public String toShortString() { return "AutoProxy Processing : " + elementType.toString(); } - /** - * Get new instance of class generator. - * - * @return instance of code generator. - */ + /** Get new instance of class generator. */ @NonNull public AutoProxyClassGenerator generator() { // https://area-51.blog/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/ @@ -216,7 +192,7 @@ public AutoProxyClassGenerator generator() { Class generator = annotation.value(); // quick jump to default generator - if (generator == AutoProxyClassGenerator.class || generator == AutoProxy.Default.class) + if (generator == AutoProxyClassGenerator.class || generator == AutoProxy.Common.class) return new CommonClassGenerator(this); // create new instance by reflection @@ -231,12 +207,10 @@ public AutoProxyClassGenerator generator() { @NonNull private AutoProxy extractAnnotation(@Nullable final Attribute.Compound annotation) { // extract default values, https://stackoverflow.com/questions/16299717/how-to-create-an-instance-of-an-annotation - if (IS_DEBUG) - logger.printMessage(NOTE, "extracting: " + (null != annotation ? annotation.toString() : "NULL")); + if (IS_DEBUG) logger.printMessage(NOTE, "extracting: " + (null != annotation ? annotation.toString() : "NULL")); - // default values of Yield - final Map map = new HashMap<>(); - map.put("value", AutoProxy.Default.class); + // default values of AutoProxy + final Map map = AutoProxy.DefaultAutoProxy.asMap(); // overrides if (null != annotation) { diff --git a/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java b/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java index fe0727e..98d0c8a 100644 --- a/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java +++ b/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java @@ -1,8 +1,8 @@ -package com.olku.annotations; +package net.easypark.annotations; -import java.lang.annotation.Retention; +import androidx.annotation.*; -import androidx.annotation.StringDef; +import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.SOURCE; diff --git a/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java b/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java index 1b761e3..db9d7b9 100644 --- a/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java +++ b/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java @@ -1,10 +1,10 @@ -package com.olku.generators; +package net.easypark.generators; + +import androidx.annotation.*; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import androidx.annotation.NonNull; - /** RxJava return values generator. */ public class JustRxGenerator implements ReturnsPoet { @NonNull @@ -24,6 +24,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - /* package */ static final JustRxGenerator INSTANCE = new JustRxGenerator(); + static final JustRxGenerator INSTANCE = new JustRxGenerator(); } } diff --git a/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java b/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java index 24df0ba..c2719df 100644 --- a/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java +++ b/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java @@ -1,10 +1,11 @@ -package com.olku.generators; +package net.easypark.generators; + +import androidx.annotation.*; -import com.olku.annotations.RetRx; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import androidx.annotation.NonNull; +import net.easypark.annotations.RetRx; /** RxJava return values generator. */ public class RetRxGenerator implements ReturnsPoet { @@ -36,6 +37,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - /* package */ static final RetRxGenerator INSTANCE = new RetRxGenerator(); + static final RetRxGenerator INSTANCE = new RetRxGenerator(); } } From 5728b3ec928f7e3317319b4fdb9601f428ec6fa2 Mon Sep 17 00:00:00 2001 From: Gurappa Chenchugari Date: Tue, 23 Jun 2020 08:38:03 +0200 Subject: [PATCH 3/5] Package name of source files changed from net.easypark to com.olku --- .../java/com/olku/annotations/AutoProxy.java | 2 +- .../annotations/AutoProxyClassGenerator.java | 2 +- .../java/com/olku/annotations/RetBool.java | 2 +- .../java/com/olku/annotations/RetNumber.java | 2 +- .../java/com/olku/annotations/Returns.java | 2 +- .../com/olku/generators/RetBoolGenerator.java | 4 ++-- .../olku/generators/RetNumberGenerator.java | 4 ++-- .../com/olku/generators/ReturnsGenerator.java | 4 ++-- .../java/com/olku/generators/ReturnsPoet.java | 2 +- .../olku/processors/AutoProxyProcessor.java | 6 ++--- .../olku/processors/CommonClassGenerator.java | 22 +++++++++---------- .../com/olku/processors/TypeProcessor.java | 6 ++--- .../main/java/com/olku/annotations/RetRx.java | 2 +- .../com/olku/generators/JustRxGenerator.java | 2 +- .../com/olku/generators/RetRxGenerator.java | 4 ++-- 15 files changed, 33 insertions(+), 33 deletions(-) diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java index 034b5ff..b9ba04b 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java @@ -1,4 +1,4 @@ -package net.easypark.annotations; +package com.olku.annotations; import androidx.annotation.NonNull; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java index 8abd638..cd27b16 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java @@ -1,4 +1,4 @@ -package net.easypark.annotations; +package com.olku.annotations; import androidx.annotation.NonNull; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java b/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java index 372f75e..1274835 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java @@ -1,4 +1,4 @@ -package net.easypark.annotations; +package com.olku.annotations; import androidx.annotation.*; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java b/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java index 8cc5249..0397dfd 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java @@ -1,4 +1,4 @@ -package net.easypark.annotations; +package com.olku.annotations; import androidx.annotation.*; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java b/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java index 4694156..eb690d3 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/Returns.java @@ -1,4 +1,4 @@ -package net.easypark.annotations; +package com.olku.annotations; import androidx.annotation.StringDef; diff --git a/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java index f6f2edd..21041ba 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java @@ -1,11 +1,11 @@ -package net.easypark.generators; +package com.olku.generators; import androidx.annotation.*; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import net.easypark.annotations.RetBool; +import com.olku.annotations.RetBool; /** Compose return types for boolean. */ public class RetBoolGenerator implements ReturnsPoet { diff --git a/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java index 786aeb1..3549e4e 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java @@ -1,11 +1,11 @@ -package net.easypark.generators; +package com.olku.generators; import androidx.annotation.*; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import net.easypark.annotations.RetNumber; +import com.olku.annotations.RetNumber; import java.util.Map; import java.util.TreeMap; diff --git a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java index adfde48..6ee15d7 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java @@ -1,11 +1,11 @@ -package net.easypark.generators; +package com.olku.generators; import androidx.annotation.NonNull; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import net.easypark.annotations.Returns; +import com.olku.annotations.Returns; /** Compose return types for boolean. */ public class ReturnsGenerator implements ReturnsPoet { diff --git a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java index bcfe748..6545310 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java @@ -1,4 +1,4 @@ -package net.easypark.generators; +package com.olku.generators; import androidx.annotation.*; diff --git a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java index f7fb6b2..2ade1b6 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java @@ -1,9 +1,9 @@ -package net.easypark.processors; +package com.olku.processors; import com.google.auto.service.AutoService; -import net.easypark.annotations.AutoProxy; -import net.easypark.annotations.AutoProxyClassGenerator; +import com.olku.annotations.AutoProxy; +import com.olku.annotations.AutoProxyClassGenerator; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java index 87d1f14..c3d7ab0 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java @@ -1,4 +1,4 @@ -package net.easypark.processors; +package com.olku.processors; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,16 +16,16 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; -import net.easypark.annotations.AutoProxy; -import net.easypark.annotations.AutoProxy.Flags; -import net.easypark.annotations.AutoProxyClassGenerator; -import net.easypark.annotations.RetBool; -import net.easypark.annotations.RetNumber; -import net.easypark.annotations.Returns; -import net.easypark.generators.RetBoolGenerator; -import net.easypark.generators.RetNumberGenerator; -import net.easypark.generators.ReturnsGenerator; -import net.easypark.generators.ReturnsPoet; +import com.olku.annotations.AutoProxy; +import com.olku.annotations.AutoProxy.Flags; +import com.olku.annotations.AutoProxyClassGenerator; +import com.olku.annotations.RetBool; +import com.olku.annotations.RetNumber; +import com.olku.annotations.Returns; +import com.olku.generators.RetBoolGenerator; +import com.olku.generators.RetNumberGenerator; +import com.olku.generators.ReturnsGenerator; +import com.olku.generators.ReturnsPoet; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java index f61e64f..5f5d81f 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java @@ -1,4 +1,4 @@ -package net.easypark.processors; +package com.olku.processors; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -7,8 +7,8 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; -import net.easypark.annotations.AutoProxy; -import net.easypark.annotations.AutoProxyClassGenerator; +import com.olku.annotations.AutoProxy; +import com.olku.annotations.AutoProxyClassGenerator; import java.util.ArrayList; import java.util.Arrays; diff --git a/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java b/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java index 98d0c8a..4a12d97 100644 --- a/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java +++ b/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java @@ -1,4 +1,4 @@ -package net.easypark.annotations; +package com.olku.annotations; import androidx.annotation.*; diff --git a/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java b/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java index db9d7b9..2e26647 100644 --- a/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java +++ b/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java @@ -1,4 +1,4 @@ -package net.easypark.generators; +package com.olku.generators; import androidx.annotation.*; diff --git a/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java b/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java index c2719df..4c0c5c4 100644 --- a/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java +++ b/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java @@ -1,11 +1,11 @@ -package net.easypark.generators; +package com.olku.generators; import androidx.annotation.*; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import net.easypark.annotations.RetRx; +import com.olku.annotations.RetRx; /** RxJava return values generator. */ public class RetRxGenerator implements ReturnsPoet { From 7efce64860a3cb25e1db9bb1b6908419b7106889 Mon Sep 17 00:00:00 2001 From: Oleksandr Kucherenko Date: Tue, 23 Jun 2020 16:30:24 +0200 Subject: [PATCH 4/5] Merge of code changed proposed by EasyPark --- README.md | 117 +++++- .../java/com/olku/annotations/AutoProxy.java | 3 + .../annotations/AutoProxyClassGenerator.java | 2 + .../java/com/olku/annotations/RetBool.java | 2 +- .../java/com/olku/annotations/RetNumber.java | 2 +- .../com/olku/generators/RetBoolGenerator.java | 10 +- .../olku/generators/RetNumberGenerator.java | 10 +- .../com/olku/generators/ReturnsGenerator.java | 8 +- .../java/com/olku/generators/ReturnsPoet.java | 5 +- autoproxy-processor/build.gradle | 2 +- .../olku/processors/AutoProxyProcessor.java | 26 +- .../olku/processors/CommonClassGenerator.java | 345 +++++++++++++----- .../com/olku/processors/TypeProcessor.java | 45 ++- .../main/java/com/olku/annotations/RetRx.java | 2 +- .../com/olku/generators/JustRxGenerator.java | 7 +- .../com/olku/generators/RetRxGenerator.java | 10 +- gradle.properties | 2 +- .../autoproxy/sample/KotlinAbstractMvpView.kt | 18 +- .../olku/autoproxy/sample/MainActivity.java | 4 +- .../olku/autoproxy/sample/ParkingArea.java | 8 +- 20 files changed, 469 insertions(+), 159 deletions(-) diff --git a/README.md b/README.md index 3582166..091d7ab 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ include ':modules:autoproxy:autoproxy-processor' ## Step #3: Declare proxy class specifics ```java -@AutoProxy +@AutoProxy(flags = AutoProxy.Flags.ALL) public interface MvpView { /** Returns NULL if predicate returns False. */ @AutoProxy.Yield(Returns.NULL) @@ -220,7 +220,7 @@ public abstract class Proxy_MvpView implements MvpView { } public final Observable dummyCall(final List generic) { - if (!predicate( Methods.DUMMYCALL, generic )) { + if (!predicate( M.DUMMYCALL, generic )) { // @com.olku.annotations.AutoProxy.Yield(adapter=com.olku.generators.RetRxGenerator.class, value="empty") return Observable.empty(); } @@ -228,7 +228,7 @@ public abstract class Proxy_MvpView implements MvpView { } public final Observable dummyCall(final String message, final List args) { - if (!predicate( Methods.DUMMYCALL, message, args )) { + if (!predicate( M.DUMMYCALL, message, args )) { // @com.olku.annotations.AutoProxy.Yield throw new UnsupportedOperationException("cannot resolve return value."); } @@ -236,7 +236,7 @@ public abstract class Proxy_MvpView implements MvpView { } public final Observable dummyCall(final String message, final Object... args) { - if (!predicate( Methods.DUMMYCALL, message, args )) { + if (!predicate( M.DUMMYCALL, message, args )) { // @com.olku.annotations.AutoProxy.Yield(adapter=com.olku.generators.RetRxGenerator.class, value="error") return Observable.error(new UnsupportedOperationException("unsupported method call")); } @@ -244,7 +244,7 @@ public abstract class Proxy_MvpView implements MvpView { } public final double numericCall() { - if (!predicate( Methods.NUMERICCALL )) { + if (!predicate( M.NUMERICCALL )) { // @com.olku.annotations.AutoProxy.Yield("0") return 0; } @@ -252,7 +252,7 @@ public abstract class Proxy_MvpView implements MvpView { } public final boolean booleanCall() { - if (!predicate( Methods.BOOLEANCALL )) { + if (!predicate( M.BOOLEANCALL )) { // @com.olku.annotations.AutoProxy.Yield("false") return false; } @@ -260,7 +260,7 @@ public abstract class Proxy_MvpView implements MvpView { } public final boolean dispatchDeepLink(@NonNull final Uri deepLink) { - if (!predicate( Methods.DISPATCHDEEPLINK, deepLink )) { + if (!predicate( M.DISPATCHDEEPLINK, deepLink )) { // @com.olku.annotations.AutoProxy.Yield("direct") // direct call, ignore predicate result } @@ -268,23 +268,48 @@ public abstract class Proxy_MvpView implements MvpView { } public final Observable startHearthAnimation() { - if (!predicate( Methods.STARTHEARTHANIMATION )) { + if (!predicate( M.STARTHEARTHANIMATION )) { // @com.olku.annotations.AutoProxy.Yield(adapter=com.olku.generators.JustRxGenerator.class, value="true") return Observable.just(true); } return this.inner.startHearthAnimation(); } - @StringDef({Methods.BOOLEANCALL, Methods.DISPATCHDEEPLINK, Methods.DUMMYCALL, Methods.NUMERICCALL, Methods.STARTHEARTHANIMATION}) - public @interface Methods { + @StringDef({M.BOOLEANCALL, M.DISPATCHDEEPLINK_DEEPLINK, M.DUMMYCALL, M.DUMMYCALL_GENERIC, M.DUMMYCALL_MESSAGE_ARGS, M.NUMERICCALL, M.STARTHEARTHANIMATION}) + public @interface M { + /** + * {@link #booleanCall()} + */ String BOOLEANCALL = "booleanCall"; - String DISPATCHDEEPLINK = "dispatchDeepLink"; + /** + * {@link #dispatchDeepLink(android.net.Uri)} + */ + String DISPATCHDEEPLINK_DEEPLINK = "dispatchDeepLink_deepLink"; + /** + * {@link #dummyCall()} + */ String DUMMYCALL = "dummyCall"; + /** + * {@link #dummyCall(java.util.List)} + */ + String DUMMYCALL_GENERIC = "dummyCall_generic"; + + /** + * {@link #dummyCall(java.lang.String, java.lang.Object[])} + */ + String DUMMYCALL_MESSAGE_ARGS = "dummyCall_message_args"; + + /** + * {@link #numericCall()} + */ String NUMERICCALL = "numericCall"; + /** + * {@link #startHearthAnimation()} + */ String STARTHEARTHANIMATION = "startHearthAnimation"; } } @@ -311,6 +336,76 @@ public abstract class Proxy_MvpView implements MvpView { ``` +## Customization of Generated Code + +By providing special flags you can customize output of AutoProxy generator: + +```kotlin +@AutoProxy(flags = AutoProxy.Flags.ALL) +abstract class KotlinAbstractMvpView { + /* ... */ +} +``` + +Outputs: + +```java + public abstract T afterCall(@M @NonNull final String methodName, final T result); + + /** + * Copy this declaration to fix method demands for old APIs: + * + *
+   * package java.util.function;
+   *
+   * public interface BiFunction<T, U, R> {
+   *     R apply(T t, U u);
+   * }
+   * 
+ */ + public static KotlinAbstractMvpView create(final KotlinAbstractMvpView instance, + final BiFunction action) { + return new Proxy_KotlinAbstractMvpView(instance) { + + @Override + public boolean predicate(final String methodName, final Object... args) { + return action.apply(methodName, args); + } + + @Override + public T afterCall(final String methodName, final T result) { + return result; + }; + }; + } + + public T dispatchByName(@M @NonNull final String methodName, final Object... args) { + final Object result; + if(M.BOOLEANCALL.equals(methodName)) { + return (T)(result = this.inner.booleanCall()); + } + if(M.DISPATCHDEEPLINK_DEEPLINK.equals(methodName)) { + return (T)(result = this.inner.dispatchDeepLink((android.net.Uri)args[0] /*deepLink*/)); + } + if(M.DUMMYCALL.equals(methodName)) { + return (T)(result = this.inner.dummyCall()); + } + if(M.DUMMYCALL_GENERIC.equals(methodName)) { + return (T)(result = this.inner.dummyCall((java.util.List)args[0] /*generic*/)); + } + if(M.DUMMYCALL_MESSAGE_ARGS.equals(methodName)) { + return (T)(result = this.inner.dummyCall((java.lang.String)args[0] /*message*/, (java.lang.Object[])args[1] /*args*/)); + } + if(M.NUMERICCALL.equals(methodName)) { + return (T)(result = this.inner.numericCall()); + } + if(M.STARTHEARTHANIMATION.equals(methodName)) { + return (T)(result = this.inner.startHearthAnimation()); + } + return (T)null; + } +``` + # Troubles http://www.vogella.com/tutorials/GitSubmodules/article.html diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java index b9ba04b..976d42d 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxy.java @@ -55,6 +55,9 @@ abstract class Common implements AutoProxyClassGenerator { int AFTER_CALL = 0x0002; /** Compose callByName(...) method that maps string name to a method call. */ int MAPPING = 0x004; + + /** Compose all additional methods. */ + int ALL = CREATOR | AFTER_CALL | MAPPING; } /** diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java index cd27b16..70e44a8 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/AutoProxyClassGenerator.java @@ -16,9 +16,11 @@ public interface AutoProxyClassGenerator { @NonNull String getErrors(); + /** Get file Name. */ @NonNull String getName(); + /** Get generated elements. */ @NonNull List getOriginating(); } diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java b/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java index 1274835..99ff364 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/RetBool.java @@ -1,6 +1,6 @@ package com.olku.annotations; -import androidx.annotation.*; +import androidx.annotation.StringDef; import java.lang.annotation.Retention; diff --git a/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java b/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java index 0397dfd..e83a241 100644 --- a/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java +++ b/autoproxy-annotations/src/main/java/com/olku/annotations/RetNumber.java @@ -1,6 +1,6 @@ package com.olku.annotations; -import androidx.annotation.*; +import androidx.annotation.StringDef; import java.lang.annotation.Retention; diff --git a/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java index 21041ba..e5b4fe2 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/RetBoolGenerator.java @@ -1,12 +1,12 @@ package com.olku.generators; -import androidx.annotation.*; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.olku.annotations.RetBool; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import com.olku.annotations.RetBool; - /** Compose return types for boolean. */ public class RetBoolGenerator implements ReturnsPoet { @NonNull @@ -15,7 +15,7 @@ public static RetBoolGenerator getInstance() { } public boolean compose(@NonNull final Type returnType, - @RetBool final String type, + @Nullable @RetBool final String type, @NonNull final MethodSpec.Builder builder) { if (RetBool.FALSE.equals(type)) { builder.addStatement("return false"); @@ -29,6 +29,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - static final RetBoolGenerator INSTANCE = new RetBoolGenerator(); + /* package */ static final RetBoolGenerator INSTANCE = new RetBoolGenerator(); } } diff --git a/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java index 3549e4e..afa77e1 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/RetNumberGenerator.java @@ -1,12 +1,12 @@ package com.olku.generators; -import androidx.annotation.*; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.olku.annotations.RetNumber; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import com.olku.annotations.RetNumber; - import java.util.Map; import java.util.TreeMap; @@ -30,7 +30,7 @@ public static RetNumberGenerator getInstance() { } public boolean compose(@NonNull final Type returnType, - @NonNull @RetNumber final String type, + @Nullable @RetNumber final String type, @NonNull final MethodSpec.Builder builder) { final Class output = PRIMITIVES.get(returnType.toString()); @@ -56,6 +56,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - static final RetNumberGenerator INSTANCE = new RetNumberGenerator(); + /* package */ static final RetNumberGenerator INSTANCE = new RetNumberGenerator(); } } diff --git a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java index 6ee15d7..1d177be 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsGenerator.java @@ -1,12 +1,12 @@ package com.olku.generators; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.olku.annotations.Returns; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import com.olku.annotations.Returns; - /** Compose return types for boolean. */ public class ReturnsGenerator implements ReturnsPoet { @NonNull @@ -15,7 +15,7 @@ public static ReturnsGenerator getInstance() { } public boolean compose(@NonNull final Type returnType, - @NonNull @Returns final String type, + @Nullable @Returns final String type, @NonNull final MethodSpec.Builder builder) { // empty string if (Returns.EMPTY.equals(type)) { @@ -52,6 +52,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - static final ReturnsGenerator INSTANCE = new ReturnsGenerator(); + /* package */ static final ReturnsGenerator INSTANCE = new ReturnsGenerator(); } } diff --git a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java index 6545310..6e50004 100644 --- a/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java +++ b/autoproxy-generators/src/main/java/com/olku/generators/ReturnsPoet.java @@ -1,6 +1,7 @@ package com.olku.generators; -import androidx.annotation.*; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; @@ -9,6 +10,6 @@ public interface ReturnsPoet { /** Compose return statement for provided method based on return type and modifier. */ boolean compose(@NonNull final Type returnType, - @NonNull final String modifier, + @Nullable final String modifier, @NonNull final MethodSpec.Builder builder); } diff --git a/autoproxy-processor/build.gradle b/autoproxy-processor/build.gradle index b27e874..f7b4e81 100644 --- a/autoproxy-processor/build.gradle +++ b/autoproxy-processor/build.gradle @@ -18,7 +18,7 @@ dependencies { /* CODE GENERATION */ implementation 'com.google.auto.service:auto-service:1.0-rc7' - kapt 'com.google.auto.service:auto-service:1.0-rc6' + kapt 'com.google.auto.service:auto-service:1.0-rc7' implementation 'com.squareup:javapoet:1.13.0' implementation "androidx.annotation:annotation:${supportVersion}" diff --git a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java index 2ade1b6..996c5cf 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java @@ -1,12 +1,12 @@ package com.olku.processors; import com.google.auto.service.AutoService; - import com.olku.annotations.AutoProxy; import com.olku.annotations.AutoProxyClassGenerator; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -65,6 +65,11 @@ public Set getSupportedAnnotationTypes() { return annotations; } + @Override + public Set getSupportedOptions() { + return Collections.singleton("org.gradle.annotation.processing.aggregating"); + } + @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); @@ -104,10 +109,25 @@ public boolean process(final Set annotations, final Round } final long end = System.nanoTime(); - if (!IS_DEBUG) logger.printMessage(NOTE, (null != tp ? tp.toShortString() : "TypeProcessor FAILED!") + - " takes: " + TimeUnit.NANOSECONDS.toMillis(end - now) + "ms\n"); + if (!IS_DEBUG) { + logger.printMessage(NOTE, (null != tp ? tp.toShortString() : "TypeProcessor FAILED!") + + " takes: " + TimeUnit.NANOSECONDS.toMillis(end - now) + "ms\n"); + } } + // TODO: compose utility class that used for CREATOR's methods + + // /** + // * Represents a function with two arguments. + // * + // * @param the first argument type + // * @param the second argument type + // * @param the result type + // */ + // public interface Func2 { + // R call(T1 t1, T2 t2); + // } + if (failed > 0) { logger.printMessage(ERROR, errors.toString()); } diff --git a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java index c3d7ab0..8561e50 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java @@ -2,8 +2,20 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringDef; +import com.olku.annotations.AutoProxy; +import com.olku.annotations.AutoProxy.Flags; +import com.olku.annotations.AutoProxyClassGenerator; +import com.olku.annotations.RetBool; +import com.olku.annotations.RetNumber; +import com.olku.annotations.Returns; +import com.olku.generators.RetBoolGenerator; +import com.olku.generators.RetNumberGenerator; +import com.olku.generators.ReturnsGenerator; +import com.olku.generators.ReturnsPoet; import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; @@ -16,17 +28,6 @@ import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; -import com.olku.annotations.AutoProxy; -import com.olku.annotations.AutoProxy.Flags; -import com.olku.annotations.AutoProxyClassGenerator; -import com.olku.annotations.RetBool; -import com.olku.annotations.RetNumber; -import com.olku.annotations.Returns; -import com.olku.generators.RetBoolGenerator; -import com.olku.generators.RetNumberGenerator; -import com.olku.generators.ReturnsGenerator; -import com.olku.generators.ReturnsPoet; - import java.io.PrintWriter; import java.io.StringWriter; import java.lang.annotation.Annotation; @@ -36,8 +37,11 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; import javax.annotation.processing.Filer; import javax.lang.model.element.Element; @@ -47,15 +51,17 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; -import rx.functions.Func2; import sun.reflect.annotation.AnnotationParser; import static javax.tools.Diagnostic.Kind.NOTE; /** Common Proxy Class generator. Class designed for inheritance. */ -@SuppressWarnings("WeakerAccess") +@SuppressWarnings({"WeakerAccess", "SameParameterValue", "UnnecessaryLocalVariable"}) public class CommonClassGenerator implements AutoProxyClassGenerator { + /** Enable/Disable debug information. */ public static boolean IS_DEBUG = AutoProxyProcessor.IS_DEBUG; + + //region Constants /** Represents Generic non-NULL value. */ public static final Attribute.Compound GLOBAL_AFTER_CALL = new Attribute.Compound(null, com.sun.tools.javac.util.List.nil()); /** Pre-call / predicate method name. */ @@ -66,7 +72,23 @@ public class CommonClassGenerator implements AutoProxyClassGenerator { protected static final String CREATOR = "create"; /** mapping method that allows to dispatch call to a proper method by it name. */ protected static final String MAPPER = "dispatchByName"; - + /** Annotation type name that is used for constants definition. */ + protected static final String METHODS = "M"; + /** parameter name. */ + protected static final String METHOD_NAME = "methodName"; + /** JavaDoc comment that inform developer how to fix issue with missed interface for old android API's. */ + private static final String BI_FUNCTION_FIX = "Copy this declaration to fix method demands for old APIs:\n\n" + + "
\n" +
+            "package java.util.function;\n" +
+            "\n" +
+            "public interface BiFunction<T, U, R> {\n" +
+            "    R apply(T t, U u);\n" +
+            "}\n" +
+            "
"; + + //endregion + + //region Members /** Data type for processing. */ protected final TypeProcessor type; /** Writer for captured errors. */ @@ -77,71 +99,92 @@ public class CommonClassGenerator implements AutoProxyClassGenerator { protected final AtomicBoolean isAnyAfterCalls = new AtomicBoolean(); /** Lookup of "method name"-to-"arguments line". */ protected final Map mappedCalls = new TreeMap<>(); + /** List of method names. */ + protected final Set knownMethods = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); /** Result file. */ protected JavaFile javaFile; + //endregion + + //region Constructor - /** Main constructor. */ + /** + * Main constructor. + * + * @param type reference on data type processor. + */ public CommonClassGenerator(@NonNull final TypeProcessor type) { this.type = type; superType = TypeName.get(this.type.element.asType()); } + //endregion + //region Code generator + + /** + * {@inheritDoc} + */ @Override public boolean compose(@NonNull final Filer filer) { try { - // is generation flag for forced afterCall set - final boolean hasAfterCalls = ((this.type.annotation.flags() & Flags.AFTER_CALL) == Flags.AFTER_CALL); - isAnyAfterCalls.set(hasAfterCalls); + composeInternal(filer); + } catch (final Throwable ex) { + ex.printStackTrace(new PrintWriter(errors)); + return false; + } - // compose class - final FieldSpec[] members = createMembers(); - final TypeSpec.Builder classSpec = createClass(members); + return true; + } - // constructor and predicate - classSpec.addMethod(createConstructor().build()); - classSpec.addMethod(createPredicate().build()); + /** Compose Plain logic without exception handling wrapper. */ + protected void composeInternal(@NonNull Filer filer) throws Exception { + // is generation flag for forced afterCall set + final boolean hasAfterCalls = ((this.type.annotation.flags() & Flags.AFTER_CALL) == Flags.AFTER_CALL); + isAnyAfterCalls.set(hasAfterCalls); - // auto-generate method proxy calls - createMethods(classSpec); + // compose class + final FieldSpec[] members = createMembers(); + final TypeSpec.Builder classSpec = createClass(members); - // if any after call annotation found in class/methods - if (isAnyAfterCalls.get()) { - classSpec.addMethod(createAfterCall().build()); - } + // constructor and predicate + classSpec.addMethod(createConstructor().build()); + classSpec.addMethod(createPredicate().build()); - // if allowed creator method - if ((this.type.annotation.flags() & Flags.CREATOR) == Flags.CREATOR) { - classSpec.addMethod(createCreator().build()); - } + // auto-generate method proxy calls + createMethods(classSpec); - // if allowed mapper method - if ((this.type.annotation.flags() & Flags.MAPPING) == Flags.MAPPING) { - classSpec.addMethod(createMapper().build()); - } + // if any after call annotation found in class/methods + if (isAnyAfterCalls.get()) { + classSpec.addMethod(createAfterCall().build()); + } - classSpec.addType(createMethodsMapper().build()); + // if allowed creator method + if ((this.type.annotation.flags() & Flags.CREATOR) == Flags.CREATOR) { + classSpec.addMethod(createCreator().build()); + } - classSpec.addOriginatingElement(type.element); + // if allowed mapper method + if ((this.type.annotation.flags() & Flags.MAPPING) == Flags.MAPPING) { + classSpec.addMethod(createMapper().build()); + } - // save class to disk - javaFile = JavaFile.builder(type.packageName.toString(), classSpec.build()).build(); - javaFile.writeTo(filer); + classSpec.addType(createMethodsMapper().build()); - } catch (final Throwable ex) { - ex.printStackTrace(new PrintWriter(errors)); - return false; - } + classSpec.addOriginatingElement(type.element); - return true; + // save class to disk + javaFile = JavaFile.builder(type.packageName.toString(), classSpec.build()).build(); + javaFile.writeTo(filer); } + /** {@inheritDoc} */ @Override @NonNull public String getErrors() { return errors.toString(); } + /** {@inheritDoc} */ @NonNull @Override public String getName() { @@ -150,6 +193,7 @@ public String getName() { return javaFile.toJavaFileObject().getName(); } + /** {@inheritDoc} */ @NonNull @Override public List getOriginating() { @@ -157,7 +201,9 @@ public List getOriginating() { return javaFile.typeSpec.originatingElements; } + //endregion + //region Implementation @NonNull protected FieldSpec[] createMembers() { final List fields = new ArrayList<>(); @@ -217,6 +263,7 @@ private void copyTypeGenericVariables(final TypeSpec.Builder builder) { } /** copy generic parameters for method. */ + @SuppressWarnings("unused") private void copyMethodGenericVariables(final MethodSpec.Builder builder) { if (!(superType instanceof ParameterizedTypeName)) return; @@ -242,7 +289,12 @@ protected MethodSpec.Builder createConstructor() { return builder; } - /** Compose proxy method for provided method definition. */ + /** + * Create methods for provided class. + * + * @param classSpec instance of class specification builder + * @throws Exception give a change to exceptions in depth to deliver the real cause + */ protected void createMethods(@NonNull final TypeSpec.Builder classSpec) throws Exception { // compose methods RuntimeException runtimeError = null; @@ -264,15 +316,26 @@ protected void createMethods(@NonNull final TypeSpec.Builder classSpec) throws E } } - /** Create predicate method declaration. */ + /** + * Create predicate method declaration. + * + * @return instance of the method builder. + */ @NonNull protected MethodSpec.Builder createPredicate() { // TODO: resolve potential name conflict - final MethodSpec.Builder builder = MethodSpec.methodBuilder(PREDICATE); + final String methodName = PREDICATE; + final MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName); builder.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT); builder.returns(boolean.class); - builder.addParameter(String.class, "methodName", Modifier.FINAL); + + final ParameterSpec pMethodNames = ParameterSpec.builder(String.class, METHOD_NAME, Modifier.FINAL) + .addAnnotation(AnnotationSpec.builder(ClassName.bestGuess(METHODS)).build()) + .addAnnotation(AnnotationSpec.builder(NonNull.class).build()) + .build(); + + builder.addParameter(pMethodNames); // varargs builder.varargs(true); @@ -281,19 +344,27 @@ protected MethodSpec.Builder createPredicate() { return builder; } - /** Create afterCall method declaration. */ + /** + * Create afterCall method declaration. + * + * @return instance of the method builder. + */ @NonNull protected MethodSpec.Builder createAfterCall() { final MethodSpec.Builder builder = MethodSpec.methodBuilder(AFTER_CALL); builder.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT); - builder.addTypeVariable(TypeVariableName.get("R", Object.class)); + builder.addTypeVariable(TypeVariableName.get("T", Object.class)); - builder.returns(TypeVariableName.get("R")); + builder.returns(TypeVariableName.get("T")); - builder.addParameter(String.class, "methodName", Modifier.FINAL); + final ParameterSpec pMethodNames = ParameterSpec.builder(String.class, METHOD_NAME, Modifier.FINAL) + .addAnnotation(AnnotationSpec.builder(ClassName.bestGuess(METHODS)).build()) + .addAnnotation(AnnotationSpec.builder(NonNull.class).build()) + .build(); + builder.addParameter(pMethodNames); - builder.addParameter(TypeVariableName.get("R"), "result", Modifier.FINAL); + builder.addParameter(TypeVariableName.get("T"), "result", Modifier.FINAL); return builder; } @@ -302,7 +373,7 @@ protected MethodSpec.Builder createAfterCall() { @NonNull protected MethodSpec.Builder createCreator() { // Output: -// public static UiChange creator(@NonNull final UiChange instance, Func2 action){ +// public static UiChange creator(@NonNull final UiChange instance, Func2 action) { // return new Proxy_UiChange(instance) { // @Override // public boolean predicate(final String methodName, final Object... args) { @@ -322,18 +393,26 @@ protected MethodSpec.Builder createCreator() { builder.returns(superType); builder.addParameter(superType, "instance", Modifier.FINAL); - builder.addParameter(ParameterizedTypeName.get(Func2.class, String.class, Object[].class, Boolean.class), "action", Modifier.FINAL); +// builder.addParameter(ParameterizedTypeName.get(Func2.class, String.class, Object[].class, Boolean.class), "action", Modifier.FINAL); + builder.addParameter(ParameterizedTypeName.get(BiFunction.class, String.class, Object[].class, Boolean.class), "action", Modifier.FINAL); + builder.addJavadoc(BI_FUNCTION_FIX); - builder.addCode("" + - "return new $L(instance) {\n" + + final String afterCallOverride = '\n' + " @Override\n" + - " public boolean predicate(final String methodName, final Object... args) {\n" + - " return action.call(methodName, args);\n" + - " }\n" + (!isAnyAfterCalls.get() ? "" : + " public T afterCall(final String methodName, final T result) {\n" + + " return result;\n" + + " };\n"; + + final String predicateOverride = '\n' + " @Override\n" + - " public R afterCall(final String methodName, final R result) {\n" + - " return result;\n" + - " };\n") + + " public boolean predicate(final String methodName, final Object... args) {\n" + + " return action.apply(methodName, args);\n" + + " }\n"; + + builder.addCode("" + + "return new $L(instance) {\n" + + predicateOverride + + (isAnyAfterCalls.get() ? afterCallOverride : "") + "};\n", "Proxy_" + type.flatClassName); return builder; @@ -344,15 +423,22 @@ protected MethodSpec.Builder createMapper() { final MethodSpec.Builder builder = MethodSpec.methodBuilder(MAPPER); builder.addModifiers(Modifier.PUBLIC); - builder.addTypeVariable(TypeVariableName.get("R", Object.class)); - builder.returns(TypeVariableName.get("R")); + builder.addTypeVariable(TypeVariableName.get("T", Object.class)); + builder.returns(TypeVariableName.get("T")); - builder.addParameter(String.class, "methodName", Modifier.FINAL); + final ParameterSpec pMethodNames = ParameterSpec.builder(String.class, METHOD_NAME, Modifier.FINAL) + .addAnnotation(AnnotationSpec.builder(ClassName.bestGuess(METHODS)).build()) + .addAnnotation(AnnotationSpec.builder(NonNull.class).build()) + .build(); + builder.addParameter(pMethodNames); // varargs builder.varargs(true); builder.addParameter(Object[].class, "args", Modifier.FINAL); + // add boxing/unboxing trick + builder.addCode("final Object result;\n"); + for (final String name : mappedCalls.keySet()) { final Symbol.MethodSymbol ms = mappedCalls.get(name); final Type returnType = ms.getReturnType(); @@ -360,18 +446,18 @@ protected MethodSpec.Builder createMapper() { final String methodName = ms.getSimpleName().toString(); final String params = composeCallParamsFromArray(ms, "args"); - builder.beginControlFlow("if(M.$L.equals(methodName))", name); + builder.beginControlFlow("if($L.$L.equals(methodName))", METHODS, toConstantName(name)); if (hasReturn) { - builder.addStatement("return (R)this.inner.$N($L)", methodName, params); + builder.addStatement("return (T)(result = this.inner.$N($L))", methodName, params); } else { builder.addStatement("this.inner.$N($L)", methodName, params); - builder.addStatement("return (R)null"); + builder.addStatement("return (T)null"); } builder.endControlFlow(); } // fallback - builder.addCode("return (R)null;\n"); + builder.addCode("return (T)null;\n"); return builder; } @@ -379,19 +465,36 @@ protected MethodSpec.Builder createMapper() { /** Compose inner interface with all method names. */ @NonNull protected TypeSpec.Builder createMethodsMapper() { - final TypeSpec.Builder builder = TypeSpec.interfaceBuilder("M") + final TypeSpec.Builder builder = TypeSpec.annotationBuilder(METHODS) .addModifiers(Modifier.PUBLIC); + final List constants = new ArrayList<>(knownMethods.size()); + final StringBuilder format = new StringBuilder().append("{"); + + String prefix = ""; + for (final String name : mappedCalls.keySet()) { final Symbol.MethodSymbol ms = mappedCalls.get(name); final String methodName = ms.getSimpleName().toString(); - builder.addField(FieldSpec.builder(String.class, name) + constants.add(METHODS + "." + toConstantName(name)); + format.append(prefix).append("$L"); + + builder.addField(FieldSpec.builder(String.class, toConstantName(name)) .addJavadoc("{@link #$L($L)}", methodName, composeCallParamsTypes(ms)) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", name) .build()); + + prefix = ", "; } + + format.append("}"); // close array + + builder.addAnnotation(AnnotationSpec.builder(StringDef.class) + .addMember("value", format.toString(), constants.toArray()) + .build()); + return builder; } @@ -417,7 +520,8 @@ private String composeCallParamsTypes(@NonNull final Symbol.MethodSymbol ms) { /** Compose extracting of method parameters from vararg array with data type casting. */ @NonNull - private String composeCallParamsFromArray(@NonNull final Symbol.MethodSymbol ms, @NonNull final String arrayName) { + private String composeCallParamsFromArray(@NonNull final Symbol.MethodSymbol ms, + @NonNull final String arrayName) { String delimiter = ""; final StringBuilder result = new StringBuilder(); @@ -475,12 +579,15 @@ protected MethodSpec.Builder createMethod(final Symbol.MethodSymbol ms) throws E // method name with unique signature final String uniqueMethodName = methodName + asMethodNamePart(arguments); mappedCalls.put(uniqueMethodName, ms); + knownMethods.add(uniqueMethodName); + knownMethods.add(methodName); // extract throws mimicThrows(builder, ms); - // expected: if (!predicate( M.translateX_diff, diff )) - builder.beginControlFlow("if (!$L( M.$L$L ))", PREDICATE, uniqueMethodName, + // expected: if (!predicate( Methods.translateX_diff, diff )) + builder.beginControlFlow("if (!$L( $L.$L$L ))", PREDICATE, + METHODS, toConstantName(uniqueMethodName), (arguments.length() == 0 ? "" : ", ") + arguments); // generate default return value @@ -500,7 +607,9 @@ protected MethodSpec.Builder createMethod(final Symbol.MethodSymbol ms) throws E isAnyAfterCalls.set(true); if (hasReturn) { - builder.addStatement("return $L($S, this.inner.$N($L))", AFTER_CALL, uniqueMethodName, methodName, arguments); + builder.addStatement("return $L($L.$L, this.inner.$N($L))", AFTER_CALL, + METHODS, toConstantName(uniqueMethodName), + methodName, arguments); } else { builder.addStatement("this.inner.$N($L)", methodName, arguments); builder.addStatement("$L($S, null)", AFTER_CALL, uniqueMethodName); @@ -510,6 +619,23 @@ protected MethodSpec.Builder createMethod(final Symbol.MethodSymbol ms) throws E return builder; } + /** + * Convert provided name to CONSTANT name. + * + * @param name name of the method that should be converted to suitable constant field name. + * @return name suitable for Constant field declaration + */ + @NonNull + protected String toConstantName(@NonNull final String name) { + return name.toUpperCase(Locale.US); + } + + /** + * Convert array of arguments string as a valid method name. + * + * @param arguments String representation of arguments array. + * @return name suitable for Constant field declaration + */ @NonNull private String asMethodNamePart(@NonNull final StringBuilder arguments) { return ((arguments.length() > 0) ? "_" : "") + // delimiter @@ -594,7 +720,8 @@ protected AutoProxy.Yield extractYield(@Nullable final Attribute.Compound yield) map.put(key, value); } } else { // apply global configuration - if (IS_DEBUG) type.logger.printMessage(NOTE, "used global config: " + this.type.annotation.defaultYield()); + if (IS_DEBUG) + type.logger.printMessage(NOTE, "used global config: " + this.type.annotation.defaultYield()); map.put("value", this.type.annotation.defaultYield()); } @@ -602,8 +729,17 @@ protected AutoProxy.Yield extractYield(@Nullable final Attribute.Compound yield) // new instance return (AutoProxy.Yield) AnnotationParser.annotationForMap(AutoProxy.Yield.class, map); } + //endregion + + //region Helpers - /** Mimic annotations of the method, but exclude @Yield annotation during processing. */ + /** + * Mimic annotations of the method, but exclude @Yield annotation during processing. + * + * @param builder instance of poet builder used for composing method + * @param ms reference on instance of a method information + * @throws Exception method can fail in depth, allow raising of exception on top + */ public static void mimicMethodAnnotations(@NonNull final MethodSpec.Builder builder, @NonNull final Symbol.MethodSymbol ms) throws Exception { if (ms.hasAnnotations()) { @@ -641,7 +777,12 @@ public static Attribute.Compound findYieldMethodAnnotation(@NonNull final Symbol return null; } - /** Compose exceptions throwing signature. */ + /** + * Compose exceptions throwing signature. + * + * @param builder instance of poet method builder + * @param ms reference on source method information + */ public static void mimicThrows(@NonNull final MethodSpec.Builder builder, @NonNull final Symbol.MethodSymbol ms) { for (final Type typeThrown : ms.getThrownTypes()) { @@ -649,7 +790,14 @@ public static void mimicThrows(@NonNull final MethodSpec.Builder builder, } } - /** Compose method parameters that mimic original code. */ + /** + * Compose method parameters that mimic original code. + * + * @param builder reference on poet class instance that used for method composing + * @param ms reference on method signature details, symbols, parameters + * @return reference on string builder with enumerated parameters + * @throws Exception can fail during mimicing signature of the method + */ @NonNull public static StringBuilder mimicParameters(@NonNull final MethodSpec.Builder builder, @NonNull final Symbol.MethodSymbol ms) throws Exception { @@ -689,7 +837,14 @@ public static StringBuilder mimicParameters(@NonNull final MethodSpec.Builder bu return arguments; } - /** Compose annotation spec from mirror the original code. */ + /** + * Compose annotation spec from mirror the original code. + * + * @param am instance of compound attribute that contains class information + * @return instance of annotation builder or NULL + * @throws Exception can potentially raise exception + */ + @SuppressWarnings("RedundantThrows") @Nullable public static AnnotationSpec.Builder mimicAnnotation(@NonNull final Attribute.Compound am) throws Exception { final Class clazz; @@ -697,15 +852,22 @@ public static AnnotationSpec.Builder mimicAnnotation(@NonNull final Attribute.Co try { clazz = extractClass(am); return AnnotationSpec.builder(clazz); - } catch (Throwable ignored) { + } catch (final Throwable ignored) { // Not all annotations can be extracted, annotations marked as @Retention(SOURCE) // cannot be extracted by our code +// if(IS_DEBUG) throw ignored; } return null; } - /** Extract reflection Class<?> information from compound. */ + /** + * Extract reflection Class<?> information from compound. + * + * @param am reference on compound attribute that represents class + * @return found class reflection information + * @throws ClassNotFoundException provided wrong class reference + */ @NonNull public static Class extractClass(@NonNull final Attribute.Compound am) throws ClassNotFoundException { final TypeElement te = (TypeElement) am.getAnnotationType().asElement(); @@ -713,7 +875,13 @@ public static Class extractClass(@NonNull final Attribute.Compound am) throws return extractClass(te); } - /** Extract reflection Class<?> information from type element. */ + /** + * Extract reflection Class<?> information from type element. + * + * @param te reference on type element + * @return found class reflection information + * @throws ClassNotFoundException provided wrong class name. + */ @NonNull public static Class extractClass(@NonNull final TypeElement te) throws ClassNotFoundException { final Name name; @@ -739,4 +907,5 @@ public static Class extractClass(@NonNull final TypeElement te) throws ClassN final String innerFix2 = className.substring(0, dot) + "$" + className.substring(dot + 1); return Class.forName(innerFix2).asSubclass(Annotation.class); } + //endregion } diff --git a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java index 5f5d81f..74ebe3d 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java @@ -3,13 +3,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.olku.annotations.AutoProxy; +import com.olku.annotations.AutoProxyClassGenerator; import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; -import com.olku.annotations.AutoProxy; -import com.olku.annotations.AutoProxyClassGenerator; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -46,7 +45,12 @@ public class TypeProcessor { final Messager logger; final ArrayList methods; - /** Main constructor. */ + /** + * Main constructor. + * + * @param element reference on code element that we process now. + * @param logger instance of logger for debug information + */ public TypeProcessor(@NonNull final Element element, @NonNull final Messager logger) { this.element = element; this.logger = logger; @@ -54,7 +58,7 @@ public TypeProcessor(@NonNull final Element element, @NonNull final Messager log elementName = element.getSimpleName(); flatClassName = flatName(element); - final PackageElement packageInfo = findPackage(element); + final Symbol.PackageSymbol packageInfo = (Symbol.PackageSymbol) findPackage(element); packageName = packageInfo.getQualifiedName(); elementType = element.asType(); @@ -63,7 +67,13 @@ public TypeProcessor(@NonNull final Element element, @NonNull final Messager log methods = new ArrayList<>(); } - /** Compose flat name for provided class element. Nested classes will be divided by '$' symbol. */ + /** + * Compose flat name for provided class element. Nested classes will be divided by '$' symbol. + * + * @param classInfo reference on class element + * @return flatten name of the class. + */ + @NonNull public String flatName(@NonNull final Element classInfo) { StringBuilder builder = new StringBuilder(); @@ -73,7 +83,7 @@ public String flatName(@NonNull final Element classInfo) { while (null != start && !(start instanceof PackageElement)) { builder.insert(0, start.getSimpleName() + divider); - start = start.getEnclosingElement(); + start = ((Symbol) start).owner; divider = "$"; } @@ -81,13 +91,18 @@ public String flatName(@NonNull final Element classInfo) { return builder.toString(); } - /** Find package name for provided class element. */ + /** + * Find package name for provided class element. + * + * @param classInfo reference on class information. + * @return found package name element or raise runtime error. + */ @NonNull public PackageElement findPackage(@NonNull final Element classInfo) { Element start = classInfo; while (null != start && !(start instanceof PackageElement)) { - start = start.getEnclosingElement(); + start = ((Symbol) start).owner; } if (null != start) @@ -96,7 +111,11 @@ public PackageElement findPackage(@NonNull final Element classInfo) { throw new AssertionError("Cannot find a package name for class. " + classInfo); } - /** Extract methods from all inheritance methods. */ + /** + * Extract methods from all inheritance methods. + * + * @param typeUtils reference on type information. + */ public void extractMethods(@NonNull final Types typeUtils) { final Set elements = inheritance(typeUtils, (TypeElement) element); @@ -184,7 +203,11 @@ public String toShortString() { return "AutoProxy Processing : " + elementType.toString(); } - /** Get new instance of class generator. */ + /** + * Get new instance of class generator. + * + * @return instance of code generator. + */ @NonNull public AutoProxyClassGenerator generator() { // https://area-51.blog/2009/02/13/getting-class-values-from-annotations-in-an-annotationprocessor/ diff --git a/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java b/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java index 4a12d97..6088219 100644 --- a/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java +++ b/autoproxy-rx-annotations/src/main/java/com/olku/annotations/RetRx.java @@ -1,6 +1,6 @@ package com.olku.annotations; -import androidx.annotation.*; +import androidx.annotation.StringDef; import java.lang.annotation.Retention; diff --git a/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java b/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java index 2e26647..153cf8f 100644 --- a/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java +++ b/autoproxy-rx-generators/src/main/java/com/olku/generators/JustRxGenerator.java @@ -1,6 +1,7 @@ package com.olku.generators; -import androidx.annotation.*; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; @@ -13,7 +14,7 @@ public static JustRxGenerator getInstance() { } public boolean compose(@NonNull final Type returnType, - final String type, + @Nullable final String type, @NonNull final MethodSpec.Builder builder) { if (null != type && type.length() > 0) { builder.addStatement("return $T.just($L)", rx.Observable.class, type); @@ -24,6 +25,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - static final JustRxGenerator INSTANCE = new JustRxGenerator(); + /* package */ static final JustRxGenerator INSTANCE = new JustRxGenerator(); } } diff --git a/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java b/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java index 4c0c5c4..a2f2719 100644 --- a/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java +++ b/autoproxy-rx-generators/src/main/java/com/olku/generators/RetRxGenerator.java @@ -1,12 +1,12 @@ package com.olku.generators; -import androidx.annotation.*; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.olku.annotations.RetRx; import com.squareup.javapoet.MethodSpec; import com.sun.tools.javac.code.Type; -import com.olku.annotations.RetRx; - /** RxJava return values generator. */ public class RetRxGenerator implements ReturnsPoet { @NonNull @@ -15,7 +15,7 @@ public static RetRxGenerator getInstance() { } public boolean compose(@NonNull final Type returnType, - @RetRx final String type, + @Nullable @RetRx final String type, @NonNull final MethodSpec.Builder builder) { if (RetRx.EMPTY.equals(type)) { // rx.Observable.empty(); @@ -37,6 +37,6 @@ public boolean compose(@NonNull final Type returnType, } private static final class Singleton { - static final RetRxGenerator INSTANCE = new RetRxGenerator(); + /* package */ static final RetRxGenerator INSTANCE = new RetRxGenerator(); } } diff --git a/gradle.properties b/gradle.properties index 32947e4..82bf590 100644 --- a/gradle.properties +++ b/gradle.properties @@ -57,7 +57,7 @@ GOOGLE_PLAY_VERSION=+ # WARNING! v1.3.50 has a bug in `kapt` # KOTLIN_VERSION=1.3.72 -ANDROID_KTX_VERSION=1.2.0 +ANDROID_KTX_VERSION=1.3.0 # ## Allow TEST configurations in solution, ## increase perfromance of IDE when switched OFF diff --git a/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt b/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt index dd1ba0b..d6b660d 100644 --- a/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt +++ b/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt @@ -6,37 +6,37 @@ import com.olku.generators.JustRxGenerator import com.olku.generators.RetRxGenerator import rx.Observable -@AutoProxy +@AutoProxy(flags = AutoProxy.Flags.ALL) abstract class KotlinAbstractMvpView { - /** Returns NULL if predicate returns False. */ + /** Returns NULL if predicate returns False. */ @AutoProxy.Yield(Returns.NULL) abstract fun dummyCall(): Observable? - /** Returns Observable.empty() */ + /** Returns Observable.empty() */ @AutoProxy.Yield(adapter = RetRxGenerator::class, value = RetRx.EMPTY) abstract fun dummyCall(generic: List?): Observable? - /** Throws exception on False result from predicate. */ + /** Throws exception on False result from predicate. */ @AutoProxy.Yield abstract fun dummyCall(message: String?, args: List?): Observable? - /** Returns Observable.error(...) on False result from predicate. */ + /** Returns Observable.error(...) on False result from predicate. */ @AutoProxy.Yield(adapter = RetRxGenerator::class, value = RetRx.ERROR) abstract fun dummyCall(message: String?, vararg args: Any?): Observable? - /** Returns ZERO on False result from predicate. */ + /** Returns ZERO on False result from predicate. */ @AutoProxy.Yield(RetNumber.ZERO) abstract fun numericCall(): Double - /** Returns FALSE on False result from predicate. */ + /** Returns FALSE on False result from predicate. */ @AutoProxy.Yield(RetBool.FALSE) abstract fun booleanCall(): Boolean - /** Does direct call independent to predicate result. */ + /** Does direct call independent to predicate result. */ @AutoProxy.Yield(Returns.DIRECT) abstract fun dispatchDeepLink(deepLink: Uri): Boolean - /** Returns Observable.just(true) on False result from predicate. */ + /** Returns Observable.just(true) on False result from predicate. */ @AutoProxy.Yield(adapter = JustRxGenerator::class, value = "true") abstract fun startHearthAnimation(): Observable? diff --git a/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java b/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java index 3a08bd9..ec5fa49 100644 --- a/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java +++ b/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java @@ -6,8 +6,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import org.jetbrains.annotations.NotNull; - import java.util.List; import rx.Observable; @@ -25,7 +23,7 @@ protected void onCreate(Bundle savedInstanceState) { public MvpView getProxy() { return new Proxy_MvpView(this) { @Override - public boolean predicate(@Methods @NotNull String methodName, Object... args) { + public boolean predicate(@M @NonNull String methodName, Object... args) { return !isFinishing(); } }; diff --git a/sample/src/main/java/com/olku/autoproxy/sample/ParkingArea.java b/sample/src/main/java/com/olku/autoproxy/sample/ParkingArea.java index e1a5e57..a933186 100644 --- a/sample/src/main/java/com/olku/autoproxy/sample/ParkingArea.java +++ b/sample/src/main/java/com/olku/autoproxy/sample/ParkingArea.java @@ -5,8 +5,6 @@ import com.google.auto.value.AutoValue; import com.olku.annotations.AutoProxy; -import org.jetbrains.annotations.NotNull; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -31,14 +29,14 @@ public abstract class ParkingArea { public ParkingArea.Builder toBuilder() { return new Proxy_ParkingArea$Builder(toBuilderInner()) { @Override - public boolean predicate(@NotNull final String methodName, final Object... args) { + public boolean predicate(@NonNull final String methodName, final Object... args) { return true; /* allow all calls */ } @Override - public R afterCall(@NotNull @Methods final String methodName, final R result) { + public R afterCall(@NonNull @M final String methodName, final R result) { // copy runtime fields from instance after clone creation - if (Methods.BUILD.equals(methodName) && result instanceof ParkingArea) { + if (M.BUILD.equals(methodName) && result instanceof ParkingArea) { ((ParkingArea) result).runtimeData = runtimeData; return result; } From c25ceb51baad063f951ae5842033f33291733f7e Mon Sep 17 00:00:00 2001 From: Oleksandr Kucherenko Date: Tue, 23 Jun 2020 16:36:33 +0200 Subject: [PATCH 5/5] added sample for creator method usage --- .../olku/autoproxy/sample/KotlinAbstractMvpView.kt | 2 +- .../com/olku/autoproxy/sample/MainActivity.java | 13 +++++++------ .../java/com/olku/autoproxy/sample/MvpView.java | 5 +++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt b/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt index d6b660d..3fa8a00 100644 --- a/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt +++ b/sample/src/main/java/com/olku/autoproxy/sample/KotlinAbstractMvpView.kt @@ -6,7 +6,7 @@ import com.olku.generators.JustRxGenerator import com.olku.generators.RetRxGenerator import rx.Observable -@AutoProxy(flags = AutoProxy.Flags.ALL) +@AutoProxy(flags = AutoProxy.Flags.CREATOR) abstract class KotlinAbstractMvpView { /** Returns NULL if predicate returns False. */ @AutoProxy.Yield(Returns.NULL) diff --git a/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java b/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java index ec5fa49..583f489 100644 --- a/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java +++ b/sample/src/main/java/com/olku/autoproxy/sample/MainActivity.java @@ -21,12 +21,13 @@ protected void onCreate(Bundle savedInstanceState) { @NonNull public MvpView getProxy() { - return new Proxy_MvpView(this) { - @Override - public boolean predicate(@M @NonNull String methodName, Object... args) { - return !isFinishing(); - } - }; + return Proxy_MvpView.create(this, (methodName, args) -> !isFinishing()); +// return new Proxy_MvpView(this) { +// @Override +// public boolean predicate(@M @NonNull String methodName, Object... args) { +// return !isFinishing(); +// } +// }; } //region View interface diff --git a/sample/src/main/java/com/olku/autoproxy/sample/MvpView.java b/sample/src/main/java/com/olku/autoproxy/sample/MvpView.java index 00f2ebe..07c8b48 100644 --- a/sample/src/main/java/com/olku/autoproxy/sample/MvpView.java +++ b/sample/src/main/java/com/olku/autoproxy/sample/MvpView.java @@ -2,6 +2,8 @@ import android.net.Uri; +import androidx.annotation.NonNull; + import com.olku.annotations.AutoProxy; import com.olku.annotations.RetBool; import com.olku.annotations.RetNumber; @@ -12,11 +14,10 @@ import java.util.List; -import androidx.annotation.NonNull; import rx.Observable; /** MVP view interface. */ -@AutoProxy +@AutoProxy(flags = AutoProxy.Flags.CREATOR) public interface MvpView { /** Returns NULL if predicate returns False. */ @AutoProxy.Yield(Returns.NULL)