Skip to content

Commit

Permalink
Added ability to run feature modules independent from main applicatio…
Browse files Browse the repository at this point in the history
…n module.

This allows Instant App support (Arello-Mobile#209),
running unit-tests for feature modules (Arello-Mobile#223)
and other issues Arello-Mobile#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);
  • Loading branch information
svcorporate committed Jul 22, 2019
1 parent a7271af commit 3b01fca
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 145 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -79,18 +73,13 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
sOptions = processingEnv.getOptions();
}

@Override
public Set<String> getSupportedOptions() {
return Collections.singleton(OPTION_MOXY_REFLECTOR_PACKAGE);
}

@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> supportedAnnotationTypes = new HashSet<>();
Collections.addAll(supportedAnnotationTypes,
InjectPresenter.class.getCanonicalName(),
InjectViewState.class.getCanonicalName(),
RegisterMoxyReflectorPackages.class.getCanonicalName());
InjectViewState.class.getCanonicalName()
);
return supportedAnnotationTypes;
}

Expand Down Expand Up @@ -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<String> 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<String> getAdditionalMoxyReflectorPackages(RoundEnvironment roundEnv) {
List<String> 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<? extends Annotation> clazz, AnnotationRule annotationRule) {
for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(clazz)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -50,61 +49,46 @@ public class MoxyReflectorGenerator {
public static JavaFile generate(String destinationPackage,
List<TypeElement> presenterClassNames,
List<TypeElement> presentersContainers,
List<TypeElement> strategyClasses,
List<String> additionalMoxyReflectorsPackages) {
TypeSpec.Builder classBuilder = TypeSpec.classBuilder("MoxyReflector")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
List<TypeElement> 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())
Expand All @@ -114,13 +98,11 @@ public static JavaFile generate(String destinationPackage,

private static CodeBlock generateStaticInitializer(List<TypeElement> presenterClassNames,
List<TypeElement> presentersContainers,
List<TypeElement> strategyClasses,
List<String> additionalMoxyReflectorsPackages) {
List<TypeElement> strategyClasses) {
// sort to preserve order of statements between compilations
Map<TypeElement, List<TypeElement>> presenterBinders = getPresenterBinders(presentersContainers);
presenterClassNames.sort(TYPE_ELEMENT_COMPARATOR);
strategyClasses.sort(TYPE_ELEMENT_COMPARATOR);
additionalMoxyReflectorsPackages.sort(Comparator.naturalOrder());

CodeBlock.Builder builder = CodeBlock.builder();

Expand Down Expand Up @@ -161,15 +143,6 @@ private static CodeBlock generateStaticInitializer(List<TypeElement> 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();
}

Expand Down
5 changes: 3 additions & 2 deletions moxy/src/main/java/com/arellomobile/mvp/MvpPresenter.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down
5 changes: 3 additions & 2 deletions moxy/src/main/java/com/arellomobile/mvp/MvpProcessor.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ReflectorDelegate> 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<Object> getPresenterBinders(final Class<?> delegated) {
for (final ReflectorDelegate delegate: sReflectorDelegates) {

final List<Object> 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.arellomobile.mvp.reflector;

import java.util.List;

public interface ReflectorDelegate {
Object getViewState(Class<?> presenterClass);
List<Object> getPresenterBinders(Class<?> delegated);
Object getStrategy(Class<?> strategyClass);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 3b01fca

Please sign in to comment.