blob: dfe198b9061da8dec0dc312d34a1ed1c8370d3a1 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.databinding.renderer
import com.android.databinding.renderer.BrRenderer
import com.android.databinding.ext.toCamelCase
import com.android.databinding.ext.times
import com.android.databinding.ext.indent
import com.android.databinding.ext.indentExceptFirst
import com.android.databinding.vo.LayoutExprBinding
import com.android.databinding.vo
import com.android.databinding.ext.toCamelCase
import com.android.databinding.ext.toCamelCaseAsVar
import com.android.databinding.ext.joinIndentedExceptFirst
import com.android.databinding.ext.joinIndented
/**
* Created by yboyar on 11/17/14.
*/
class ViewExprBinderRenderer(val pkg: String, val projectPackage: String, val baseClassName: String,
val layoutName:String, val lb: LayoutExprBinding) {
val className = "${baseClassName}Impl"
val interfaceName = "${baseClassName}"
val dirtyFlagName = "mDirty"
fun setChildren(v : vo.Variable) : String = if (v.isPrimitive || v.children.size == 0) ""
else
"""if (($dirtyFlagName | ${v.childrenDirtyFlagName}) != 0) { // ${v.childrenDirtyFlagsExpr}
if (${v.localName} != null) {
${v.children.map{"""
${it.localName} = ${it.getterOnParentLocal};
${if (it.isObservable) {
"updateRegistration(${it.localIdName}, ${it.localName});"
} else ""}
${setChildren(it).indentExceptFirst(1)}
"""}.joinIndented(1)}
} ${if(v.observableDescendents.size == 0) "" else {"""else {
${v.observableDescendents.map{"updateRegistration(${it.localIdName}, null);"}.joinIndentedExceptFirst(5)}
}"""}}
}
"""
public fun render(br : BrRenderer) : String =
"""
package $pkg;
import $projectPackage.R;
import com.android.databinding.library.Observable;
public class $className extends com.android.databinding.library.ViewDataBinder
implements $interfaceName {
private long $dirtyFlagName = -1;
${lb.rootVariables.values().filterNot{it.static}.map{
"${it.declare};"
}.joinIndented(1)}
${lb.bindingTargets.filter { it.bindings.size > 0 }.map {
"${it.viewClass} ${it.resolvedViewName};" }.joinIndented(1)
}
public $className(android.view.View root) {
super(root, ${lb.observableVariables.size});
${lb.bindingTargets.filter { it.bindings.size > 0 }.map { "this.${it.resolvedViewName} = (${it.viewClass})root.${it.findViewById};" }.join("\n ") }
}
public boolean setVariable(int variableId, Object variable){
switch(variableId) {
${lb.rootVariables.values().filterNot{it.static}.map { variable -> """
case ${br.toIntS(variable.name)}: {
${variable.setter}((${variable.klassName}) variable);
return true;
}
"""}.joinIndentedExceptFirst(1)}
}
return false;
}
${lb.rootVariables.values().filterNot{it.static}.map { variable -> """
public void ${variable.setter}(${variable.klassName} ${variable.name}) {
${if (variable.isObservable) {
"updateRegistration(${variable.localIdName}, ${variable.name});"
} else ""}
this.${variable.name} = ${variable.name};
$dirtyFlagName |= ${variable.dirtyFlagName};
super.requestRebind();
}
"""}.joinIndentedExceptFirst(1)}
${lb.observableVariables.map { variable -> """
public boolean ${variable.onChange}(${variable.klassName} ${variable.name}, int fieldId) {
switch (fieldId) {
${variable.children.map{"""
case ${br.toIntS(it.name)}: {
$dirtyFlagName |= ${it.dirtyFlagName};
return true;
}"""}.joinIndentedExceptFirst(1)}
case ${br.toIntS("")}:
$dirtyFlagName |= ${variable.dirtyFlagName};
return true;
}
return false;
}
"""}.joinIndentedExceptFirst(1)}
@Override
protected boolean onFieldChange(int mLocalFieldId, Object object, int fieldId) {
switch (mLocalFieldId) {
// TODO also handle non-observables. basically, set them
${lb.observableVariables.map {"""
case ${it.localIdName} :
return ${it.onChange}((${it.klassName})object, fieldId);
"""}.joinIndentedExceptFirst(1)}
}
return false;
}
${lb.bindingTargets.filter { it.bindings.size > 0 }.map { """
@Override
public ${it.viewClass} get${it.resolvedUniqueName}() {
return this.${it.resolvedViewName};
}"""}.joinIndentedExceptFirst(1) }
@Override
public void rebindDirty() {
final long dirty = $dirtyFlagName;
$dirtyFlagName = 0L; // mark everything non dirty
${lb.dynamicVariables.filterNot{it.isRootVariable}.map{
"${it.declareLocal}${"= ${it.defaultValue}"};"
}.joinIndentedExceptFirst(2)}
// collect variables that exist
${lb.rootVariables.filterNot{it.value.static}.map {
setChildren(it.value)}.joinIndentedExceptFirst(3)
}
// TODO find common ancestor from bound variables and do this binding in its setter.
// common ancestor might be null in which case we should do it here.
// we can even avoid checking another dirty here if it exactly matches the variable scope
// (unlikely because it is probably a field)
${lb.bindings.map { binding -> """
if ( (dirty & (${binding.isDirtyName})) != 0 ) {
// ${binding.expr.toReadableString()}
${binding.target.resolvedViewName}.${binding.setter}(${binding.expr.toJava()});
}"""}.joinIndented(2)}
}
${lb.observableVariables.map {
"private static final int ${it.localIdName} = ${it.localId};"
}.joinIndented(1)}
${lb.dynamicVariables.map {
"private static final long ${it.dirtyFlagName} = ${it.dirtyFlag}L;"
}.joinIndented(1)}
${lb.dynamicVariables.map {
"private static final long ${it.childrenDirtyFlagName} = ${it.childrenDirtyFlagsExpr};"
}.joinIndented(1)}
${lb.bindings.map {
"private static final long ${it.isDirtyName} = ${it.isDirtyExpr};"
}.joinIndented(1)}
/**
${lb.bindingTargets.map{ target ->
target.bindings.map { binding ->
"${binding.expr.toReadableString()}"
}.joinIndentedExceptFirst(1)
}.joinIndentedExceptFirst(2)}
**/
}
"""
public fun renderInterface(br : BrRenderer) : String =
"""
package $pkg;
public interface ${interfaceName} extends com.android.databinding.library.IViewDataBinder {
${lb.rootVariables.values().filterNot{it.static}.map { variable -> """
public void ${variable.setter}(${variable.klassName} ${variable.name});
"""}.joinIndentedExceptFirst(1)}
${lb.bindingTargets.filter { it.bindings.size > 0 }.map { """
public ${it.viewClass} get${it.resolvedUniqueName}();"""}.joinIndentedExceptFirst(1) }
}
"""
}