Add annotation processor to create static ReactModule infos

Reviewed By: lexs

Differential Revision: D3781016

fbshipit-source-id: 8169e8b55fc044df2230fd01e912c4e96a044f98
This commit is contained in:
Aaron Chiu 2016-09-01 19:23:52 -07:00 committed by Facebook Github Bot 6
parent c06c1e1786
commit 605a0a62dc
11 changed files with 277 additions and 12 deletions

View File

@ -46,16 +46,27 @@ def android_library(
*args,
**kwargs):
common_processors = [
'com.facebook.react.processing.ReactPropertyProcessor',
]
common_processor_deps = [
react_native_target('java/com/facebook/react/processing:processing'),
]
if react_native_target('java/com/facebook/react/uimanager/annotations:annotations') in deps and name != 'processing':
annotation_processors = list(set(annotation_processors + common_processors))
annotation_processor_deps = list(set(annotation_processor_deps + common_processor_deps))
react_property_processors = [
'com.facebook.react.processing.ReactPropertyProcessor',
]
react_property_processor_deps = [
react_native_target('java/com/facebook/react/processing:processing'),
]
annotation_processors = list(set(annotation_processors + react_property_processors))
annotation_processor_deps = list(set(annotation_processor_deps + react_property_processor_deps))
if react_native_target('java/com/facebook/react/module/annotations:annotations') in deps and name != 'processing':
react_module_processors = [
'com.facebook.react.module.processing.ReactModuleSpecProcessor',
]
react_module_processor_deps = [
react_native_target('java/com/facebook/react/module/processing:processing'),
]
annotation_processors = list(set(annotation_processors + react_module_processors))
annotation_processor_deps = list(set(annotation_processor_deps + react_module_processor_deps))
original_android_library(
name=name,

View File

@ -245,6 +245,7 @@ android {
java {
srcDirs = ['src/main/java', 'src/main/libraries/soloader/java', 'src/main/jni/first-party/fb/jni/java']
exclude 'com/facebook/react/processing'
exclude 'com/facebook/react/module/processing'
}
}

View File

@ -0,0 +1,18 @@
include_defs('//ReactAndroid/DEFS')
android_library(
name = 'annotations',
srcs = glob(['**/*.java']),
deps = [
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
react_native_dep('third-party/java/jsr-305:jsr-305'),
react_native_target('java/com/facebook/react/bridge:bridge'),
],
visibility=[
'PUBLIC'
]
)
project_config(
src_target = ':annotations',
)

View File

@ -7,7 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.bridge.annotations;
package com.facebook.react.module.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

View File

@ -0,0 +1,26 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.module.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.facebook.react.bridge.NativeModule;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
/**
* Annotates a function that returns a list of ModuleSpecs from which we get a list of NativeModules
* to create ReactModuleInfos from.
*/
@Retention(SOURCE)
@Target(TYPE)
public @interface ReactModuleList {
/**
* The native modules in this list should be annotated with {@link ReactModule}.
* @return List of native modules.
*/
Class<? extends NativeModule>[] value();
}

View File

@ -0,0 +1,16 @@
include_defs('//ReactAndroid/DEFS')
android_library(
name = 'model',
srcs = glob(['**/*.java']),
deps = [
react_native_dep('third-party/java/jsr-305:jsr-305'),
],
visibility=[
'PUBLIC'
]
)
project_config(
src_target = ':model',
)

View File

@ -0,0 +1,25 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.module.model;
/**
* Data holder class holding native module specifications.
*/
public class ReactModuleInfo {
public final String mName;
public final boolean mCanOverrideExistingModule;
public final boolean mSupportsWebWorkers;
public final boolean mNeedsEagerInit;
public ReactModuleInfo(
String name,
boolean canOverrideExistingModule,
boolean supportsWebWorkers,
boolean needsEagerInit) {
mName = name;
mCanOverrideExistingModule = canOverrideExistingModule;
mSupportsWebWorkers = supportsWebWorkers;
mNeedsEagerInit = needsEagerInit;
}
}

View File

@ -0,0 +1,22 @@
include_defs('//ReactAndroid/DEFS')
java_library(
name = 'processing',
srcs = glob(['*.java']),
source = '7',
target = '7',
deps = [
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
react_native_dep('third-party/java/javapoet:javapoet'),
react_native_dep('third-party/java/jsr-305:jsr-305'),
react_native_target('java/com/facebook/react/module/annotations:annotations'),
react_native_target('java/com/facebook/react/module/model:model'),
],
visibility=[
'PUBLIC'
]
)
project_config(
src_target = ':processing',
)

View File

@ -0,0 +1,143 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.module.processing;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypesException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
import com.facebook.react.module.model.ReactModuleInfo;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import static javax.lang.model.element.Modifier.PUBLIC;
/**
* Generates a list of ReactModuleInfo for modules annotated with {@link ReactModule} in
* {@link ReactPackage}s annotated with {@link ReactModuleList}.
*/
@SupportedAnnotationTypes({
"com.facebook.react.module.annotations.ReactModule",
"com.facebook.react.module.annotations.ReactModuleList",
})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ReactModuleSpecProcessor extends AbstractProcessor {
private static final TypeName MAP_TYPE = ParameterizedTypeName.get(
Map.class,
Class.class,
ReactModuleInfo.class);
private static final TypeName INSTANTIATED_MAP_TYPE = ParameterizedTypeName.get(HashMap.class);
@SuppressFieldNotInitialized
private Filer mFiler;
@SuppressFieldNotInitialized
private Elements mElements;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mElements = processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> reactModuleListElements = roundEnv.getElementsAnnotatedWith(
ReactModuleList.class);
for (Element reactModuleListElement : reactModuleListElements) {
TypeElement typeElement = (TypeElement) reactModuleListElement;
ClassName className = ClassName.get(typeElement);
String packageName = ClassName.get(typeElement).packageName();
String fileName = className.simpleName();
ReactModuleList reactModuleList = typeElement.getAnnotation(ReactModuleList.class);
List<String> nativeModules = new ArrayList<>();
try {
reactModuleList.value(); // throws MirroredTypesException
} catch (MirroredTypesException mirroredTypesException) {
List<? extends TypeMirror> typeMirrors = mirroredTypesException.getTypeMirrors();
for (TypeMirror typeMirror : typeMirrors) {
nativeModules.add(typeMirror.toString());
}
}
MethodSpec getReactModuleInfosMethod = MethodSpec.methodBuilder("getReactModuleInfos")
.addModifiers(PUBLIC)
// TODO add function to native module interface
// .addAnnotation(Override.class)
.addCode(getCodeBlockForReactModuleInfos(nativeModules))
.returns(MAP_TYPE)
.build();
TypeSpec reactModulesInfosTypeSpec = TypeSpec.classBuilder(
fileName + "$$ReactModuleInfoProvider")
.addModifiers(Modifier.PUBLIC)
.addMethod(getReactModuleInfosMethod)
.build();
JavaFile javaFile = JavaFile.builder(packageName, reactModulesInfosTypeSpec)
.addFileComment("Generated by " + getClass().getName())
.build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private CodeBlock getCodeBlockForReactModuleInfos(List<String> nativeModules) {
CodeBlock.Builder builder = CodeBlock.builder()
.addStatement("$T map = new $T()", MAP_TYPE, INSTANTIATED_MAP_TYPE);
for (String nativeModule : nativeModules) {
String keyString = nativeModule + ".class";
TypeElement typeElement = mElements.getTypeElement(nativeModule);
ReactModule reactModule = typeElement.getAnnotation(ReactModule.class);
String valueString = new StringBuilder()
.append("new ReactModuleInfo(")
.append("\"").append(reactModule.name()).append("\"").append(", ")
.append(reactModule.canOverrideExistingModule()).append(", ")
.append(reactModule.supportsWebWorkers()).append(", ")
.append(reactModule.needsEagerInit())
.append(")")
.toString();
builder.addStatement("map.put(" + keyString + ", " + valueString + ")");
}
builder.addStatement("return map");
return builder.build();
}
}

View File

@ -14,6 +14,7 @@ android_library(
react_native_target('java/com/facebook/react/bridge:bridge'),
react_native_target('java/com/facebook/react/common/network:network'),
react_native_target('java/com/facebook/react/common:common'),
react_native_target('java/com/facebook/react/module/annotations:annotations'),
react_native_target('java/com/facebook/react/modules/core:core'),
],
visibility = [

View File

@ -9,8 +9,6 @@
package com.facebook.react.modules.network;
import android.util.Base64;
import javax.annotation.Nullable;
import java.io.IOException;
@ -21,6 +19,8 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import android.util.Base64;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ExecutorToken;
import com.facebook.react.bridge.GuardedAsyncTask;
@ -31,6 +31,7 @@ import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.network.OkHttpCallUtil;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
import okhttp3.Call;
@ -49,6 +50,7 @@ import okhttp3.ResponseBody;
/**
* Implements the XMLHttpRequest JavaScript interface.
*/
@ReactModule(name = "RCTNetworking")
public final class NetworkingModule extends ReactContextBaseJavaModule {
private static final String CONTENT_ENCODING_HEADER_NAME = "content-encoding";