/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.uimanager import android.view.View import com.facebook.common.logging.FLog import com.facebook.react.bridge.ReadableArray import com.facebook.react.uimanager.ViewManagersPropertyCache.PropSetter import java.util.HashMap @Suppress("DEPRECATION") public object ViewManagerPropertyUpdater { public fun interface Settable { public fun getProperties(props: MutableMap) } @Suppress("FINITE_BOUNDS_VIOLATION_IN_JAVA") public interface ViewManagerSetter, V : View> : Settable { public fun setProperty(manager: T, view: V, name: String, value: Any?) } @Suppress("FINITE_BOUNDS_VIOLATION_IN_JAVA") public interface ShadowNodeSetter<@Suppress("DEPRECATION") in T : ReactShadowNode<*>> : Settable { public fun setProperty(node: T, name: String, value: Any?) } private const val TAG = "ViewManagerPropertyUpdater" private val VIEW_MANAGER_SETTER_MAP: MutableMap, ViewManagerSetter<*, *>> = HashMap() private val SHADOW_NODE_SETTER_MAP: MutableMap, ShadowNodeSetter<*>> = HashMap() @JvmStatic public fun clear() { ViewManagersPropertyCache.clear() VIEW_MANAGER_SETTER_MAP.clear() SHADOW_NODE_SETTER_MAP.clear() } @JvmStatic @Deprecated("Use ViewManager#updateProperties to update a view's properties") public fun , V : View> updateProps( delegate: T, view: V, props: ReactStylesDiffMap, ) { val iterator = props.backingMap.entryIterator while (iterator.hasNext()) { val entry = iterator.next() delegate.setProperty(view, entry.key, entry.value) } } @JvmStatic @Deprecated("Use ViewManager#updateProperties to update a view's properties") public fun updateProps( manager: ViewManager, view: V, props: ReactStylesDiffMap, ) { val setter = findManagerSetter(manager.javaClass) val iterator = props.backingMap.entryIterator while (iterator.hasNext()) { val entry = iterator.next() setter.setProperty(manager, view, entry.key, entry.value) } } @JvmStatic @Deprecated("Use ViewManager#updateProperties to update a view's properties") public fun <@Suppress("DEPRECATION") T : ReactShadowNode> updateProps( node: T, props: ReactStylesDiffMap, ) { val setter = findNodeSetter(node.javaClass) val iterator = props.backingMap.entryIterator while (iterator.hasNext()) { val entry = iterator.next() setter.setProperty(node, entry.key, entry.value) } } @JvmStatic public fun getNativeProps( viewManagerTopClass: Class>, shadowNodeTopClass: Class?, ): Map { val props: MutableMap = HashMap() findManagerSetter(viewManagerTopClass).getProperties(props) if (shadowNodeTopClass != null) { findNodeSetter(shadowNodeTopClass).getProperties(props) } return props } private fun findManagerSetter( managerClass: Class> ): ViewManagerSetter, V> { var setter = VIEW_MANAGER_SETTER_MAP[managerClass] if (setter == null) { setter = findGeneratedSetter(managerClass) if (setter == null) { setter = FallbackViewManagerSetter(managerClass) } VIEW_MANAGER_SETTER_MAP[managerClass] = setter } @Suppress("UNCHECKED_CAST") return setter as ViewManagerSetter, V> } private fun <@Suppress("DEPRECATION") T : ReactShadowNode> findNodeSetter( nodeClass: Class ): ShadowNodeSetter { var setter = SHADOW_NODE_SETTER_MAP[nodeClass] if (setter == null) { setter = findGeneratedSetter(nodeClass) if (setter == null) { @Suppress("UNCHECKED_CAST") setter = FallbackShadowNodeSetter(nodeClass as Class) } SHADOW_NODE_SETTER_MAP[nodeClass] = setter } @Suppress("UNCHECKED_CAST") return setter as ShadowNodeSetter } private fun findGeneratedSetter(cls: Class<*>): T? { val clsName = cls.name try { val setterClass = Class.forName("$clsName$\$PropsSetter") @Suppress("DEPRECATION", "UNCHECKED_CAST") return setterClass.newInstance() as T } catch (e: ClassNotFoundException) { FLog.w(TAG, "Could not find generated setter for $cls") return null } catch (e: InstantiationException) { throw RuntimeException("Unable to instantiate methods getter for $clsName", e) } catch (e: IllegalAccessException) { throw RuntimeException("Unable to instantiate methods getter for $clsName", e) } } private class FallbackViewManagerSetter( viewManagerClass: Class> ) : ViewManagerSetter, V> { private val propSetters: Map = ViewManagersPropertyCache.getNativePropSettersForViewManagerClass(viewManagerClass) override fun setProperty(manager: ViewManager, view: V, name: String, value: Any?) { val setter = propSetters[name] setter?.updateViewProp(manager, view, value) } override fun getProperties(props: MutableMap) { for (setter in propSetters.values) { props[setter.propName] = setter.propType } } } private class FallbackShadowNodeSetter(shadowNodeClass: Class) : ShadowNodeSetter> { private val propSetters: Map = ViewManagersPropertyCache.getNativePropSettersForShadowNodeClass(shadowNodeClass) override fun setProperty(node: ReactShadowNode<*>, name: String, value: Any?) { val setter = propSetters[name] setter?.updateShadowNodeProp(node, value) } override fun getProperties(props: MutableMap) { for (setter in propSetters.values) { props[setter.propName] = setter.propType } } } internal class GenericViewManagerDelegate(private val manager: ViewManager) : ViewManagerDelegate { private val setter = findManagerSetter(manager.javaClass) @Suppress("ACCIDENTAL_OVERRIDE") override fun setProperty(view: T, propName: String, value: Any?) { setter.setProperty(manager, view, propName, value) } @Suppress("ACCIDENTAL_OVERRIDE") override fun receiveCommand(view: T, commandName: String, args: ReadableArray) = Unit } }