From 26091a1a5af07ea2fbd5a10302ce1e26355754dd 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 | 58 +++------- .../reflector/MoxyReflectorGenerator.java | 103 +++++++----------- .../com/arellomobile/mvp/MvpPresenter.java | 1 + .../com/arellomobile/mvp/MvpProcessor.java | 1 + .../mvp/reflector/MoxyReflector.java | 66 +++++++++++ .../mvp/reflector/ReflectorDelegate.java | 9 ++ .../mvp/viewstate/ViewCommands.java | 2 +- .../com/arellomobile/mvp/MoxyReflector.java | 34 ------ 8 files changed, 134 insertions(+), 140 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 4c6f88d1..ff9aaef8 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 @@ -2,7 +2,6 @@ import com.arellomobile.mvp.GenerateViewState; 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; @@ -16,10 +15,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; @@ -49,9 +46,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; @@ -80,18 +74,12 @@ 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(), GenerateViewState.class.getCanonicalName()); return supportedAnnotationTypes; } @@ -140,43 +128,33 @@ private boolean throwableProcess(RoundEnvironment roundEnv) { viewInterfaceProcessor, viewStateClassGenerator); } - String moxyReflectorPackage = sOptions.get(OPTION_MOXY_REFLECTOR_PACKAGE); + String moxyReflectorDelegatePackage = getMoxyReflectorDelegatePackage(roundEnv); - if (moxyReflectorPackage == null) { - moxyReflectorPackage = MOXY_REFLECTOR_DEFAULT_PACKAGE; - } - - List additionalMoxyReflectorPackages = getAdditionalMoxyReflectorPackages(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 c00dc74e..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 viewStateProvider.getViewState()") - .build()); - - classBuilder.addMethod(MethodSpec.methodBuilder("getPresenterBinders") - .addModifiers(Modifier.PUBLIC, Modifier.STATIC) - .returns(MAP_CLASS_TO_LIST_OF_OBJECT_TYPE_NAME) - .addStatement("return sViewStateProviders") - .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 79b66e43..3a9db047 100644 --- a/moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java +++ b/moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java @@ -5,6 +5,7 @@ import java.util.WeakHashMap; import com.arellomobile.mvp.presenter.PresenterType; +import com.arellomobile.mvp.reflector.MoxyReflector; import com.arellomobile.mvp.viewstate.MvpViewState; /** diff --git a/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java b/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java index ea07d5dd..0e5220c0 100644 --- a/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java +++ b/moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java @@ -6,6 +6,7 @@ import com.arellomobile.mvp.presenter.PresenterField; import com.arellomobile.mvp.presenter.PresenterType; +import com.arellomobile.mvp.reflector.MoxyReflector; /** * Date: 18-Dec-15 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); - } -}