From 3b01fca38e564c8bb1ef99c9abf8c7ebe3d8dae5 Mon Sep 17 00:00:00 2001 From: Sergey Valevich Date: Sun, 3 Mar 2019 10:51:52 +0300 Subject: [PATCH] Added ability to run feature modules independent from main application module. This allows Instant App support (https://github.com/Arello-Mobile/Moxy/issues/209), running unit-tests for feature modules (https://github.com/Arello-Mobile/Moxy/issues/223) and other issues https://github.com/Arello-Mobile/Moxy/issues/224. Here, Moxy always references to MoxyReflector by com.arellomobile.mvp package and generates MoxyReflectorDelegate for each module. In order to use Moxy, there is no need to follow instructions described on this page https://github.com/Arello-Mobile/Moxy/wiki/Multiple-modules Now it is only required to add 1 line of code to BaseActivity.onCreate of each module : MoxyReflector.registerDelegate(MoxyReflectorDelegate.INSTANCE); --- .../mvp/compiler/MvpCompiler.java | 61 ++++------- .../reflector/MoxyReflectorGenerator.java | 103 +++++++----------- .../com/arellomobile/mvp/MvpPresenter.java | 5 +- .../com/arellomobile/mvp/MvpProcessor.java | 5 +- .../mvp/reflector/MoxyReflector.java | 66 +++++++++++ .../mvp/reflector/ReflectorDelegate.java | 9 ++ .../mvp/viewstate/ViewCommands.java | 2 +- .../com/arellomobile/mvp/MoxyReflector.java | 34 ------ 8 files changed, 140 insertions(+), 145 deletions(-) create mode 100644 moxy/src/main/java/com/arellomobile/mvp/reflector/MoxyReflector.java create mode 100644 moxy/src/main/java/com/arellomobile/mvp/reflector/ReflectorDelegate.java delete mode 100644 moxy/stub-reflector/src/main/java/com/arellomobile/mvp/MoxyReflector.java diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java index 3ff166b7..5f66f6d2 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/MvpCompiler.java @@ -1,7 +1,6 @@ package com.arellomobile.mvp.compiler; import com.arellomobile.mvp.InjectViewState; -import com.arellomobile.mvp.RegisterMoxyReflectorPackages; import com.arellomobile.mvp.compiler.presenterbinder.InjectPresenterProcessor; import com.arellomobile.mvp.compiler.presenterbinder.PresenterBinderClassGenerator; import com.arellomobile.mvp.compiler.reflector.MoxyReflectorGenerator; @@ -15,10 +14,8 @@ import java.io.IOException; import java.lang.annotation.Annotation; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -48,9 +45,6 @@ @SuppressWarnings("unused") @AutoService(Processor.class) public class MvpCompiler extends AbstractProcessor { - public static final String MOXY_REFLECTOR_DEFAULT_PACKAGE = "com.arellomobile.mvp"; - - private static final String OPTION_MOXY_REFLECTOR_PACKAGE = "moxyReflectorPackage"; private static Messager sMessager; private static Types sTypeUtils; @@ -79,18 +73,13 @@ public synchronized void init(ProcessingEnvironment processingEnv) { sOptions = processingEnv.getOptions(); } - @Override - public Set getSupportedOptions() { - return Collections.singleton(OPTION_MOXY_REFLECTOR_PACKAGE); - } - @Override public Set getSupportedAnnotationTypes() { Set supportedAnnotationTypes = new HashSet<>(); Collections.addAll(supportedAnnotationTypes, InjectPresenter.class.getCanonicalName(), - InjectViewState.class.getCanonicalName(), - RegisterMoxyReflectorPackages.class.getCanonicalName()); + InjectViewState.class.getCanonicalName() + ); return supportedAnnotationTypes; } @@ -138,43 +127,33 @@ private boolean throwableProcess(RoundEnvironment roundEnv) { viewInterfaceProcessor, viewStateClassGenerator); } - String moxyReflectorPackage = sOptions.get(OPTION_MOXY_REFLECTOR_PACKAGE); - - if (moxyReflectorPackage == null) { - moxyReflectorPackage = MOXY_REFLECTOR_DEFAULT_PACKAGE; - } - - List additionalMoxyReflectorPackages = getAdditionalMoxyReflectorPackages(roundEnv); + String moxyReflectorDelegatePackage = getMoxyReflectorDelegatePackage(roundEnv); - JavaFile moxyReflector = MoxyReflectorGenerator.generate( - moxyReflectorPackage, + JavaFile moxyReflectorDelegate = MoxyReflectorGenerator.generate( + moxyReflectorDelegatePackage, injectViewStateProcessor.getPresenterClassNames(), injectPresenterProcessor.getPresentersContainers(), - viewInterfaceProcessor.getUsedStrategies(), - additionalMoxyReflectorPackages + viewInterfaceProcessor.getUsedStrategies() ); - createSourceFile(moxyReflector); + createSourceFile(moxyReflectorDelegate); return true; } - private List getAdditionalMoxyReflectorPackages(RoundEnvironment roundEnv) { - List result = new ArrayList<>(); - - for (Element element : roundEnv.getElementsAnnotatedWith(RegisterMoxyReflectorPackages.class)) { - if (element.getKind() != ElementKind.CLASS) { - getMessager().printMessage(Diagnostic.Kind.ERROR, element + " must be " + ElementKind.CLASS.name() + ", or not mark it as @" + RegisterMoxyReflectorPackages.class.getSimpleName()); - } - - String[] packages = element.getAnnotation(RegisterMoxyReflectorPackages.class).value(); - - Collections.addAll(result, packages); - } - - return result; - } - + /** + * @return first package associated with this module. + * Probably should return module's top-level package + */ + private String getMoxyReflectorDelegatePackage( + final RoundEnvironment roundEnv + ) { + return processingEnv + .getElementUtils() + .getPackageOf(roundEnv.getElementsAnnotatedWith(InjectPresenter.class).iterator().next()) + .getQualifiedName() + .toString(); + } private void checkInjectors(final RoundEnvironment roundEnv, Class clazz, AnnotationRule annotationRule) { for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(clazz)) { diff --git a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java index 2bf16a20..c11b99c6 100644 --- a/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java +++ b/moxy-compiler/src/main/java/com/arellomobile/mvp/compiler/reflector/MoxyReflectorGenerator.java @@ -2,6 +2,7 @@ import com.arellomobile.mvp.MvpProcessor; import com.arellomobile.mvp.ViewStateProvider; +import com.arellomobile.mvp.reflector.ReflectorDelegate; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; @@ -26,8 +27,6 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import static com.arellomobile.mvp.compiler.MvpCompiler.MOXY_REFLECTOR_DEFAULT_PACKAGE; - /** * Date: 07.12.2016 * Time: 19:05 @@ -50,61 +49,46 @@ public class MoxyReflectorGenerator { public static JavaFile generate(String destinationPackage, List presenterClassNames, List presentersContainers, - List strategyClasses, - List additionalMoxyReflectorsPackages) { - TypeSpec.Builder classBuilder = TypeSpec.classBuilder("MoxyReflector") - .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + List strategyClasses) { + + TypeSpec.Builder classBuilder = TypeSpec.enumBuilder("MoxyReflectorDelegate") + .addEnumConstant("INSTANCE") + .addModifiers(Modifier.PUBLIC) + .addSuperinterface(TypeName.get(ReflectorDelegate.class)) .addField(MAP_CLASS_TO_OBJECT_TYPE_NAME, "sViewStateProviders", Modifier.PRIVATE, Modifier.STATIC) .addField(MAP_CLASS_TO_LIST_OF_OBJECT_TYPE_NAME, "sPresenterBinders", Modifier.PRIVATE, Modifier.STATIC) .addField(MAP_CLASS_TO_OBJECT_TYPE_NAME, "sStrategies", Modifier.PRIVATE, Modifier.STATIC); - classBuilder.addStaticBlock(generateStaticInitializer(presenterClassNames, presentersContainers, strategyClasses, additionalMoxyReflectorsPackages)); - - if (destinationPackage.equals(MOXY_REFLECTOR_DEFAULT_PACKAGE)) { - classBuilder.addMethod(MethodSpec.methodBuilder("getViewState") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(Object.class) - .addParameter(CLASS_WILDCARD_TYPE_NAME, "presenterClass") - .addStatement("$1T viewStateProvider = ($1T) sViewStateProviders.get(presenterClass)", ViewStateProvider.class) - .beginControlFlow("if (viewStateProvider == null)") - .addStatement("return null") - .endControlFlow() - .addCode("\n") - .addStatement("return viewStateProvider.getViewState()") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(ParameterizedTypeName.get(List.class, Object.class)) - .addParameter(CLASS_WILDCARD_TYPE_NAME, "delegated") - .addStatement("return sPresenterBinders.get(delegated)") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getStrategy") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(Object.class) - .addParameter(CLASS_WILDCARD_TYPE_NAME, "strategyClass") - .addStatement("return sStrategies.get(strategyClass)") - .build()); - } else { - classBuilder.addMethod(MethodSpec.methodBuilder("getViewStateProviders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_OBJECT_TYPE_NAME) - .addStatement("return sViewStateProviders") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_LIST_OF_OBJECT_TYPE_NAME) - .addStatement("return sPresenterBinders") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getStrategies") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_OBJECT_TYPE_NAME) - .addStatement("return sStrategies") - .build()); - } + classBuilder.addStaticBlock(generateStaticInitializer(presenterClassNames, presentersContainers, strategyClasses)); + + classBuilder.addMethod(MethodSpec.methodBuilder("getViewState") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(Object.class) + .addParameter(CLASS_WILDCARD_TYPE_NAME, "presenterClass") + .addStatement("$1T viewStateProvider = ($1T) sViewStateProviders.get(presenterClass)", ViewStateProvider.class) + .beginControlFlow("if (viewStateProvider == null)") + .addStatement("return null") + .endControlFlow() + .addCode("\n") + .addStatement("return viewStateProvider.getViewState()") + .build()); + + classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(ParameterizedTypeName.get(List.class, Object.class)) + .addParameter(CLASS_WILDCARD_TYPE_NAME, "delegated") + .addStatement("return sPresenterBinders.get(delegated)") + .build()); + + classBuilder.addMethod(MethodSpec.methodBuilder("getStrategy") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(Object.class) + .addParameter(CLASS_WILDCARD_TYPE_NAME, "strategyClass") + .addStatement("return sStrategies.get(strategyClass)") + .build()); return JavaFile.builder(destinationPackage, classBuilder.build()) @@ -114,13 +98,11 @@ public static JavaFile generate(String destinationPackage, private static CodeBlock generateStaticInitializer(List presenterClassNames, List presentersContainers, - List strategyClasses, - List additionalMoxyReflectorsPackages) { + List strategyClasses) { // sort to preserve order of statements between compilations Map> presenterBinders = getPresenterBinders(presentersContainers); presenterClassNames.sort(TYPE_ELEMENT_COMPARATOR); strategyClasses.sort(TYPE_ELEMENT_COMPARATOR); - additionalMoxyReflectorsPackages.sort(Comparator.naturalOrder()); CodeBlock.Builder builder = CodeBlock.builder(); @@ -161,15 +143,6 @@ private static CodeBlock generateStaticInitializer(List presenterCl builder.addStatement("sStrategies.put($1T.class, new $1T())", strategyClass); } - for (String pkg : additionalMoxyReflectorsPackages) { - ClassName moxyReflector = ClassName.get(pkg, "MoxyReflector"); - - builder.add("\n"); - builder.addStatement("sViewStateProviders.putAll($T.getViewStateProviders())", moxyReflector); - builder.addStatement("sPresenterBinders.putAll($T.getPresenterBinders())", moxyReflector); - builder.addStatement("sStrategies.putAll($T.getStrategies())", moxyReflector); - } - return builder.build(); } diff --git a/moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java b/moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java index 86f5ce75..62329e29 100644 --- a/moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java +++ b/moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java @@ -1,11 +1,12 @@ package com.arellomobile.mvp; -import com.arellomobile.mvp.viewstate.MvpViewState; - import java.util.Collections; import java.util.Set; import java.util.WeakHashMap; +import com.arellomobile.mvp.reflector.MoxyReflector; +import com.arellomobile.mvp.viewstate.MvpViewState; + /** * Date: 15.12.2015 * Time: 19:31 diff --git a/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java b/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java index a697fc1a..92010658 100644 --- a/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java +++ b/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java @@ -1,11 +1,12 @@ package com.arellomobile.mvp; -import com.arellomobile.mvp.presenter.PresenterField; - import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.arellomobile.mvp.presenter.PresenterField; +import com.arellomobile.mvp.reflector.MoxyReflector; + /** * Date: 18-Dec-15 * Time: 13:16 diff --git a/moxy/src/main/java/com/arellomobile/mvp/reflector/MoxyReflector.java b/moxy/src/main/java/com/arellomobile/mvp/reflector/MoxyReflector.java new file mode 100644 index 00000000..0cb9b278 --- /dev/null +++ b/moxy/src/main/java/com/arellomobile/mvp/reflector/MoxyReflector.java @@ -0,0 +1,66 @@ +package com.arellomobile.mvp.reflector; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Date: 07.12.2016 + * Time: 16:39 + * + * @author Yuri Shmakov + */ +public final class MoxyReflector { + + private static final Collection sReflectorDelegates = new HashSet<>(); + + public static void registerDelegate(final ReflectorDelegate reflectorDelegate) { + sReflectorDelegates.add(reflectorDelegate); + } + + public static Object getViewState(final Class presenterClass) { + + for (final ReflectorDelegate delegate: sReflectorDelegates) { + + final Object viewState = delegate.getViewState(presenterClass); + + if (viewState != null) { + + return viewState; + } + } + + return null; + } + + public static List getPresenterBinders(final Class delegated) { + for (final ReflectorDelegate delegate: sReflectorDelegates) { + + final List binders = delegate.getPresenterBinders(delegated); + + if (binders != null) { + + return binders; + } + } + + return null; + } + + public static Object getStrategy(final Class strategyClass) { + + for (final ReflectorDelegate delegate: sReflectorDelegates) { + + final Object strategy = delegate.getStrategy(strategyClass); + + if (strategy != null) { + + return strategy; + } + } + + return null; + } +} diff --git a/moxy/src/main/java/com/arellomobile/mvp/reflector/ReflectorDelegate.java b/moxy/src/main/java/com/arellomobile/mvp/reflector/ReflectorDelegate.java new file mode 100644 index 00000000..42594e69 --- /dev/null +++ b/moxy/src/main/java/com/arellomobile/mvp/reflector/ReflectorDelegate.java @@ -0,0 +1,9 @@ +package com.arellomobile.mvp.reflector; + +import java.util.List; + +public interface ReflectorDelegate { + Object getViewState(Class presenterClass); + List getPresenterBinders(Class delegated); + Object getStrategy(Class strategyClass); +} diff --git a/moxy/src/main/java/com/arellomobile/mvp/viewstate/ViewCommands.java b/moxy/src/main/java/com/arellomobile/mvp/viewstate/ViewCommands.java index 044d0aad..a91060f6 100644 --- a/moxy/src/main/java/com/arellomobile/mvp/viewstate/ViewCommands.java +++ b/moxy/src/main/java/com/arellomobile/mvp/viewstate/ViewCommands.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.Set; -import com.arellomobile.mvp.MoxyReflector; +import com.arellomobile.mvp.reflector.MoxyReflector; import com.arellomobile.mvp.MvpView; import com.arellomobile.mvp.viewstate.strategy.StateStrategy; diff --git a/moxy/stub-reflector/src/main/java/com/arellomobile/mvp/MoxyReflector.java b/moxy/stub-reflector/src/main/java/com/arellomobile/mvp/MoxyReflector.java deleted file mode 100644 index 835f1dc5..00000000 --- a/moxy/stub-reflector/src/main/java/com/arellomobile/mvp/MoxyReflector.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.arellomobile.mvp; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Date: 07.12.2016 - * Time: 16:39 - * - * @author Yuri Shmakov - */ -public class MoxyReflector { - private static Map, Object> sViewStateProviders; - private static Map, List> sPresenterBinders; - private static Map, Object> sStrategies; - - static { - sViewStateProviders = new HashMap<>(); - sPresenterBinders = new HashMap<>(); - } - - public static Object getViewState(Class presenterClass) { - return sViewStateProviders.get(presenterClass); - } - - public static List getPresenterBinders(Class delegated) { - return sPresenterBinders.get(delegated); - } - - public static Object getStrategy(Class strategyClass) { - return sStrategies.get(strategyClass); - } -}