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(); } }