Add annotation processor for @ReactProp

Summary:
The annotation processor finds subclasses of ViewManager and ShadowNode and generates classes that can both provide a mapping of property names to their type as well as a way of setting these properties.

This avoids having to do reflection to find the properties.

The annotation processor is currently not working when building with Gradle.

public

Reviewed By: astreet

Differential Revision: D2748958

fb-gh-sync-id: ded5b072d236ebf19fb43eaf704fc7f221a82c26
This commit is contained in:
Alexander Blom
2016-01-07 03:51:28 -08:00
committed by facebook-github-bot-4
parent c1b7a369af
commit 57f6cbb3dc
8 changed files with 880 additions and 37 deletions

View File

@@ -12,12 +12,10 @@ package com.facebook.react.uimanager;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Map;
import com.facebook.csslayout.CSSNode;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
/**
* Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily
@@ -42,6 +40,7 @@ import com.facebook.react.bridge.ReadableMapKeySetIterator;
* children (e.g. {@link #getNativeChildCount()}). See {@link NativeViewHierarchyOptimizer} for more
* information.
*/
@ReactPropertyHolder
public class ReactShadowNode extends CSSNode {
private int mReactTag;
@@ -170,17 +169,7 @@ public class ReactShadowNode extends CSSNode {
}
public final void updateProperties(CatalystStylesDiffMap props) {
Map<String, ViewManagersPropertyCache.PropSetter> propSetters =
ViewManagersPropertyCache.getNativePropSettersForShadowNodeClass(getClass());
ReadableMap propMap = props.mBackingMap;
ReadableMapKeySetIterator iterator = propMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ViewManagersPropertyCache.PropSetter setter = propSetters.get(key);
if (setter != null) {
setter.updateShadowNodeProp(this, props);
}
}
ViewManagerPropertyUpdater.updateProps(this, props);
onAfterUpdateTransaction();
}

View File

@@ -16,32 +16,22 @@ import java.util.Map;
import android.view.View;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.touch.CatalystInterceptingViewGroup;
import com.facebook.react.touch.JSResponderHandler;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
/**
* Class responsible for knowing how to create and update catalyst Views of a given type. It is also
* responsible for creating and updating CSSNode subclasses used for calculating position and size
* for the corresponding native view.
*/
@ReactPropertyHolder
public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
public final void updateProperties(T viewToUpdate, CatalystStylesDiffMap props) {
Map<String, ViewManagersPropertyCache.PropSetter> propSetters =
ViewManagersPropertyCache.getNativePropSettersForViewManagerClass(getClass());
ReadableMap propMap = props.mBackingMap;
ReadableMapKeySetIterator iterator = propMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ViewManagersPropertyCache.PropSetter setter = propSetters.get(key);
if (setter != null) {
setter.updateViewProp(this, viewToUpdate, props);
}
}
ViewManagerPropertyUpdater.updateProps(this, viewToUpdate, props);
onAfterUpdateTransaction(viewToUpdate);
}
@@ -206,6 +196,6 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
}
public Map<String, String> getNativeProps() {
return ViewManagersPropertyCache.getNativePropsForView(getClass(), getShadowNodeClass());
return ViewManagerPropertyUpdater.getNativeProps(getClass(), getShadowNodeClass());
}
}

View File

@@ -0,0 +1,163 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.uimanager;
import java.util.HashMap;
import java.util.Map;
import android.view.View;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
public class ViewManagerPropertyUpdater {
public interface Settable {
Map<String, String> getProperties();
}
public interface ViewManagerSetter<T extends ViewManager, V extends View> extends Settable {
void setProperty(T manager, V view, String name, CatalystStylesDiffMap props);
}
public interface ShadowNodeSetter<T extends ReactShadowNode> extends Settable {
void setProperty(T node, String name, CatalystStylesDiffMap props);
}
private static final String TAG = "ViewManagerPropertyUpdater";
private static final Map<Class<?>, ViewManagerSetter<?, ?>> VIEW_MANAGER_SETTER_MAP =
new HashMap<>();
private static final Map<Class<?>, ShadowNodeSetter<?>> SHADOW_NODE_SETTER_MAP = new HashMap<>();
public static <T extends ViewManager, V extends View> void updateProps(
T manager,
V v,
CatalystStylesDiffMap props) {
ViewManagerSetter<T, V> setter = findManagerSetter(manager.getClass());
ReadableMap propMap = props.mBackingMap;
ReadableMapKeySetIterator iterator = propMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
setter.setProperty(manager, v, key, props);
}
}
public static <T extends ReactShadowNode> void updateProps(T node, CatalystStylesDiffMap props) {
ShadowNodeSetter<T> setter = findNodeSetter(node.getClass());
ReadableMap propMap = props.mBackingMap;
ReadableMapKeySetIterator iterator = propMap.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
setter.setProperty(node, key, props);
}
}
public static Map<String, String> getNativeProps(
Class<? extends ViewManager> viewManagerTopClass,
Class<? extends ReactShadowNode> shadowNodeTopClass) {
Map<String, String> props = new HashMap<>();
props.putAll(findManagerSetter(viewManagerTopClass).getProperties());
props.putAll(findNodeSetter(shadowNodeTopClass).getProperties());
return props;
}
private static <T extends ViewManager, V extends View> ViewManagerSetter<T, V> findManagerSetter(
Class<? extends ViewManager> managerClass) {
@SuppressWarnings("unchecked")
ViewManagerSetter<T, V> setter =
(ViewManagerSetter<T, V>) VIEW_MANAGER_SETTER_MAP.get(managerClass);
if (setter == null) {
setter = findGeneratedSetter(managerClass);
if (setter == null) {
setter = new FallbackViewManagerSetter<>(managerClass);
}
VIEW_MANAGER_SETTER_MAP.put(managerClass, setter);
}
return setter;
}
private static <T extends ReactShadowNode> ShadowNodeSetter<T> findNodeSetter(
Class<? extends ReactShadowNode> nodeClass) {
@SuppressWarnings("unchecked")
ShadowNodeSetter<T> setter = (ShadowNodeSetter<T>) SHADOW_NODE_SETTER_MAP.get(nodeClass);
if (setter == null) {
setter = findGeneratedSetter(nodeClass);
if (setter == null) {
setter = new FallbackShadowNodeSetter<>(nodeClass);
}
SHADOW_NODE_SETTER_MAP.put(nodeClass, setter);
}
return setter;
}
private static <T> T findGeneratedSetter(Class<?> cls) {
String clsName = cls.getName();
try {
Class<?> setterClass = Class.forName(clsName + "$$PropsSetter");
//noinspection unchecked
return (T) setterClass.newInstance();
} catch (ClassNotFoundException e) {
FLog.w(TAG, "Could not find generated setter for " + cls);
return null;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate methods getter for " + clsName, e);
}
}
private static class FallbackViewManagerSetter<T extends ViewManager, V extends View>
implements ViewManagerSetter<T, V> {
private final Map<String, ViewManagersPropertyCache.PropSetter> mPropSetters;
private FallbackViewManagerSetter(Class<? extends ViewManager> viewManagerClass) {
mPropSetters =
ViewManagersPropertyCache.getNativePropSettersForViewManagerClass(viewManagerClass);
}
@Override
public void setProperty(T manager, V v, String name, CatalystStylesDiffMap props) {
ViewManagersPropertyCache.PropSetter setter = mPropSetters.get(name);
if (setter != null) {
setter.updateViewProp(manager, v, props);
}
}
@Override
public Map<String, String> getProperties() {
Map<String, String> nativeProps = new HashMap<>();
for (ViewManagersPropertyCache.PropSetter setter : mPropSetters.values()) {
nativeProps.put(setter.getPropName(), setter.getPropType());
}
return nativeProps;
}
}
private static class FallbackShadowNodeSetter<T extends ReactShadowNode>
implements ShadowNodeSetter<T> {
private final Map<String, ViewManagersPropertyCache.PropSetter> mPropSetters;
private FallbackShadowNodeSetter(Class<? extends ReactShadowNode> shadowNodeClass) {
mPropSetters =
ViewManagersPropertyCache.getNativePropSettersForShadowNodeClass(shadowNodeClass);
}
@Override
public void setProperty(ReactShadowNode node, String name, CatalystStylesDiffMap props) {
ViewManagersPropertyCache.PropSetter setter = mPropSetters.get(name);
if (setter != null) {
setter.updateShadowNodeProp(node, props);
}
}
@Override
public Map<String, String> getProperties() {
Map<String, String> nativeProps = new HashMap<>();
for (ViewManagersPropertyCache.PropSetter setter : mPropSetters.values()) {
nativeProps.put(setter.getPropName(), setter.getPropType());
}
return nativeProps;
}
}
}

View File

@@ -0,0 +1,15 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.uimanager.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface ReactPropertyHolder {
}