mirror of
https://github.com/divkit/divkit.git
synced 2026-06-06 20:07:59 +00:00
Make resolver the only source of truth
commit_hash:d1e09b695ddeab7dd443a0002811c5252b7e24fd
This commit is contained in:
+11
-9
@@ -2,12 +2,15 @@ package com.yandex.div.core.actions
|
||||
|
||||
import com.yandex.div.core.DivActionHandler
|
||||
import com.yandex.div.core.DivRequestExecutor
|
||||
import com.yandex.div.core.expression.local.variableController
|
||||
import com.yandex.div.core.expression.name
|
||||
import com.yandex.div.core.state.DivStatePath
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.evaluable.MissingVariableException
|
||||
import com.yandex.div.internal.core.DivItemBuilderResult
|
||||
import com.yandex.div.internal.core.DivTreeVisitor
|
||||
import com.yandex.div.internal.core.toItemBuilderResult
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivAction
|
||||
@@ -30,7 +33,7 @@ class DivActionTypedSubmitHandler @Inject constructor(
|
||||
val submitAction = (action as? DivActionTyped.Submit)?.value ?: return false
|
||||
|
||||
val containerId = submitAction.containerId.evaluate(resolver)
|
||||
val (containerDiv, context, path) = ContainerFinder(containerId).findContainer(view) ?: return false
|
||||
val container = ContainerFinder(containerId).findContainer(view) ?: return false
|
||||
|
||||
val headers = submitAction.request.headers?.map {
|
||||
DivRequestExecutor.Header(it.name.evaluate(resolver), it.value.evaluate(resolver))
|
||||
@@ -40,7 +43,7 @@ class DivActionTypedSubmitHandler @Inject constructor(
|
||||
submitAction.request.url.evaluate(resolver),
|
||||
submitAction.request.method.evaluate(resolver).toString(),
|
||||
headers,
|
||||
createBody(containerDiv, context, path, view),
|
||||
createBody(container, view),
|
||||
)
|
||||
|
||||
val callback = createCallback(submitAction.onSuccessActions, submitAction.onFailActions, view, resolver)
|
||||
@@ -49,12 +52,11 @@ class DivActionTypedSubmitHandler @Inject constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun createBody(div: Div, context: BindingContext, path: DivStatePath, view: Div2View): String {
|
||||
val variables = div.value().variables
|
||||
private fun createBody(container: DivItemBuilderResult, view: Div2View): String {
|
||||
val variables = container.div.value().variables
|
||||
if (variables.isNullOrEmpty()) return ""
|
||||
|
||||
val variableController = context.runtimeStore
|
||||
?.getOrCreateRuntime(path.fullPath, div, context.expressionResolver)?.variableController ?: return ""
|
||||
val variableController = container.expressionResolver.variableController ?: return ""
|
||||
|
||||
val body = JSONObject()
|
||||
variables.forEach {
|
||||
@@ -93,9 +95,9 @@ class DivActionTypedSubmitHandler @Inject constructor(
|
||||
|
||||
private class ContainerFinder(private val id: String) : DivTreeVisitor<Unit>() {
|
||||
|
||||
private val containers = mutableListOf<Triple<Div, BindingContext, DivStatePath>>()
|
||||
private val containers = mutableListOf<DivItemBuilderResult>()
|
||||
|
||||
fun findContainer(view: Div2View): Triple<Div, BindingContext, DivStatePath>? {
|
||||
fun findContainer(view: Div2View): DivItemBuilderResult? {
|
||||
val data = view.divData ?: return null
|
||||
data.states.forEach { state ->
|
||||
visit(state.div, view.bindingContext, DivStatePath.fromState(state))
|
||||
@@ -120,7 +122,7 @@ class DivActionTypedSubmitHandler @Inject constructor(
|
||||
|
||||
override fun defaultVisit(data: Div, context: BindingContext, path: DivStatePath) {
|
||||
if (data.value().id == id) {
|
||||
containers.add(Triple(data, context, path))
|
||||
containers.add(data.toItemBuilderResult(context.expressionResolver))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-10
@@ -10,6 +10,7 @@ import com.yandex.div.core.actions.colorIntValue
|
||||
import com.yandex.div.core.actions.doubleValue
|
||||
import com.yandex.div.core.actions.logError
|
||||
import com.yandex.div.core.actions.longValue
|
||||
import com.yandex.div.core.expression.local.variableController
|
||||
import com.yandex.div.core.util.androidInterpolator
|
||||
import com.yandex.div.core.util.isAlternated
|
||||
import com.yandex.div.core.util.isReversed
|
||||
@@ -50,7 +51,7 @@ internal object DivVariableAnimatorBuilder {
|
||||
startAction: DivActionAnimatorStart,
|
||||
resolver: ExpressionResolver
|
||||
): Animator? {
|
||||
return when (val variable = findVariable<Variable>(divView, animator.variableName, resolver)) {
|
||||
return when (val variable = findVariable<Variable>(animator.variableName, resolver)) {
|
||||
is Variable.IntegerVariable -> buildIntegerAnimator(divView, animator, startAction, resolver, variable)
|
||||
is Variable.DoubleVariable -> buildDoubleAnimator(divView, animator, startAction, resolver, variable)
|
||||
|
||||
@@ -103,7 +104,7 @@ internal object DivVariableAnimatorBuilder {
|
||||
startAction: DivActionAnimatorStart,
|
||||
resolver: ExpressionResolver
|
||||
): Animator? {
|
||||
val variable = findVariable<Variable.ColorVariable>(divView, animator.variableName, resolver)
|
||||
val variable = findVariable<Variable.ColorVariable>(animator.variableName, resolver)
|
||||
if (variable == null) {
|
||||
divView.logError(MissingVariableException("Unable to find color variable with name '${animator.variableName}'"))
|
||||
return null
|
||||
@@ -152,12 +153,6 @@ internal object DivVariableAnimatorBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
private inline fun <reified T : Variable> findVariable(
|
||||
divView: Div2View,
|
||||
name: String,
|
||||
resolver: ExpressionResolver,
|
||||
): T? {
|
||||
val runtime = divView.runtimeStore?.getRuntimeWithOrNull(resolver) ?: divView.expressionsRuntime
|
||||
return runtime?.variableController?.getMutableVariable(name) as? T
|
||||
}
|
||||
private inline fun <reified T : Variable> findVariable(name: String, resolver: ExpressionResolver) =
|
||||
resolver.variableController?.getMutableVariable(name) as? T
|
||||
}
|
||||
|
||||
+6
-14
@@ -30,10 +30,10 @@ internal class ExpressionResolverImpl(
|
||||
/**
|
||||
* Unique path by which resolver could be found at [RuntimeStore].
|
||||
*/
|
||||
internal val path: String,
|
||||
internal val runtimeStore: RuntimeStore,
|
||||
private val variableController: VariableController,
|
||||
private val evaluator: Evaluator,
|
||||
val path: String,
|
||||
val runtimeStore: RuntimeStore,
|
||||
val variableController: VariableController,
|
||||
val evaluator: Evaluator,
|
||||
private val errorCollector: ErrorCollector,
|
||||
private val onCreateCallback: OnCreateCallback,
|
||||
) : ExpressionResolver {
|
||||
@@ -46,11 +46,7 @@ internal class ExpressionResolverImpl(
|
||||
var suppressMissingVariableException: Boolean = false
|
||||
|
||||
init {
|
||||
onCreateCallback.onCreate(
|
||||
this,
|
||||
variableController,
|
||||
evaluator.evaluationContext.functionProvider as FunctionProviderDecorator
|
||||
)
|
||||
onCreateCallback.onCreate(this)
|
||||
}
|
||||
|
||||
override fun <R, T : Any> get(
|
||||
@@ -289,10 +285,6 @@ internal class ExpressionResolverImpl(
|
||||
* as a new ExpressionRuntime we are using OnCreateCallback.
|
||||
*/
|
||||
internal fun interface OnCreateCallback {
|
||||
fun onCreate(
|
||||
resolver: ExpressionResolverImpl,
|
||||
variableController: VariableController,
|
||||
functionProvider: FunctionProviderDecorator
|
||||
)
|
||||
fun onCreate(resolver: ExpressionResolverImpl)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,19 +1,17 @@
|
||||
package com.yandex.div.core.expression
|
||||
|
||||
import com.yandex.div.core.DivViewFacade
|
||||
import com.yandex.div.core.expression.local.RuntimeStore
|
||||
import com.yandex.div.core.expression.triggers.TriggersController
|
||||
import com.yandex.div.core.expression.variables.VariableController
|
||||
|
||||
internal class ExpressionsRuntime(
|
||||
val expressionResolver: ExpressionResolverImpl,
|
||||
val variableController: VariableController,
|
||||
val triggersController: TriggersController? = null,
|
||||
val functionProvider: FunctionProviderDecorator,
|
||||
val runtimeStore: RuntimeStore,
|
||||
) {
|
||||
private var unsubscribed = true
|
||||
|
||||
val variableController = expressionResolver.variableController
|
||||
val runtimeStore = expressionResolver.runtimeStore
|
||||
|
||||
fun clearBinding(view: DivViewFacade) {
|
||||
triggersController?.clearBinding(view)
|
||||
}
|
||||
|
||||
+8
-14
@@ -8,7 +8,6 @@ import com.yandex.div.core.expression.local.RuntimeStore
|
||||
import com.yandex.div.core.expression.storedvalues.StoredValuesController
|
||||
import com.yandex.div.core.expression.triggers.TriggersController
|
||||
import com.yandex.div.core.expression.variables.DivVariableController
|
||||
import com.yandex.div.core.expression.variables.VariableController
|
||||
import com.yandex.div.core.expression.variables.VariableControllerImpl
|
||||
import com.yandex.div.core.expression.variables.toVariable
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
@@ -20,7 +19,6 @@ import com.yandex.div.data.VariableDeclarationException
|
||||
import com.yandex.div.evaluable.EvaluationContext
|
||||
import com.yandex.div.evaluable.Evaluator
|
||||
import com.yandex.div.evaluable.function.GeneratedBuiltinFunctionProvider
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div2.DivData
|
||||
import com.yandex.div2.DivVariable
|
||||
import java.util.Collections
|
||||
@@ -46,7 +44,7 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
|
||||
val result = runtimes.getOrPut(tag.id) { createRuntimeFor(data, tag) }
|
||||
val errorCollector = errorCollectors.getOrCreate(tag, data)
|
||||
divDataTags.getOrPut(div2View, ::mutableSetOf).add(tag.id)
|
||||
ensureVariablesSynced(result.variableController, result.expressionResolver, data, errorCollector)
|
||||
ensureVariablesSynced(result.expressionResolver, data, errorCollector)
|
||||
result.triggersController?.ensureTriggersSynced(data.variableTriggers ?: emptyList())
|
||||
return result
|
||||
}
|
||||
@@ -69,11 +67,11 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
|
||||
}
|
||||
|
||||
private fun ensureVariablesSynced(
|
||||
v: VariableController,
|
||||
resolver: ExpressionResolver,
|
||||
resolver: ExpressionResolverImpl,
|
||||
data: DivData,
|
||||
errorCollector: ErrorCollector
|
||||
) {
|
||||
val v = resolver.variableController
|
||||
data.variables?.forEach {
|
||||
val existingVariable = v.getMutableVariable(it.name) ?: run {
|
||||
try {
|
||||
@@ -133,9 +131,9 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
|
||||
val evaluator = Evaluator(evaluationContext)
|
||||
|
||||
val runtimeStore = RuntimeStore(evaluator, errorCollector, logger, divActionBinder)
|
||||
val callback = ExpressionResolverImpl.OnCreateCallback { resolver, variableController, functionProvider ->
|
||||
val callback = ExpressionResolverImpl.OnCreateCallback { resolver ->
|
||||
runtimeStore.putRuntime(
|
||||
runtime = ExpressionsRuntime(resolver, variableController, null, functionProvider, runtimeStore)
|
||||
runtime = ExpressionsRuntime(resolver, null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -165,13 +163,9 @@ internal class ExpressionsRuntimeProvider @Inject constructor(
|
||||
divActionBinder
|
||||
)
|
||||
|
||||
return ExpressionsRuntime(
|
||||
expressionResolver,
|
||||
variableController,
|
||||
triggersController,
|
||||
functionProvider,
|
||||
runtimeStore,
|
||||
).also { runtimeStore.rootRuntime = it }
|
||||
return ExpressionsRuntime(expressionResolver, triggersController).also {
|
||||
runtimeStore.rootRuntime = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -2,6 +2,7 @@ package com.yandex.div.core.expression.local
|
||||
|
||||
import com.yandex.div.core.annotations.Mockable
|
||||
import com.yandex.div.core.dagger.DivScope
|
||||
import com.yandex.div.core.expression.ExpressionResolverImpl
|
||||
import com.yandex.div.core.expression.ExpressionsRuntime
|
||||
import com.yandex.div.core.state.DivPathUtils.getId
|
||||
import com.yandex.div.core.state.DivStatePath
|
||||
@@ -160,7 +161,7 @@ internal class DivRuntimeVisitor @Inject constructor(
|
||||
div: DivState,
|
||||
divView: Div2View,
|
||||
states: MutableList<String>,
|
||||
parentRuntime: ExpressionsRuntime,
|
||||
resolver: ExpressionResolverImpl,
|
||||
): String? {
|
||||
val statePath = states.joinToString("/")
|
||||
val cardId = divView.divTag.id
|
||||
@@ -168,9 +169,9 @@ internal class DivRuntimeVisitor @Inject constructor(
|
||||
return temporaryStateCache.getState(cardId, statePath)
|
||||
?: divStateCache.getState(cardId, statePath)
|
||||
?: div.stateIdVariable?.let {
|
||||
parentRuntime.variableController.getMutableVariable(it)?.getValue().toString()
|
||||
resolver.variableController.getMutableVariable(it)?.getValue().toString()
|
||||
}
|
||||
?: parentRuntime.let { div.defaultStateId?.evaluate(it.expressionResolver) }
|
||||
?: div.defaultStateId?.evaluate(resolver)
|
||||
?: div.states.firstOrNull()?.stateId
|
||||
}
|
||||
|
||||
@@ -182,7 +183,7 @@ internal class DivRuntimeVisitor @Inject constructor(
|
||||
parentRuntime: ExpressionsRuntime,
|
||||
) {
|
||||
states.add(div.getId())
|
||||
val activeStateId = getActiveStateId(div, divView, states, parentRuntime)
|
||||
val activeStateId = getActiveStateId(div, divView, states, parentRuntime.expressionResolver)
|
||||
div.states.forEach {
|
||||
val childDiv = it.div ?: return@forEach
|
||||
val childPath = path.appendChild(it.stateId)
|
||||
|
||||
+6
-4
@@ -5,6 +5,7 @@ import com.yandex.div.core.DivViewFacade
|
||||
import com.yandex.div.core.ObserverList
|
||||
import com.yandex.div.core.expression.ExpressionResolverImpl
|
||||
import com.yandex.div.core.expression.ExpressionsRuntime
|
||||
import com.yandex.div.core.expression.FunctionProviderDecorator
|
||||
import com.yandex.div.core.expression.triggers.TriggersController
|
||||
import com.yandex.div.core.expression.variables.VariableController
|
||||
import com.yandex.div.core.expression.variables.VariableControllerImpl
|
||||
@@ -46,8 +47,8 @@ internal class RuntimeStore(
|
||||
}
|
||||
|
||||
private val onCreateCallback by lazy {
|
||||
ExpressionResolverImpl.OnCreateCallback { resolver, variableController, functionProvider ->
|
||||
ExpressionsRuntime(resolver, variableController, null, functionProvider, this).also {
|
||||
ExpressionResolverImpl.OnCreateCallback { resolver ->
|
||||
ExpressionsRuntime(resolver, null).also {
|
||||
/**
|
||||
* we cannot provide path here, otherwise descendants of ExpressionResolver will
|
||||
* receive the same callback and override runtime for provided path.
|
||||
@@ -155,7 +156,8 @@ internal class RuntimeStore(
|
||||
val localVariableController = VariableControllerImpl(baseRuntime.variableController)
|
||||
|
||||
val functions = div.functions
|
||||
var functionProvider = baseRuntime.functionProvider
|
||||
var functionProvider =
|
||||
baseRuntime.expressionResolver.evaluator.evaluationContext.functionProvider as FunctionProviderDecorator
|
||||
if (!functions.isNullOrEmpty()) {
|
||||
functionProvider += functions.toLocalFunctions()
|
||||
}
|
||||
@@ -183,7 +185,7 @@ internal class RuntimeStore(
|
||||
|
||||
val triggerController = div.variableTriggers.toTriggersController(localVariableController, resolver, evaluator)
|
||||
|
||||
return ExpressionsRuntime(resolver, localVariableController, triggerController, functionProvider, this).also {
|
||||
return ExpressionsRuntime(resolver, triggerController).also {
|
||||
putRuntime(it, path, parentRuntime)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package com.yandex.div.core.expression.local
|
||||
|
||||
import com.yandex.div.core.expression.ExpressionResolverImpl
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div2.Div
|
||||
|
||||
internal val Div.needLocalRuntime get() =
|
||||
!value().run { variables.isNullOrEmpty() && variableTriggers.isNullOrEmpty() && functions.isNullOrEmpty() }
|
||||
|
||||
internal val ExpressionResolver.asImpl get() = this as? ExpressionResolverImpl
|
||||
|
||||
internal val ExpressionResolver.variableController get() = asImpl?.variableController
|
||||
|
||||
+6
-14
@@ -4,7 +4,7 @@ import androidx.annotation.MainThread
|
||||
import com.yandex.div.core.Disposable
|
||||
import com.yandex.div.core.annotations.Mockable
|
||||
import com.yandex.div.core.dagger.DivScope
|
||||
import com.yandex.div.core.expression.ExpressionsRuntimeProvider
|
||||
import com.yandex.div.core.expression.local.variableController
|
||||
import com.yandex.div.core.state.DivStatePath
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.errors.ErrorCollectors
|
||||
@@ -16,8 +16,7 @@ import javax.inject.Inject
|
||||
@Mockable
|
||||
internal class TwoWayStringVariableBinder @Inject constructor(
|
||||
errorCollectors: ErrorCollectors,
|
||||
expressionsRuntimeProvider: ExpressionsRuntimeProvider
|
||||
) : TwoWayVariableBinder<String>(errorCollectors, expressionsRuntimeProvider) {
|
||||
) : TwoWayVariableBinder<String>(errorCollectors) {
|
||||
|
||||
interface Callbacks : TwoWayVariableBinder.Callbacks<String>
|
||||
|
||||
@@ -28,8 +27,7 @@ internal class TwoWayStringVariableBinder @Inject constructor(
|
||||
@Mockable
|
||||
internal class TwoWayIntegerVariableBinder @Inject constructor(
|
||||
errorCollectors: ErrorCollectors,
|
||||
expressionsRuntimeProvider: ExpressionsRuntimeProvider
|
||||
) : TwoWayVariableBinder<Long>(errorCollectors, expressionsRuntimeProvider) {
|
||||
) : TwoWayVariableBinder<Long>(errorCollectors) {
|
||||
|
||||
interface Callbacks : TwoWayVariableBinder.Callbacks<Long>
|
||||
|
||||
@@ -40,8 +38,7 @@ internal class TwoWayIntegerVariableBinder @Inject constructor(
|
||||
@Mockable
|
||||
internal class TwoWayBooleanVariableBinder @Inject constructor(
|
||||
errorCollectors: ErrorCollectors,
|
||||
expressionsRuntimeProvider: ExpressionsRuntimeProvider
|
||||
) : TwoWayVariableBinder<Boolean>(errorCollectors, expressionsRuntimeProvider) {
|
||||
) : TwoWayVariableBinder<Boolean>(errorCollectors) {
|
||||
|
||||
interface Callbacks : TwoWayVariableBinder.Callbacks<Boolean>
|
||||
|
||||
@@ -49,10 +46,7 @@ internal class TwoWayBooleanVariableBinder @Inject constructor(
|
||||
}
|
||||
|
||||
@Mockable
|
||||
internal abstract class TwoWayVariableBinder<T>(
|
||||
private val errorCollectors: ErrorCollectors,
|
||||
private val expressionsRuntimeProvider: ExpressionsRuntimeProvider
|
||||
) {
|
||||
internal abstract class TwoWayVariableBinder<T>(private val errorCollectors: ErrorCollectors) {
|
||||
|
||||
@Mockable
|
||||
interface Callbacks<T> {
|
||||
@@ -72,9 +66,7 @@ internal abstract class TwoWayVariableBinder<T>(
|
||||
|
||||
var pendingValue: T? = null
|
||||
val tag = divView.dataTag
|
||||
val variableController = bindingContext.runtimeStore?.let { runtimeStore ->
|
||||
runtimeStore.getRuntimeWithOrNull(bindingContext.expressionResolver)?.variableController
|
||||
} ?: expressionsRuntimeProvider.getOrCreate(tag, data, divView).variableController
|
||||
val variableController = bindingContext.expressionResolver.variableController ?: return Disposable.NULL
|
||||
|
||||
callbacks.setViewStateChangeListener { value ->
|
||||
if (pendingValue == value) return@setViewStateChangeListener
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package com.yandex.div.core.view2
|
||||
|
||||
import com.yandex.div.core.expression.local.RuntimeStore
|
||||
import com.yandex.div.core.expression.local.asImpl
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
|
||||
internal class BindingContext private constructor(
|
||||
val divView: Div2View,
|
||||
val expressionResolver: ExpressionResolver,
|
||||
val runtimeStore: RuntimeStore?,
|
||||
) {
|
||||
fun getFor(resolver: ExpressionResolver) =
|
||||
if (expressionResolver == resolver) this else BindingContext(divView, resolver, runtimeStore)
|
||||
|
||||
fun getFor(resolver: ExpressionResolver, runtimeStore: RuntimeStore?) =
|
||||
if (expressionResolver == resolver) this else BindingContext(divView, resolver, runtimeStore)
|
||||
val runtimeStore = expressionResolver.asImpl?.runtimeStore
|
||||
|
||||
fun getFor(resolver: ExpressionResolver) =
|
||||
if (expressionResolver == resolver) this else BindingContext(divView, resolver)
|
||||
|
||||
companion object {
|
||||
fun createEmpty(divView: Div2View) = BindingContext(divView, ExpressionResolver.EMPTY, null)
|
||||
fun createEmpty(divView: Div2View) = BindingContext(divView, ExpressionResolver.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ class Div2View private constructor(
|
||||
runtimeStore?.clearBindings(this)
|
||||
}
|
||||
runtimeStore = expressionsRuntime?.runtimeStore
|
||||
bindingContext = bindingContext.getFor(expressionResolver, runtimeStore)
|
||||
bindingContext = bindingContext.getFor(expressionResolver)
|
||||
}
|
||||
|
||||
private fun attachVariableTriggers(data: DivData) {
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.yandex.div.core.downloader.DivPatchApply
|
||||
import com.yandex.div.core.downloader.DivPatchCache
|
||||
import com.yandex.div.core.expression.ExpressionResolverImpl
|
||||
import com.yandex.div.core.expression.local.asImpl
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.internal.core.DivItemBuilderResult
|
||||
import com.yandex.div.internal.core.toDivItemBuilderResult
|
||||
@@ -131,7 +131,7 @@ internal abstract class DivCollectionAdapter<VH: DivCollectionViewHolder>(
|
||||
}
|
||||
|
||||
private fun DivItemBuilderResult.suppressMissingVariableException(suppress: Boolean) {
|
||||
(expressionResolver as? ExpressionResolverImpl)?.suppressMissingVariableException = suppress
|
||||
expressionResolver.asImpl?.suppressMissingVariableException = suppress
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.ViewGroup
|
||||
import android.view.animation.AnimationSet
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.isNotEmpty
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionSet
|
||||
@@ -15,6 +16,7 @@ import com.yandex.div.core.dagger.DivScope
|
||||
import com.yandex.div.core.downloader.DivPatchCache
|
||||
import com.yandex.div.core.downloader.DivPatchManager
|
||||
import com.yandex.div.core.expression.local.DivRuntimeVisitor
|
||||
import com.yandex.div.core.expression.local.variableController
|
||||
import com.yandex.div.core.expression.variables.TwoWayStringVariableBinder
|
||||
import com.yandex.div.core.state.DivPathUtils.getId
|
||||
import com.yandex.div.core.state.DivStatePath
|
||||
@@ -152,7 +154,7 @@ internal class DivStateBinder @Inject constructor(
|
||||
val newStateDiv = newState.div
|
||||
val newStateDivValue = newStateDiv?.value()
|
||||
|
||||
val outgoing = if (childCount > 0) getChildAt(0) else null
|
||||
val outgoing = if (isNotEmpty()) getChildAt(0) else null
|
||||
val incoming: View?
|
||||
val reusableIncomingView = newStateDiv?.let {
|
||||
divView.currentRebindReusableList?.getUniqueViewForDiv(newStateDiv)
|
||||
@@ -271,9 +273,7 @@ internal class DivStateBinder @Inject constructor(
|
||||
}
|
||||
|
||||
private fun getValueFromVariable(context: BindingContext, variableName: String): String? {
|
||||
val variableController = context.runtimeStore?.getRuntimeWithOrNull(context.expressionResolver)?.variableController
|
||||
?: context.divView.expressionsRuntime?.variableController ?: return null
|
||||
|
||||
val variableController = context.expressionResolver.variableController ?: return null
|
||||
return variableController.getMutableVariable(variableName)?.getValue()?.toString()
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package com.yandex.div.internal.core
|
||||
|
||||
import com.yandex.div.core.DivViewFacade
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
import com.yandex.div.core.expression.ExpressionResolverImpl
|
||||
import com.yandex.div.core.expression.local.asImpl
|
||||
import com.yandex.div.core.expression.variables.ConstantsProvider
|
||||
import com.yandex.div.internal.Assert
|
||||
import com.yandex.div.internal.util.forEach
|
||||
@@ -85,7 +85,7 @@ private fun DivCollectionItemBuilder.getItemResolver(
|
||||
index: Int,
|
||||
resolver: ExpressionResolver,
|
||||
): ExpressionResolver? {
|
||||
val resolverImpl = resolver as? ExpressionResolverImpl ?: return resolver
|
||||
val resolverImpl = resolver.asImpl ?: return resolver
|
||||
val validElement = resolverImpl.validateItemBuilderDataElement(dataElement, index) ?: return null
|
||||
val localDataProvider = ConstantsProvider(mapOf(
|
||||
dataElementName to validElement,
|
||||
|
||||
+5
-11
@@ -2,6 +2,7 @@ package com.yandex.div.internal.core
|
||||
|
||||
import com.yandex.div.core.actions.logError
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
import com.yandex.div.core.expression.local.variableController
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.data.Variable
|
||||
import com.yandex.div.data.VariableMutationException
|
||||
@@ -21,7 +22,7 @@ class VariableMutationHandler {
|
||||
value: String,
|
||||
resolver: ExpressionResolver
|
||||
): VariableMutationException? {
|
||||
val mutableVariable = findVariable(div2View, name, resolver)
|
||||
val mutableVariable = findVariable(name, resolver)
|
||||
?: return createAndReportError(null, div2View, "Variable '$name' not defined!")
|
||||
|
||||
runCatching {
|
||||
@@ -45,7 +46,7 @@ class VariableMutationHandler {
|
||||
resolver: ExpressionResolver,
|
||||
valueMutation: (T) -> T,
|
||||
): VariableMutationException? {
|
||||
val mutableVariable = findVariable(div2View, name, resolver)
|
||||
val mutableVariable = findVariable(name, resolver)
|
||||
?: return createAndReportError(null, div2View, "Variable '$name' not defined!")
|
||||
|
||||
runCatching {
|
||||
@@ -58,15 +59,8 @@ class VariableMutationHandler {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findVariable(
|
||||
div2View: Div2View,
|
||||
name: String,
|
||||
resolver: ExpressionResolver,
|
||||
): Variable? {
|
||||
val runtime = div2View.runtimeStore?.getRuntimeWithOrNull(resolver) ?: div2View.expressionsRuntime
|
||||
|
||||
return runtime?.variableController?.getMutableVariable(name)
|
||||
}
|
||||
private fun findVariable(name: String, resolver: ExpressionResolver) =
|
||||
resolver.variableController?.getMutableVariable(name)
|
||||
|
||||
private fun createAndReportError(
|
||||
e: Throwable?,
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ class ExpressionResolverImplTest {
|
||||
externalVariables,
|
||||
Evaluator(evaluationContext),
|
||||
mock()
|
||||
) { _, _, _ -> }
|
||||
) { _ -> }
|
||||
|
||||
private val withFuncGetCallback = { callback: () -> Unit ->
|
||||
ExpressionResolverImpl(
|
||||
@@ -112,7 +112,7 @@ class ExpressionResolverImplTest {
|
||||
)
|
||||
),
|
||||
mock()
|
||||
) { _, _, _ -> }
|
||||
) { _ -> }
|
||||
}
|
||||
|
||||
@Before
|
||||
|
||||
+8
-12
@@ -47,24 +47,20 @@ class RuntimeStoreTest {
|
||||
private val underTest = RuntimeStore(evaluator, errorCollector, div2Logger, divActionBinder)
|
||||
|
||||
private var runtimeFromCallback: ExpressionsRuntime? = null
|
||||
private val callback = ExpressionResolverImpl.OnCreateCallback { resolver, variableController, functionProvider ->
|
||||
runtimeFromCallback = ExpressionsRuntime(
|
||||
resolver, variableController, null, functionProvider, underTest
|
||||
)
|
||||
private val callback = ExpressionResolverImpl.OnCreateCallback { resolver ->
|
||||
runtimeFromCallback = ExpressionsRuntime(resolver, null)
|
||||
underTest.putRuntime(runtimeFromCallback!!)
|
||||
}
|
||||
|
||||
private val resolver = ExpressionResolverImpl("", mock(), mock(), evaluator, errorCollector, callback)
|
||||
private val rootVariableController: VariableController = mock<VariableController>()
|
||||
private val functionProvider = mock<FunctionProviderDecorator>()
|
||||
private val divBase = mock<DivBase>()
|
||||
private val divView = mock<DivViewFacade>()
|
||||
private val div = mock<Div> {
|
||||
on { value() } doReturn divBase
|
||||
}
|
||||
private val rootResolver = ExpressionResolverImpl("", mock(), rootVariableController, evaluator, errorCollector, callback)
|
||||
private val rootRuntime: ExpressionsRuntime =
|
||||
ExpressionsRuntime(rootResolver, rootVariableController, null, functionProvider, underTest)
|
||||
private val rootRuntime: ExpressionsRuntime = ExpressionsRuntime(rootResolver, null)
|
||||
|
||||
@Before
|
||||
fun putRootResolver() {
|
||||
@@ -104,7 +100,7 @@ class RuntimeStoreTest {
|
||||
@Test
|
||||
fun `getOrCreateRuntime returns runtime for path if exist`() {
|
||||
val resolver = ExpressionResolverImpl("", mock(), mock(), evaluator, errorCollector, callback)
|
||||
val runtime = ExpressionsRuntime(resolver, mock(), null, functionProvider, underTest)
|
||||
val runtime = ExpressionsRuntime(resolver, null)
|
||||
underTest.putRuntime(runtime, PATH, rootRuntime)
|
||||
|
||||
Assert.assertEquals(runtime, underTest.getOrCreateRuntime(path.fullPath, div, resolver))
|
||||
@@ -115,8 +111,7 @@ class RuntimeStoreTest {
|
||||
@Test
|
||||
fun `getOrCreateRuntime returns parent runtime for path if no variables provided`() {
|
||||
val resolver = ExpressionResolverImpl("", mock(), mock(), evaluator, errorCollector, callback)
|
||||
val parentVariableController = mock<VariableController>()
|
||||
val runtime = ExpressionsRuntime(resolver, parentVariableController, null, functionProvider, underTest)
|
||||
val runtime = ExpressionsRuntime(resolver, null)
|
||||
underTest.putRuntime(runtime, PARENT_PATH, rootRuntime)
|
||||
|
||||
Assert.assertEquals(runtime, underTest.getOrCreateRuntime(path.fullPath, div, parentResolver = resolver))
|
||||
@@ -125,11 +120,12 @@ class RuntimeStoreTest {
|
||||
|
||||
@Test
|
||||
fun `getOrCreateRuntime returns wrapped parent runtime with new variables if new variables provided`() {
|
||||
val resolver = ExpressionResolverImpl("", mock(), mock(), evaluator, errorCollector, callback)
|
||||
val parentVariableController = mock<VariableController> {
|
||||
on { getMutableVariable(PARENT_VARIABLE) } doReturn Variable.StringVariable(PARENT_VARIABLE, "123")
|
||||
}
|
||||
val runtime = ExpressionsRuntime(resolver, parentVariableController, null, functionProvider, underTest)
|
||||
val resolver =
|
||||
ExpressionResolverImpl("", mock(), parentVariableController, evaluator, errorCollector, callback)
|
||||
val runtime = ExpressionsRuntime(resolver, null)
|
||||
setVariable()
|
||||
|
||||
underTest.putRuntime(runtime, PARENT_PATH, rootRuntime)
|
||||
|
||||
+33
-35
@@ -1,23 +1,16 @@
|
||||
package com.yandex.div.core.expression.variables
|
||||
|
||||
import com.yandex.div.core.Disposable
|
||||
import com.yandex.div.core.expression.ExpressionResolverImpl
|
||||
import com.yandex.div.core.expression.ExpressionsRuntime
|
||||
import com.yandex.div.core.expression.ExpressionsRuntimeProvider
|
||||
import com.yandex.div.core.expression.FunctionProviderDecorator
|
||||
import com.yandex.div.core.expression.local.RuntimeStore
|
||||
import com.yandex.div.core.state.DivStatePath
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.errors.ErrorCollector
|
||||
import com.yandex.div.core.view2.errors.ErrorCollectors
|
||||
import com.yandex.div.data.Variable
|
||||
import com.yandex.div.evaluable.EvaluationContext
|
||||
import com.yandex.div.evaluable.Evaluator
|
||||
import com.yandex.div2.DivData
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.anyOrNull
|
||||
import org.mockito.kotlin.argumentCaptor
|
||||
import org.mockito.kotlin.doAnswer
|
||||
import org.mockito.kotlin.doReturn
|
||||
@@ -34,30 +27,32 @@ private const val ANOTHER_VALUE = "another_value"
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class TwoWayVariableBinderTest {
|
||||
|
||||
private val errorCollector = mock<ErrorCollector>()
|
||||
private val errorCollectors = mock<ErrorCollectors> {
|
||||
on { getOrCreate(any(), any()) } doReturn errorCollector
|
||||
}
|
||||
private val variable = Variable.StringVariable(VARIABLE_NAME, INITIAL_VALUE)
|
||||
private val variableController = VariableControllerImpl().apply {
|
||||
declare(variable)
|
||||
private val variableUpdateCaptor = argumentCaptor<(Variable?) -> Unit>()
|
||||
private val invokeOnSubscriptionCaptor = argumentCaptor<Boolean>()
|
||||
private val variableController = mock<VariableController> {
|
||||
on { getMutableVariable(VARIABLE_NAME) } doReturn variable
|
||||
on {
|
||||
subscribeToVariableChange(
|
||||
any(),
|
||||
anyOrNull(),
|
||||
invokeOnSubscriptionCaptor.capture(),
|
||||
variableUpdateCaptor.capture()
|
||||
)
|
||||
} doAnswer {
|
||||
if (invokeOnSubscriptionCaptor.firstValue) {
|
||||
variableUpdateCaptor.firstValue.invoke(variable)
|
||||
}
|
||||
Disposable.NULL
|
||||
}
|
||||
}
|
||||
private val path = DivStatePath(0)
|
||||
private val store = RuntimeStore(mock(), mock(), mock(), mock())
|
||||
private val evaluationContext = EvaluationContext(mock(), mock(), mock<FunctionProviderDecorator>(), mock())
|
||||
private val evaluator = mock<Evaluator> {
|
||||
on { evaluationContext } doReturn evaluationContext
|
||||
}
|
||||
private val expressionResolver = ExpressionResolverImpl("", mock(), mock(), evaluator, mock(), mock())
|
||||
private val expressionsRuntime = ExpressionsRuntime(expressionResolver, variableController, mock(), mock(), store)
|
||||
private val expressionsRuntimeProvider = mock<ExpressionsRuntimeProvider> {
|
||||
on { getOrCreate(any(), any(), any()) } doReturn expressionsRuntime
|
||||
private val expressionResolver = mock<ExpressionResolverImpl> {
|
||||
on { variableController } doReturn variableController
|
||||
}
|
||||
|
||||
private val divView = mock<Div2View> {
|
||||
on { dataTag } doReturn mock()
|
||||
on { divData } doReturn DivData(logId = "test", states = emptyList())
|
||||
on { runtimeStore } doReturn store
|
||||
}
|
||||
private val updateCaptor = argumentCaptor<(String) -> Unit>()
|
||||
private val callbacks = mock<TwoWayStringVariableBinder.Callbacks> {
|
||||
@@ -65,10 +60,8 @@ class TwoWayVariableBinderTest {
|
||||
}
|
||||
|
||||
init {
|
||||
store.rootRuntime = expressionsRuntime
|
||||
val bindingContext = BindingContext.createEmpty(divView).getFor(expressionResolver, store)
|
||||
TwoWayStringVariableBinder(errorCollectors, expressionsRuntimeProvider)
|
||||
.bindVariable(bindingContext, VARIABLE_NAME, callbacks, path)
|
||||
val bindingContext = BindingContext.createEmpty(divView).getFor(expressionResolver)
|
||||
TwoWayStringVariableBinder(mock()).bindVariable(bindingContext, VARIABLE_NAME, callbacks, mock())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -78,7 +71,7 @@ class TwoWayVariableBinderTest {
|
||||
|
||||
@Test
|
||||
fun `invoke callback on variable change`() {
|
||||
variable.set(NEW_VALUE)
|
||||
updateVariable()
|
||||
verify(callbacks).onVariableChanged(NEW_VALUE)
|
||||
}
|
||||
|
||||
@@ -91,13 +84,13 @@ class TwoWayVariableBinderTest {
|
||||
@Test
|
||||
fun `not invoke callback on variable change after view state change with same value`() {
|
||||
updateCaptor.firstValue.invoke(NEW_VALUE)
|
||||
variable.set(NEW_VALUE)
|
||||
updateVariable()
|
||||
verify(callbacks, never()).onVariableChanged(NEW_VALUE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `not set variable value on view state change after variable change with same value`() {
|
||||
variable.set(NEW_VALUE)
|
||||
updateVariable()
|
||||
val variableObserver = mock<(Variable) -> Unit>()
|
||||
variable.addObserver(variableObserver)
|
||||
|
||||
@@ -109,14 +102,19 @@ class TwoWayVariableBinderTest {
|
||||
@Test
|
||||
fun `invoke callback on variable change after view state change with another value`() {
|
||||
updateCaptor.firstValue.invoke(NEW_VALUE)
|
||||
variable.set(ANOTHER_VALUE)
|
||||
updateVariable(ANOTHER_VALUE)
|
||||
verify(callbacks).onVariableChanged(ANOTHER_VALUE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `set variable value on view state change after variable change with another value`() {
|
||||
variable.set(NEW_VALUE)
|
||||
updateVariable()
|
||||
updateCaptor.firstValue.invoke(ANOTHER_VALUE)
|
||||
assertEquals(ANOTHER_VALUE, variable.getValue())
|
||||
}
|
||||
|
||||
private fun updateVariable(value: String = NEW_VALUE) {
|
||||
variable.set(value)
|
||||
variableUpdateCaptor.firstValue.invoke(variable)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user