Introducing view property annotation.
Differential Revision: D2475680 committer: Service User <svcscm@fb.com>
This commit is contained in:
parent
6c3fb77f30
commit
5623c831b3
|
@ -17,4 +17,8 @@ public class JSApplicationIllegalArgumentException extends JSApplicationCausedNa
|
|||
public JSApplicationIllegalArgumentException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
public JSApplicationIllegalArgumentException(String detailMessage, Throwable t) {
|
||||
super(detailMessage, t);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
throw new IllegalViewOperationException("Trying to update view with tag " + tag
|
||||
+ " which doesn't exist");
|
||||
}
|
||||
viewManager.updateView(viewToUpdate, props);
|
||||
viewManager.updateProperties(viewToUpdate, props);
|
||||
}
|
||||
|
||||
public void updateViewExtraData(int tag, Object extraData) {
|
||||
|
@ -179,7 +179,7 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
// creating another (potentially much more expensive) mapping from view to React tag
|
||||
view.setId(tag);
|
||||
if (initialProps != null) {
|
||||
viewManager.updateView(view, initialProps);
|
||||
viewManager.updateProperties(view, initialProps);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.uimanager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Use this annotation to annotate properties of native views that should be exposed to JS. This
|
||||
* annotation should only be used for setter methods of subclasses of {@link ViewManager}.
|
||||
*
|
||||
* Each annotated method should return {@code void} and take exactly two arguments: first being
|
||||
* a view instance to be updated and second a value that should be set.
|
||||
*
|
||||
* Allowed types of values are:
|
||||
* - primitives (int, boolean, double, float)
|
||||
* - {@link String}
|
||||
* - {@link Boolean}
|
||||
* - {@link ReadableArray}
|
||||
* - {@link ReadableMap}
|
||||
*
|
||||
* When property gets removed from the corresponding component in React, annotated setter will be
|
||||
* called with {@code null} in case of non-primitive value type or with a default value in case when
|
||||
* the value type is a primitive (use appropriate default field of this annotation to customize
|
||||
* default value that is going to be used: {@link #defaultBoolean}, {@link #defaultDouble}, etc.)
|
||||
*
|
||||
* Since in case of property removal for non-primitive value type setter will be called with value
|
||||
* set to {@code null} it's required that value type is annotated with {@link Nullable}.
|
||||
*
|
||||
* Note: Since boolean property type can be represented both as primitive and wrapped default value
|
||||
* set through {@link #defaultBoolean} is only respected for primitive type and for the wrapped type
|
||||
* {@code null} will be used as a default.
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ReactProp {
|
||||
|
||||
// Used as a default value for "customType" property as "null" is not allowed. Moreover, when this
|
||||
// const is used in annotation declaration compiler will actually create a copy of it, so
|
||||
// comparing it using "==" with this filed doesn't work either. We need to compare using "equals"
|
||||
// which means that this value needs to be unique.
|
||||
String USE_DEFAULT_TYPE = "__default_type__";
|
||||
|
||||
/**
|
||||
* Name of the property exposed to JS that will be updated using setter method annotated with
|
||||
* the given instance of {@code ReactProp} annotation
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Type of property that will be send to JS. In most of the cases {@code customType} should not be
|
||||
* set in which case default type will be send to JS based on the type of value argument from the
|
||||
* setter method (e.g. for {@code int}, {@code double} default is "number", for
|
||||
* {@code ReadableArray} it's "Array"). Custom type may be used when additional processing of the
|
||||
* value needs to be done in JS before sending it over the brige. A good example of that would be
|
||||
* backgroundColor property, which is expressed as a {@code String} in JS, but we use
|
||||
* {@code processColor} JS module to convert it to {@code int} before sending over the bridge.
|
||||
*/
|
||||
@Nullable String customType() default USE_DEFAULT_TYPE;
|
||||
|
||||
/**
|
||||
* Default value for property of type {@code double}. This value will be provided to property
|
||||
* setter method annotated with {@link ReactProp} if property with a given name gets removed
|
||||
* from the component description in JS
|
||||
*/
|
||||
double defaultDouble() default 0.0;
|
||||
|
||||
/**
|
||||
* Default value for property of type {@code float}. This value will be provided to property
|
||||
* setter method annotated with {@link ReactProp} if property with a given name gets removed
|
||||
* from the component description in JS
|
||||
*/
|
||||
float defaultFloat() default 0.0f;
|
||||
|
||||
/**
|
||||
* Default value for property of type {@code int}. This value will be provided to property
|
||||
* setter method annotated with {@link ReactProp} if property with a given name gets removed
|
||||
* from the component description in JS
|
||||
*/
|
||||
int defaultInt() default 0;
|
||||
|
||||
/**
|
||||
* Default value for property of type {@code boolean}. This value will be provided to property
|
||||
* setter method annotated with {@link ReactProp} if property with a given name gets removed
|
||||
* from the component description in JS
|
||||
*/
|
||||
boolean defaultBoolean() default false;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.uimanager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Use this annotation to annotate group of properties of native views that should be exposed to JS.
|
||||
* This annotation should only be used for setter methods of subclasses of {@link ViewManager}. It's
|
||||
* a batched version of {@link ReactProp} annotation (please see documentation of {@link ReactProp}
|
||||
* for more details about how this annotation can be used).
|
||||
*
|
||||
* This annotation is meant to be used for a group of similar properties. That's why it support only
|
||||
* a set of properties of the same type. A good example is supporting "border", where we have 7
|
||||
* variations of that property ("borderLeft", "borderHorizontal", etc.) and very similar code for
|
||||
* handling each of those.
|
||||
*
|
||||
* Each annotated method should return {@code void} and take exactly three arguments: first being
|
||||
* a view instance to be updated, second should be of type int and will represent index in the
|
||||
* group of the property being updated. Last, third argument represent the value that should be set.
|
||||
*
|
||||
*
|
||||
* Currently only {@code int}, {@code float} and {@link String} value types are supported.
|
||||
*
|
||||
* In case when property has been removed from the corresponding react component annotated setter
|
||||
* will be called and default value will be provided as a value parameter. Default value can be
|
||||
* customize using {@link #defaultInt} or {@link #defaultFloat} in the case when property is of
|
||||
* one of primitive types. In case when {@link String} is the property type {@code null} value will
|
||||
* be provided as a default.
|
||||
*/
|
||||
@Retention(RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ReactPropGroup {
|
||||
|
||||
// Used as a default value for "customType" property as "null" is not allowed. Moreover, when this
|
||||
// const is used in annotation declaration compiler will actually create a copy of it, so
|
||||
// comparing it using "==" with this filed doesn't work either. We need to compare using "equals"
|
||||
// which means that this value needs to be unique.
|
||||
String USE_DEFAULT_TYPE = "__default_type__";
|
||||
|
||||
/**
|
||||
* Array of names of properties exposed to JS that will be updated using setter method annotated
|
||||
* with the given instance of {@code ReactPropGroup} annotation
|
||||
*/
|
||||
String[] names();
|
||||
|
||||
/**
|
||||
* Type of property that will be send to JS. In most of the cases {@code customType} should not be
|
||||
* set in which case default type will be send to JS based on the type of value argument from the
|
||||
* setter method (e.g. for {@code int}, {@code float} default is "number"). Custom type may be
|
||||
* used when additional processing of the value needs to be done in JS before sending it over the
|
||||
* bridge. A good example of that would be backgroundColor property, which is expressed as a
|
||||
* {@code String} in JS, but we use {@code processColor} JS module to convert it to {@code int}
|
||||
* before sending over the bridge.
|
||||
*/
|
||||
@Nullable String customType() default USE_DEFAULT_TYPE;
|
||||
|
||||
/**
|
||||
* Default value for property of type {@code float}. This value will be provided to property
|
||||
* setter method annotated with {@link ReactPropGroup} if property with a given name gets removed
|
||||
* from the component description in JS
|
||||
*/
|
||||
float defaultFloat() default 0.0f;
|
||||
|
||||
/**
|
||||
* Default value for property of type {@code int}. This value will be provided to property
|
||||
* setter method annotated with {@link ReactPropGroup} if property with a given name gets removed
|
||||
* from the component description in JS
|
||||
*/
|
||||
int defaultInt() default 0;
|
||||
}
|
|
@ -64,13 +64,9 @@ import com.facebook.react.common.MapBuilder;
|
|||
if (viewManagerCommands != null) {
|
||||
viewManagerConstants.put("Commands", viewManagerCommands);
|
||||
}
|
||||
Map<String, UIProp.Type> viewManagerNativeProps = viewManager.getNativeProps();
|
||||
Map<String, String> viewManagerNativeProps = viewManager.getNativeProps();
|
||||
if (!viewManagerNativeProps.isEmpty()) {
|
||||
Map<String, String> nativeProps = new HashMap<>();
|
||||
for (Map.Entry<String, UIProp.Type> entry : viewManagerNativeProps.entrySet()) {
|
||||
nativeProps.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
viewManagerConstants.put("NativeProps", nativeProps);
|
||||
viewManagerConstants.put("NativeProps", viewManagerNativeProps);
|
||||
}
|
||||
if (!viewManagerConstants.isEmpty()) {
|
||||
constants.put(viewManager.getName(), viewManagerConstants);
|
||||
|
|
|
@ -18,10 +18,11 @@ import java.util.Map;
|
|||
import android.view.View;
|
||||
|
||||
import com.facebook.csslayout.CSSNode;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySeyIterator;
|
||||
import com.facebook.react.touch.CatalystInterceptingViewGroup;
|
||||
import com.facebook.react.touch.JSResponderHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
||||
/**
|
||||
* Class responsible for knowing how to create and update catalyst Views of a given type. It is also
|
||||
|
@ -32,6 +33,28 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
|
|||
|
||||
private static final Map<Class, Map<String, UIProp.Type>> CLASS_PROP_CACHE = new HashMap<>();
|
||||
|
||||
public final void updateProperties(T viewToUpdate, CatalystStylesDiffMap props) {
|
||||
Map<String, ViewManagersPropertyCache.PropSetter> propSetters =
|
||||
ViewManagersPropertyCache.getNativePropSettersForClass(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.updateProp(this, viewToUpdate, props);
|
||||
} else {
|
||||
missingSetters = true;
|
||||
}
|
||||
}
|
||||
if (missingSetters) {
|
||||
updateView(viewToUpdate, props);
|
||||
}
|
||||
onAfterUpdateTransaction(viewToUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a view and installs event emitters on it.
|
||||
*/
|
||||
|
@ -84,8 +107,22 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
|
|||
* Subclass should use this method to populate native view with updated style properties. In case
|
||||
* when a certain property is present in {@param props} map but the value is null, this property
|
||||
* should be reset to the default value
|
||||
*
|
||||
* TODO(krzysztof) This method should be replaced by updateProperties and removed completely after
|
||||
* all view managers adapt @ReactProp
|
||||
*/
|
||||
public abstract void updateView(T root, CatalystStylesDiffMap props);
|
||||
@Deprecated
|
||||
protected void updateView(T root, CatalystStylesDiffMap props) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback that will be triggered after all properties are updated in current update transaction
|
||||
* (all @ReactProp handlers for properties updated in current transaction have been called). If
|
||||
* you want to override this method you should call super.onAfterUpdateTransaction from it as
|
||||
* the parent class of the ViewManager may rely on callback being executed.
|
||||
*/
|
||||
protected void onAfterUpdateTransaction(T view) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can implement this method to receive an optional extra data enqueued from the
|
||||
|
@ -178,13 +215,15 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
|
|||
return null;
|
||||
}
|
||||
|
||||
public Map<String, UIProp.Type> getNativeProps() {
|
||||
Map<String, UIProp.Type> nativeProps = new HashMap<>();
|
||||
public Map<String, String> getNativeProps() {
|
||||
// TODO(krzysztof): This method will just delegate to ViewManagersPropertyRegistry once
|
||||
// refactoring is finished
|
||||
Class cls = getClass();
|
||||
Map<String, String> nativeProps = ViewManagersPropertyCache.getNativePropsForClass(cls);
|
||||
while (cls.getSuperclass() != null) {
|
||||
Map<String, UIProp.Type> props = getNativePropsForClass(cls);
|
||||
for (Map.Entry<String, UIProp.Type> entry : props.entrySet()) {
|
||||
nativeProps.put(entry.getKey(), entry.getValue());
|
||||
nativeProps.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
|
@ -192,6 +231,7 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode> {
|
|||
}
|
||||
|
||||
private Map<String, UIProp.Type> getNativePropsForClass(Class cls) {
|
||||
// TODO(krzysztof): Blow up this method once refactoring is finished
|
||||
Map<String, UIProp.Type> props = CLASS_PROP_CACHE.get(cls);
|
||||
if (props != null) {
|
||||
return props;
|
||||
|
|
|
@ -0,0 +1,405 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
package com.facebook.react.uimanager;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
/**
|
||||
* This class is responsible for holding view manager property setters and is used in a process of
|
||||
* updating views with the new properties set in JS.
|
||||
*/
|
||||
/*package*/ class ViewManagersPropertyCache {
|
||||
|
||||
private static final Map<Class, Map<String, PropSetter>> CLASS_PROPS_CACHE = new HashMap<>();
|
||||
private static final Map<String, PropSetter> EMPTY_PROPS_MAP = new HashMap<>();
|
||||
|
||||
/*package*/ static abstract class PropSetter {
|
||||
|
||||
protected final String mPropName;
|
||||
protected final String mPropType;
|
||||
protected final Method mSetter;
|
||||
|
||||
// The following two constructors make it easy to reuse code responsible for setting property
|
||||
// type. It's probably not a best design but this API is not exposed and since we can't use
|
||||
// inheritance for annotation classes it's the easiest way to avoid creating an extra base class
|
||||
// just to support group and non-group setters.
|
||||
private PropSetter(ReactProp prop, String defaultType, Method setter) {
|
||||
mPropName = prop.name();
|
||||
mPropType = ReactProp.USE_DEFAULT_TYPE.equals(prop.customType()) ?
|
||||
defaultType : prop.customType();
|
||||
mSetter = setter;
|
||||
}
|
||||
|
||||
private PropSetter(ReactPropGroup prop, String defaultType, Method setter, int index) {
|
||||
mPropName = prop.names()[index];
|
||||
mPropType = ReactPropGroup.USE_DEFAULT_TYPE.equals(prop.customType()) ?
|
||||
defaultType : prop.customType();
|
||||
mSetter = setter;
|
||||
}
|
||||
|
||||
public String getPropName() {
|
||||
return mPropName;
|
||||
}
|
||||
|
||||
public String getPropType() {
|
||||
return mPropType;
|
||||
}
|
||||
|
||||
public void updateProp(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) {
|
||||
try {
|
||||
updateProperty(viewManager, viewToUpdate, props);
|
||||
} catch (Throwable t) {
|
||||
FLog.e(ViewManager.class, "Error while updating prop " + mPropName, t);
|
||||
throw new JSApplicationIllegalArgumentException("Error while updating property '" +
|
||||
mPropName + "' of a view managed by: " + viewManager.getName(), t);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException;
|
||||
}
|
||||
|
||||
private static class IntPropSetter extends PropSetter {
|
||||
|
||||
private final int mDefaultValue;
|
||||
|
||||
public IntPropSetter(ReactProp prop, Method setter, int defaultValue) {
|
||||
super(prop, "number", setter);
|
||||
mDefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getInt(mPropName, mDefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoublePropSetter extends PropSetter {
|
||||
|
||||
private final double mDefaultValue;
|
||||
|
||||
public DoublePropSetter(ReactProp prop, Method setter, double defaultValue) {
|
||||
super(prop, "number", setter);
|
||||
mDefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getDouble(mPropName, mDefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static class BooleanPropSetter extends PropSetter {
|
||||
|
||||
private final boolean mDefaultValue;
|
||||
|
||||
public BooleanPropSetter(ReactProp prop, Method setter, boolean defaultValue) {
|
||||
super(prop, "boolean", setter);
|
||||
mDefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getBoolean(mPropName, mDefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static class FloatPropSetter extends PropSetter {
|
||||
|
||||
private final float mDefaultValue;
|
||||
|
||||
public FloatPropSetter(ReactProp prop, Method setter, float defaultValue) {
|
||||
super(prop, "number", setter);
|
||||
mDefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getFloat(mPropName, mDefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArrayPropSetter extends PropSetter {
|
||||
|
||||
public ArrayPropSetter(ReactProp prop, Method setter) {
|
||||
super(prop, "Array", setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getArray(mPropName));
|
||||
}
|
||||
}
|
||||
|
||||
private static class MapPropSetter extends PropSetter {
|
||||
|
||||
public MapPropSetter(ReactProp prop, Method setter) {
|
||||
super(prop, "Map", setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getMap(mPropName));
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringPropSetter extends PropSetter {
|
||||
|
||||
public StringPropSetter(ReactProp prop, Method setter) {
|
||||
super(prop, "String", setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, props.getString(mPropName));
|
||||
}
|
||||
}
|
||||
|
||||
private static class BoxedBooleanPropSetter extends PropSetter {
|
||||
|
||||
public BoxedBooleanPropSetter(ReactProp prop, Method setter) {
|
||||
super(prop, "boolean", setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
Boolean value = null;
|
||||
if (!props.isNull(mPropName)) {
|
||||
value = props.getBoolean(mPropName, false) ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
mSetter.invoke(viewManager, viewToUpdate, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BoxedIntPropSetter extends PropSetter {
|
||||
|
||||
public BoxedIntPropSetter(ReactProp prop, Method setter) {
|
||||
super(prop, "number", setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
Integer value = null;
|
||||
if (!props.isNull(mPropName)) {
|
||||
value = props.getInt(mPropName, /* ignored */ 0);
|
||||
}
|
||||
mSetter.invoke(viewManager, viewToUpdate, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class GroupSetter extends PropSetter {
|
||||
|
||||
protected final int mIndex;
|
||||
|
||||
protected GroupSetter(ReactPropGroup prop, String defaultType, Method setter, int index) {
|
||||
super(prop, defaultType, setter, index);
|
||||
mIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
private static class GroupIntSetter extends GroupSetter {
|
||||
|
||||
private final int mDefaultValue;
|
||||
|
||||
public GroupIntSetter(ReactPropGroup prop, Method setter, int index, int defaultValue) {
|
||||
super(prop, "number", setter, index);
|
||||
mDefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, mIndex, props.getInt(mPropName, mDefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static class GroupFloatSetter extends GroupSetter {
|
||||
|
||||
private final float mDefaultValue;
|
||||
|
||||
public GroupFloatSetter(ReactPropGroup prop, Method setter, int index, float defaultValue) {
|
||||
super(prop, "number", setter, index);
|
||||
mDefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(viewManager, viewToUpdate, mIndex, props.getFloat(mPropName, mDefaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static class GroupBoxedIntSetter extends GroupSetter {
|
||||
|
||||
protected GroupBoxedIntSetter(ReactPropGroup prop, Method setter, int index) {
|
||||
super(prop, "number", setter, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateProperty(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
CatalystStylesDiffMap props) throws InvocationTargetException, IllegalAccessException {
|
||||
mSetter.invoke(
|
||||
viewManager,
|
||||
viewToUpdate,
|
||||
mIndex,
|
||||
props.isNull(mPropName) ? null : props.getInt(mPropName, /* unused */ 0));
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ static Map<String, String> getNativePropsForClass(
|
||||
Class<? extends ViewManager> topLevelClass) {
|
||||
Map<String, String> nativeProps = new HashMap<>();
|
||||
Map<String, PropSetter> props = getNativePropSettersForClass(topLevelClass);
|
||||
for (PropSetter setter : props.values()) {
|
||||
nativeProps.put(setter.getPropName(), setter.getPropType());
|
||||
}
|
||||
return nativeProps;
|
||||
}
|
||||
|
||||
/*package*/ static Map<String, PropSetter> getNativePropSettersForClass(
|
||||
Class<? extends ViewManager> cls) {
|
||||
if (cls == ViewManager.class) {
|
||||
return EMPTY_PROPS_MAP;
|
||||
}
|
||||
Map<String, PropSetter> props = CLASS_PROPS_CACHE.get(cls);
|
||||
if (props != null) {
|
||||
return props;
|
||||
}
|
||||
props = new HashMap<>(
|
||||
getNativePropSettersForClass((Class<? extends ViewManager>) cls.getSuperclass()));
|
||||
for (Method method : cls.getDeclaredMethods()) {
|
||||
{
|
||||
ReactProp annotation = method.getAnnotation(ReactProp.class);
|
||||
if (annotation != null) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 2) {
|
||||
throw new RuntimeException("Wrong number of args for prop setter: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
}
|
||||
if (!View.class.isAssignableFrom(paramTypes[0])) {
|
||||
throw new RuntimeException("First param should be a view subclass to be updated: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
}
|
||||
Class<?> propTypeClass = paramTypes[1];
|
||||
PropSetter propSetter;
|
||||
if (propTypeClass == boolean.class) {
|
||||
propSetter =
|
||||
new BooleanPropSetter(annotation, method, annotation.defaultBoolean());
|
||||
} else if (propTypeClass == int.class) {
|
||||
propSetter = new IntPropSetter(annotation, method, annotation.defaultInt());
|
||||
} else if (propTypeClass == float.class) {
|
||||
propSetter = new FloatPropSetter(annotation, method, annotation.defaultFloat());
|
||||
} else if (propTypeClass == double.class) {
|
||||
propSetter =
|
||||
new DoublePropSetter(annotation, method, annotation.defaultDouble());
|
||||
} else if (propTypeClass == String.class) {
|
||||
propSetter = new StringPropSetter(annotation, method);
|
||||
} else if (propTypeClass == Boolean.class) {
|
||||
propSetter = new BoxedBooleanPropSetter(annotation, method);
|
||||
} else if (propTypeClass == Integer.class) {
|
||||
propSetter = new BoxedIntPropSetter(annotation, method);
|
||||
} else if (propTypeClass == ReadableArray.class) {
|
||||
propSetter = new ArrayPropSetter(annotation, method);
|
||||
} else if (propTypeClass == ReadableMap.class) {
|
||||
propSetter = new MapPropSetter(annotation, method);
|
||||
} else {
|
||||
throw new RuntimeException("Unrecognized type");
|
||||
}
|
||||
props.put(annotation.name(), propSetter);
|
||||
}
|
||||
}
|
||||
{
|
||||
ReactPropGroup annotation = method.getAnnotation(ReactPropGroup.class);
|
||||
if (annotation != null) {
|
||||
Class<?> [] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 3) {
|
||||
throw new RuntimeException("Wrong number of args for group prop setter: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
}
|
||||
if (!View.class.isAssignableFrom(paramTypes[0])) {
|
||||
throw new RuntimeException("First param should be a view subclass to be updated: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
}
|
||||
if (paramTypes[1] != int.class) {
|
||||
throw new RuntimeException("Second argument should be property index: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
}
|
||||
Class<?> propTypeClass = paramTypes[2];
|
||||
String[] names = annotation.names();
|
||||
if (propTypeClass == int.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new GroupIntSetter(annotation, method, i, annotation.defaultInt()));
|
||||
}
|
||||
} else if (propTypeClass == float.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new GroupFloatSetter(annotation, method, i, annotation.defaultFloat()));
|
||||
}
|
||||
} else if (propTypeClass == Integer.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new GroupBoxedIntSetter(annotation, method, i));
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unrecognized type: " + paramTypes[2] + " for method: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CLASS_PROPS_CACHE.put(cls, props);
|
||||
return props;
|
||||
}
|
||||
}
|
|
@ -69,15 +69,15 @@ public class ReactViewManager extends ViewGroupManager<ReactViewGroup> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<String, UIProp.Type> getNativeProps() {
|
||||
Map<String, UIProp.Type> nativeProps = super.getNativeProps();
|
||||
public Map<String, String> getNativeProps() {
|
||||
Map<String, String> nativeProps = super.getNativeProps();
|
||||
Map<String, UIProp.Type> baseProps = BaseViewPropertyApplicator.getCommonProps();
|
||||
for (Map.Entry<String, UIProp.Type> entry : baseProps.entrySet()) {
|
||||
nativeProps.put(entry.getKey(), entry.getValue());
|
||||
nativeProps.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
for (int i = 0; i < SPACING_TYPES.length; i++) {
|
||||
nativeProps.put(ViewProps.BORDER_WIDTHS[i], UIProp.Type.NUMBER);
|
||||
nativeProps.put(PROPS_BORDER_COLOR[i], UIProp.Type.STRING);
|
||||
nativeProps.put(ViewProps.BORDER_WIDTHS[i], UIProp.Type.NUMBER.toString());
|
||||
nativeProps.put(PROPS_BORDER_COLOR[i], UIProp.Type.STRING.toString());
|
||||
}
|
||||
return nativeProps;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue