Skip to content

Commit

Permalink
Extract @ReactProp annotated properties from shadow nodes.
Browse files Browse the repository at this point in the history
Differential Revision: D2536419

fb-gh-sync-id: 643499d4fdcb481349dad1701391059d2362984e
  • Loading branch information
kmagiera authored and facebook-github-bot-2 committed Oct 13, 2015
1 parent 3d22547 commit 05015e8
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
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.ReadableMapKeySeyIterator;

/**
* Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily
Expand All @@ -23,7 +26,7 @@
* {@link CSSNode} by adding additional capabilities.
*
* 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.
*
* Subclasses of {@link ReactShadowNode} should be created only from {@link ViewManager} that
Expand Down Expand Up @@ -159,7 +162,34 @@ public ReactShadowNode removeChildAt(int i) {
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);
}

Expand Down Expand Up @@ -235,7 +265,7 @@ protected void setThemedContext(ThemedReactContext themedContext) {
mThemedContext = themedContext;
}

/* package */ void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
mShouldNotifyOnLayout = shouldNotifyOnLayout;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ public Map<String, String> getNativeProps() {
// refactoring is finished
Class cls = getClass();
Map<String, String> nativeProps =
ViewManagersPropertyCache.getNativePropsForView(cls);
ViewManagersPropertyCache.getNativePropsForView(cls, getShadowNodeClass());
while (cls.getSuperclass() != null) {
Map<String, UIProp.Type> props = getNativePropsForClass(cls);
for (Map.Entry<String, UIProp.Type> entry : props.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
// thread sequentially
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[] SHADOW_ARGS = new Object[1];
private static final Object[] SHADOW_GROUP_ARGS = new Object[2];

private PropSetter(ReactProp prop, String defaultType, Method setter) {
mPropName = prop.name();
Expand Down Expand Up @@ -83,6 +85,25 @@ public void updateViewProp(
}
}

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);
}

Expand Down Expand Up @@ -227,7 +248,8 @@ public BoxedIntPropSetter(ReactPropGroup prop, Method setter, int index) {
}

/*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, PropSetter> viewManagerProps =
Expand All @@ -236,12 +258,18 @@ public BoxedIntPropSetter(ReactPropGroup prop, Method setter, int index) {
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;
}

/**
* 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.
*/
/*package*/ static Map<String, PropSetter> getNativePropSettersForViewManagerClass(
Expand All @@ -263,6 +291,30 @@ public BoxedIntPropSetter(ReactPropGroup prop, Method setter, int index) {
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(
ReactProp annotation,
Method method,
Expand Down Expand Up @@ -360,4 +412,34 @@ private static void extractPropSettersFromViewManagerClassDefinition(
}
}
}

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);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public void measure(CSSNode node, float width, MeasureOutput measureOutput) {
}

@Override
public void updateProperties(CatalystStylesDiffMap styles) {
super.updateProperties(styles);
public void updateShadowNode(CatalystStylesDiffMap styles) {
super.updateShadowNode(styles);

if (styles.hasKey(ReactProgressBarViewManager.PROP_STYLE)) {
String style = styles.getString(ReactProgressBarViewManager.PROP_STYLE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ protected void markUpdated() {
}

@Override
public void updateProperties(CatalystStylesDiffMap styles) {
super.updateProperties(styles);
public void updateShadowNode(CatalystStylesDiffMap styles) {
super.updateShadowNode(styles);

if (styles.hasKey(PROP_TEXT)) {
mText = styles.getString(PROP_TEXT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public void onBeforeLayout() {
}

@Override
public void updateProperties(CatalystStylesDiffMap styles) {
super.updateProperties(styles);
public void updateShadowNode(CatalystStylesDiffMap styles) {
super.updateShadowNode(styles);
if (styles.hasKey(ViewProps.FONT_SIZE)) {
float fontSize = styles.getFloat(ViewProps.FONT_SIZE, ViewDefaults.FONT_SIZE_SP);
mFontSize = (int) Math.ceil(PixelUtil.toPixelFromSP(fontSize));
Expand Down

0 comments on commit 05015e8

Please sign in to comment.