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 fccf9f5..976d42d 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; +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,80 @@ 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; + + /** Compose all additional methods. */ + int ALL = CREATOR | AFTER_CALL | MAPPING; + } + + /** + * 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..70e44a8 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; - 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,12 @@ public interface AutoProxyClassGenerator { /** Get errors captured during processing. */ @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 6adcde5..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,9 +1,9 @@ package com.olku.annotations; -import java.lang.annotation.Retention; - import androidx.annotation.StringDef; +import java.lang.annotation.Retention; + import static java.lang.annotation.RetentionPolicy.SOURCE; /** Boolean value returns. */ 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..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,9 +1,9 @@ package com.olku.annotations; -import java.lang.annotation.Retention; - 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..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,9 +1,9 @@ package com.olku.annotations; -import java.lang.annotation.Retention; - 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"; } 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..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,11 +1,12 @@ package com.olku.generators; +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 androidx.annotation.NonNull; - /** Compose return types for boolean. */ public class RetBoolGenerator implements ReturnsPoet { @NonNull @@ -14,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"); 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..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,5 +1,8 @@ package com.olku.generators; +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; @@ -7,8 +10,6 @@ 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); @@ -29,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()); 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..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,11 +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 androidx.annotation.NonNull; - /** Compose return types for boolean. */ public class ReturnsGenerator implements ReturnsPoet { @NonNull @@ -14,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)) { @@ -40,6 +41,13 @@ 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; } 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..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,14 +1,15 @@ package com.olku.generators; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + 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. */ 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 167612b..996c5cf 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/AutoProxyProcessor.java @@ -7,7 +7,10 @@ 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) { @@ -85,9 +94,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)); @@ -95,15 +109,33 @@ 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(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..8561e50 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/CommonClassGenerator.java @@ -5,6 +5,7 @@ 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; @@ -19,6 +20,7 @@ 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; @@ -31,13 +33,15 @@ 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.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; @@ -51,43 +55,55 @@ import static javax.tools.Diagnostic.Kind.NOTE; -/** - * Common Proxy Class generator. Class designed for inheritance. - */ -@SuppressWarnings("WeakerAccess") +/** Common Proxy Class generator. Class designed for inheritance. */ +@SuppressWarnings({"WeakerAccess", "SameParameterValue", "UnnecessaryLocalVariable"}) public class CommonClassGenerator implements AutoProxyClassGenerator { + /** Enable/Disable debug information. */ public static boolean IS_DEBUG = AutoProxyProcessor.IS_DEBUG; - /** - * Pre-call / predicate method name. - */ + //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. */ 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"; + /** 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"; + /** 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" +
+            "
"; - /** - * Data type for processing. - */ + //endregion + + //region Members + /** 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); + /** 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<>(); + /** List of method names. */ + protected final Set knownMethods = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + /** Result file. */ + protected JavaFile javaFile; + //endregion //region Constructor @@ -111,44 +127,80 @@ public CommonClassGenerator(@NonNull final TypeProcessor type) { @Override public boolean compose(@NonNull final Filer filer) { try { - // compose class - final FieldSpec[] members = createMembers(); - final TypeSpec.Builder classSpec = createClass(members); + composeInternal(filer); + } catch (final Throwable ex) { + ex.printStackTrace(new PrintWriter(errors)); + return false; + } - // constructor and predicate - classSpec.addMethod(createConstructor().build()); - classSpec.addMethod(createPredicate().build()); + return true; + } - // auto-generate method proxy calls - createMethods(classSpec); + /** 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); - // if any after call annotation found in class/methods - if (afterCalls.get()) { - classSpec.addMethod(createAfterCall().build()); - } + // compose class + final FieldSpec[] members = createMembers(); + final TypeSpec.Builder classSpec = createClass(members); - createNamesOfMethods(classSpec); + // constructor and predicate + classSpec.addMethod(createConstructor().build()); + classSpec.addMethod(createPredicate().build()); - // save class to disk - final JavaFile javaFile = JavaFile.builder(type.packageName.toString(), classSpec.build()).build(); - javaFile.writeTo(filer); + // auto-generate method proxy calls + createMethods(classSpec); - } catch (final Throwable ignored) { - ignored.printStackTrace(new PrintWriter(errors)); - return false; + // if any after call annotation found in class/methods + if (isAnyAfterCalls.get()) { + classSpec.addMethod(createAfterCall().build()); } - return true; + // 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 + javaFile = JavaFile.builder(type.packageName.toString(), classSpec.build()).build(); + javaFile.writeTo(filer); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override @NonNull public String getErrors() { return errors.toString(); } + + /** {@inheritDoc} */ + @NonNull + @Override + public String getName() { + if (null == javaFile) return ""; + + return javaFile.toJavaFileObject().getName(); + } + + /** {@inheritDoc} */ + @NonNull + @Override + public List getOriginating() { + if (null == javaFile) return Collections.emptyList(); + + return javaFile.typeSpec.originatingElements; + } //endregion //region Implementation @@ -170,10 +222,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 +249,33 @@ 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. */ + @SuppressWarnings("unused") + 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) @@ -242,7 +330,7 @@ protected MethodSpec.Builder createPredicate() { builder.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT); builder.returns(boolean.class); - final ParameterSpec pMethodNames = ParameterSpec.builder(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(); @@ -263,31 +351,214 @@ protected MethodSpec.Builder createPredicate() { */ @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.addTypeVariable(TypeVariableName.get("T", Object.class)); - builder.returns(TypeVariableName.get("R")); + builder.returns(TypeVariableName.get("T")); - final ParameterSpec pMethodNames = ParameterSpec.builder(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; } + /** 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.addParameter(ParameterizedTypeName.get(BiFunction.class, String.class, Object[].class, Boolean.class), "action", Modifier.FINAL); + builder.addJavadoc(BI_FUNCTION_FIX); + + final String afterCallOverride = '\n' + + " @Override\n" + + " public T afterCall(final String methodName, final T result) {\n" + + " return result;\n" + + " };\n"; + + final String predicateOverride = '\n' + + " @Override\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; + } + + @NonNull + protected MethodSpec.Builder createMapper() { + final MethodSpec.Builder builder = MethodSpec.methodBuilder(MAPPER); + builder.addModifiers(Modifier.PUBLIC); + + builder.addTypeVariable(TypeVariableName.get("T", Object.class)); + builder.returns(TypeVariableName.get("T")); + + 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(); + final boolean hasReturn = returnType.getKind() != TypeKind.VOID; + final String methodName = ms.getSimpleName().toString(); + final String params = composeCallParamsFromArray(ms, "args"); + + builder.beginControlFlow("if($L.$L.equals(methodName))", METHODS, toConstantName(name)); + if (hasReturn) { + builder.addStatement("return (T)(result = this.inner.$N($L))", methodName, params); + } else { + builder.addStatement("this.inner.$N($L)", methodName, params); + builder.addStatement("return (T)null"); + } + builder.endControlFlow(); + } + + // fallback + builder.addCode("return (T)null;\n"); + + return builder; + } + + /** Compose inner interface with all method names. */ + @NonNull + protected TypeSpec.Builder createMethodsMapper() { + 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(); + + 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; + } + + /** 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 +566,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 +576,18 @@ 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); + knownMethods.add(uniqueMethodName); + knownMethods.add(methodName); + // extract throws mimicThrows(builder, ms); + // expected: if (!predicate( Methods.translateX_diff, diff )) builder.beginControlFlow("if (!$L( $L.$L$L ))", PREDICATE, - METHODS, toConstantName(methodName), + METHODS, toConstantName(uniqueMethodName), (arguments.length() == 0 ? "" : ", ") + arguments); // generate default return value @@ -326,56 +604,21 @@ 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), + 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)", 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("{"); - - String prefix = ""; - for (String methodName : knownMethods) { - constants.add(METHODS + "." + toConstantName(methodName)); - format.append(prefix).append("$L"); - - final FieldSpec field = FieldSpec - .builder(String.class, toConstantName(methodName), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("$S", methodName) - .build(); - - typeMethods.addField(field); - prefix = ", "; - } - - format.append("}"); // close array - - typeMethods.addAnnotation(AnnotationSpec.builder(StringDef.class) - .addMember("value", format.toString(), constants.toArray()) - .build()); - - classSpec.addType(typeMethods.build()); - } - /** * Convert provided name to CONSTANT name. * @@ -388,13 +631,32 @@ protected String toConstantName(@NonNull final String name) { } /** - * Compose default value return if proxy do not allows call to inner instance. + * Convert array of arguments string as a valid method name. * - * @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 + * @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 + arguments.toString().replaceAll(", ", "_"); + } + + /** 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 boolean hasAfterCalls = ((this.type.annotation.flags() & Flags.AFTER_CALL) == Flags.AFTER_CALL); + + if (hasAfterCalls) { + return GLOBAL_AFTER_CALL; + } + + return null; + } + + /** 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 +700,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,6 +719,11 @@ 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 @@ -579,6 +844,7 @@ public static StringBuilder mimicParameters(@NonNull final MethodSpec.Builder bu * @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; @@ -589,6 +855,7 @@ public static AnnotationSpec.Builder mimicAnnotation(@NonNull final Attribute.Co } 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; 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..74ebe3d 100644 --- a/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java +++ b/autoproxy-processor/src/main/java/com/olku/processors/TypeProcessor.java @@ -1,5 +1,8 @@ package com.olku.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; @@ -8,7 +11,6 @@ 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 +28,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; @@ -216,7 +215,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 +230,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..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,9 +1,9 @@ package com.olku.annotations; -import java.lang.annotation.Retention; - import androidx.annotation.StringDef; +import java.lang.annotation.Retention; + import static java.lang.annotation.RetentionPolicy.SOURCE; /** Rx Observable results. */ 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..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,10 +1,11 @@ package com.olku.generators; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + 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 @@ -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); 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..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,11 +1,12 @@ package com.olku.generators; +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 androidx.annotation.NonNull; - /** RxJava return values generator. */ public class RetRxGenerator implements ReturnsPoet { @NonNull @@ -14,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(); 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..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,37 +6,37 @@ import com.olku.generators.JustRxGenerator import com.olku.generators.RetRxGenerator import rx.Observable -@AutoProxy +@AutoProxy(flags = AutoProxy.Flags.CREATOR) 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..583f489 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; @@ -23,12 +21,13 @@ protected void onCreate(Bundle savedInstanceState) { @NonNull public MvpView getProxy() { - return new Proxy_MvpView(this) { - @Override - public boolean predicate(@Methods @NotNull 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) 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; }