Extract @ReactProp annotated properties from shadow nodes.
Differential Revision: D2536419 fb-gh-sync-id: 643499d4fdcb481349dad1701391059d2362984e
This commit is contained in:
Родитель
3d22547e31
Коммит
05015e8d36
|
@ -12,9 +12,12 @@ package com.facebook.react.uimanager;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.facebook.csslayout.CSSNode;
|
import com.facebook.csslayout.CSSNode;
|
||||||
import com.facebook.infer.annotation.Assertions;
|
import com.facebook.infer.annotation.Assertions;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.ReadableMapKeySeyIterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily
|
* Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily
|
||||||
|
@ -23,7 +26,7 @@ import com.facebook.infer.annotation.Assertions;
|
||||||
* {@link CSSNode} by adding additional capabilities.
|
* {@link CSSNode} by adding additional capabilities.
|
||||||
*
|
*
|
||||||
* Instances of this class receive property updates from JS via @{link UIManagerModule}. Subclasses
|
* Instances of this class receive property updates from JS via @{link UIManagerModule}. Subclasses
|
||||||
* may use {@link #updateProperties} to persist some of the updated fields in the node instance that
|
* may use {@link #updateShadowNode} to persist some of the updated fields in the node instance that
|
||||||
* corresponds to a particular view type.
|
* corresponds to a particular view type.
|
||||||
*
|
*
|
||||||
* Subclasses of {@link ReactShadowNode} should be created only from {@link ViewManager} that
|
* Subclasses of {@link ReactShadowNode} should be created only from {@link ViewManager} that
|
||||||
|
@ -159,7 +162,34 @@ public class ReactShadowNode extends CSSNode {
|
||||||
public void onBeforeLayout() {
|
public void onBeforeLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateProperties(CatalystStylesDiffMap styles) {
|
public final void updateProperties(CatalystStylesDiffMap props) {
|
||||||
|
Map<String, ViewManagersPropertyCache.PropSetter> propSetters =
|
||||||
|
ViewManagersPropertyCache.getNativePropSettersForShadowNodeClass(getClass());
|
||||||
|
ReadableMap propMap = props.mBackingMap;
|
||||||
|
ReadableMapKeySeyIterator iterator = propMap.keySetIterator();
|
||||||
|
// TODO(krzysztof): Remove missingSetters code once all views are migrated to @ReactProp
|
||||||
|
boolean missingSetters = false;
|
||||||
|
while (iterator.hasNextKey()) {
|
||||||
|
String key = iterator.nextKey();
|
||||||
|
ViewManagersPropertyCache.PropSetter setter = propSetters.get(key);
|
||||||
|
if (setter != null) {
|
||||||
|
setter.updateShadowNodeProp(this, props);
|
||||||
|
} else {
|
||||||
|
missingSetters = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (missingSetters) {
|
||||||
|
updateShadowNode(props);
|
||||||
|
}
|
||||||
|
onAfterUpdateTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAfterUpdateTransaction() {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void updateShadowNode(CatalystStylesDiffMap styles) {
|
||||||
BaseCSSPropertyApplicator.applyCSSProperties(this, styles);
|
BaseCSSPropertyApplicator.applyCSSProperties(this, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +265,7 @@ public class ReactShadowNode extends CSSNode {
|
||||||
mThemedContext = themedContext;
|
mThemedContext = themedContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
|
public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
|
||||||
mShouldNotifyOnLayout = shouldNotifyOnLayout;
|
mShouldNotifyOnLayout = shouldNotifyOnLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
|
||||||
// refactoring is finished
|
// refactoring is finished
|
||||||
Class cls = getClass();
|
Class cls = getClass();
|
||||||
Map<String, String> nativeProps =
|
Map<String, String> nativeProps =
|
||||||
ViewManagersPropertyCache.getNativePropsForView(cls);
|
ViewManagersPropertyCache.getNativePropsForView(cls, getShadowNodeClass());
|
||||||
while (cls.getSuperclass() != null) {
|
while (cls.getSuperclass() != null) {
|
||||||
Map<String, UIProp.Type> props = getNativePropsForClass(cls);
|
Map<String, UIProp.Type> props = getNativePropsForClass(cls);
|
||||||
for (Map.Entry<String, UIProp.Type> entry : props.entrySet()) {
|
for (Map.Entry<String, UIProp.Type> entry : props.entrySet()) {
|
||||||
|
|
|
@ -36,6 +36,8 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
// thread sequentially
|
// thread sequentially
|
||||||
private static final Object[] VIEW_MGR_ARGS = new Object[2];
|
private static final Object[] VIEW_MGR_ARGS = new Object[2];
|
||||||
private static final Object[] VIEW_MGR_GROUP_ARGS = new Object[3];
|
private static final Object[] VIEW_MGR_GROUP_ARGS = new Object[3];
|
||||||
|
private static final Object[] SHADOW_ARGS = new Object[1];
|
||||||
|
private static final Object[] SHADOW_GROUP_ARGS = new Object[2];
|
||||||
|
|
||||||
private PropSetter(ReactProp prop, String defaultType, Method setter) {
|
private PropSetter(ReactProp prop, String defaultType, Method setter) {
|
||||||
mPropName = prop.name();
|
mPropName = prop.name();
|
||||||
|
@ -83,6 +85,25 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateShadowNodeProp(
|
||||||
|
ReactShadowNode nodeToUpdate,
|
||||||
|
CatalystStylesDiffMap props) {
|
||||||
|
try {
|
||||||
|
if (mIndex == null) {
|
||||||
|
SHADOW_ARGS[0] = extractProperty(props);
|
||||||
|
mSetter.invoke(nodeToUpdate, SHADOW_ARGS);
|
||||||
|
} else {
|
||||||
|
SHADOW_GROUP_ARGS[0] = mIndex;
|
||||||
|
SHADOW_GROUP_ARGS[1] = extractProperty(props);
|
||||||
|
mSetter.invoke(nodeToUpdate, SHADOW_GROUP_ARGS);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
FLog.e(ViewManager.class, "Error while updating prop " + mPropName, t);
|
||||||
|
throw new JSApplicationIllegalArgumentException("Error while updating property '" +
|
||||||
|
mPropName + "' in shadow node of type: " + nodeToUpdate.getViewClass(), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract @Nullable Object extractProperty(CatalystStylesDiffMap props);
|
protected abstract @Nullable Object extractProperty(CatalystStylesDiffMap props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +248,8 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ static Map<String, String> getNativePropsForView(
|
/*package*/ static Map<String, String> getNativePropsForView(
|
||||||
Class<? extends ViewManager> viewManagerTopClass) {
|
Class<? extends ViewManager> viewManagerTopClass,
|
||||||
|
Class<? extends ReactShadowNode> shadowNodeTopClass) {
|
||||||
Map<String, String> nativeProps = new HashMap<>();
|
Map<String, String> nativeProps = new HashMap<>();
|
||||||
|
|
||||||
Map<String, PropSetter> viewManagerProps =
|
Map<String, PropSetter> viewManagerProps =
|
||||||
|
@ -236,12 +258,18 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
nativeProps.put(setter.getPropName(), setter.getPropType());
|
nativeProps.put(setter.getPropName(), setter.getPropType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, PropSetter> shadowNodeProps =
|
||||||
|
getNativePropSettersForShadowNodeClass(shadowNodeTopClass);
|
||||||
|
for (PropSetter setter : shadowNodeProps.values()) {
|
||||||
|
nativeProps.put(setter.getPropName(), setter.getPropType());
|
||||||
|
}
|
||||||
|
|
||||||
return nativeProps;
|
return nativeProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns map from property name to setter instances for all the property setters annotated with
|
* Returns map from property name to setter instances for all the property setters annotated with
|
||||||
* {@link ReactProp} in the given {@link ViewManager} plus all the setter declared by it's
|
* {@link ReactProp} in the given {@link ViewManager} class plus all the setter declared by its
|
||||||
* parent classes.
|
* parent classes.
|
||||||
*/
|
*/
|
||||||
/*package*/ static Map<String, PropSetter> getNativePropSettersForViewManagerClass(
|
/*package*/ static Map<String, PropSetter> getNativePropSettersForViewManagerClass(
|
||||||
|
@ -263,6 +291,30 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns map from property name to setter instances for all the property setters annotated with
|
||||||
|
* {@link ReactProp} (or {@link ReactPropGroup} in the given {@link ReactShadowNode} subclass plus
|
||||||
|
* all the setters declared by its parent classes up to {@link ReactShadowNode} which is treated
|
||||||
|
* as a base class.
|
||||||
|
*/
|
||||||
|
/*package*/ static Map<String, PropSetter> getNativePropSettersForShadowNodeClass(
|
||||||
|
Class<? extends ReactShadowNode> cls) {
|
||||||
|
if (cls == ReactShadowNode.class) {
|
||||||
|
return EMPTY_PROPS_MAP;
|
||||||
|
}
|
||||||
|
Map<String, PropSetter> props = CLASS_PROPS_CACHE.get(cls);
|
||||||
|
if (props != null) {
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
// This is to include all the setters from parent classes up to ReactShadowNode class
|
||||||
|
props = new HashMap<>(
|
||||||
|
getNativePropSettersForShadowNodeClass(
|
||||||
|
(Class<? extends ReactShadowNode>) cls.getSuperclass()));
|
||||||
|
extractPropSettersFromShadowNodeClassDefinition(cls, props);
|
||||||
|
CLASS_PROPS_CACHE.put(cls, props);
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
private static PropSetter createPropSetter(
|
private static PropSetter createPropSetter(
|
||||||
ReactProp annotation,
|
ReactProp annotation,
|
||||||
Method method,
|
Method method,
|
||||||
|
@ -360,4 +412,34 @@ import com.facebook.react.bridge.ReadableMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void extractPropSettersFromShadowNodeClassDefinition(
|
||||||
|
Class<? extends ReactShadowNode> cls,
|
||||||
|
Map<String, PropSetter> props) {
|
||||||
|
for (Method method : cls.getDeclaredMethods()) {
|
||||||
|
ReactProp annotation = method.getAnnotation(ReactProp.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
Class<?>[] paramTypes = method.getParameterTypes();
|
||||||
|
if (paramTypes.length != 1) {
|
||||||
|
throw new RuntimeException("Wrong number of args for prop setter: " +
|
||||||
|
cls.getName() + "#" + method.getName());
|
||||||
|
}
|
||||||
|
props.put(annotation.name(), createPropSetter(annotation, method, paramTypes[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactPropGroup groupAnnotation = method.getAnnotation(ReactPropGroup.class);
|
||||||
|
if (groupAnnotation != null) {
|
||||||
|
Class<?> [] paramTypes = method.getParameterTypes();
|
||||||
|
if (paramTypes.length != 2) {
|
||||||
|
throw new RuntimeException("Wrong number of args for group prop setter: " +
|
||||||
|
cls.getName() + "#" + method.getName());
|
||||||
|
}
|
||||||
|
if (paramTypes[0] != int.class) {
|
||||||
|
throw new RuntimeException("Second argument should be property index: " +
|
||||||
|
cls.getName() + "#" + method.getName());
|
||||||
|
}
|
||||||
|
createPropSetters(groupAnnotation, method, paramTypes[1], props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,8 @@ public class ProgressBarShadowNode extends ReactShadowNode implements CSSNode.Me
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProperties(CatalystStylesDiffMap styles) {
|
public void updateShadowNode(CatalystStylesDiffMap styles) {
|
||||||
super.updateProperties(styles);
|
super.updateShadowNode(styles);
|
||||||
|
|
||||||
if (styles.hasKey(ReactProgressBarViewManager.PROP_STYLE)) {
|
if (styles.hasKey(ReactProgressBarViewManager.PROP_STYLE)) {
|
||||||
String style = styles.getString(ReactProgressBarViewManager.PROP_STYLE);
|
String style = styles.getString(ReactProgressBarViewManager.PROP_STYLE);
|
||||||
|
|
|
@ -288,8 +288,8 @@ public class ReactTextShadowNode extends ReactShadowNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProperties(CatalystStylesDiffMap styles) {
|
public void updateShadowNode(CatalystStylesDiffMap styles) {
|
||||||
super.updateProperties(styles);
|
super.updateShadowNode(styles);
|
||||||
|
|
||||||
if (styles.hasKey(PROP_TEXT)) {
|
if (styles.hasKey(PROP_TEXT)) {
|
||||||
mText = styles.getString(PROP_TEXT);
|
mText = styles.getString(PROP_TEXT);
|
||||||
|
|
|
@ -97,8 +97,8 @@ import com.facebook.react.views.text.ReactTextShadowNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProperties(CatalystStylesDiffMap styles) {
|
public void updateShadowNode(CatalystStylesDiffMap styles) {
|
||||||
super.updateProperties(styles);
|
super.updateShadowNode(styles);
|
||||||
if (styles.hasKey(ViewProps.FONT_SIZE)) {
|
if (styles.hasKey(ViewProps.FONT_SIZE)) {
|
||||||
float fontSize = styles.getFloat(ViewProps.FONT_SIZE, ViewDefaults.FONT_SIZE_SP);
|
float fontSize = styles.getFloat(ViewProps.FONT_SIZE, ViewDefaults.FONT_SIZE_SP);
|
||||||
mFontSize = (int) Math.ceil(PixelUtil.toPixelFromSP(fontSize));
|
mFontSize = (int) Math.ceil(PixelUtil.toPixelFromSP(fontSize));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче