/* * Copyright (C) 2015 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 android.databinding.tool.expr; import android.databinding.tool.reflection.ModelAnalyzer; import android.databinding.tool.reflection.ModelClass; import android.databinding.tool.writer.KCode; import java.util.List; public class MathExpr extends Expr { final String mOp; MathExpr(Expr left, String op, Expr right) { super(left, right); mOp = op; } @Override protected String computeUniqueKey() { return addTwoWay(join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey())); } @Override protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { if ("+".equals(mOp)) { // TODO we need upper casting etc. if (getLeft().getResolvedType().isString() || getRight().getResolvedType().isString()) { return modelAnalyzer.findClass(String.class); } } return modelAnalyzer.findCommonParentOf(getLeft().getResolvedType(), getRight().getResolvedType()); } @Override protected List constructDependencies() { return constructDynamicChildrenDependencies(); } public String getOp() { return mOp; } public Expr getLeft() { return getChildren().get(0); } public Expr getRight() { return getChildren().get(1); } @Override protected KCode generateCode(boolean expand) { return new KCode().app("", getLeft().toCode(expand)).app(mOp, getRight().toCode(expand)); } @Override public String getInvertibleError() { if (mOp.equals("%")) { return "The modulus operator (%) is not supported in two-way binding."; } else if (getResolvedType().isString()) { return "String concatenation operator (+) is not supported in two-way binding."; } if (!getLeft().isDynamic()) { return getRight().getInvertibleError(); } else if (!getRight().isDynamic()) { return getLeft().getInvertibleError(); } else { return "Arithmetic operator " + mOp + " is not supported with two dynamic expressions."; } } private String inverseCast() { if (!getLeft().isDynamic()) { return inverseCast(getRight()); } else { return inverseCast(getLeft()); } } private String inverseCast(Expr expr) { if (!expr.getResolvedType().isAssignableFrom(getResolvedType())) { return "(" + getResolvedType() + ")"; } return null; } @Override public KCode toInverseCode(KCode value) { if (!isDynamic()) { return toCode(); } final Expr left = getLeft(); final Expr right = getRight(); final Expr constExpr = left.isDynamic() ? right : left; final Expr varExpr = left.isDynamic() ? left : right; final String cast = inverseCast(); if (cast != null) { value = new KCode(cast).app("(", value).app(")"); } switch (mOp.charAt(0)) { case '+': // const + x = value => x = value - const return varExpr.toInverseCode(value.app(" - (", constExpr.toCode()).app(")")); case '*': // const * x = value => x = value / const return varExpr.toInverseCode(value.app(" / (", constExpr.toCode()).app(")")); case '-': if (!left.isDynamic()) { // const - x = value => x = const - value) return varExpr.toInverseCode(new KCode() .app("(", constExpr.toCode()) .app(") - (", value) .app(")")); } else { // x - const = value => x = value + const) return varExpr.toInverseCode(value.app(" + ", constExpr.toCode())); } case '/': if (!left.isDynamic()) { // const / x = value => x = const / value return varExpr.toInverseCode(new KCode("(") .app("", constExpr.toCode()) .app(") / (", value) .app(")")); } else { // x / const = value => x = value * const return varExpr.toInverseCode(new KCode("(") .app("", value) .app(") * (", constExpr.toCode()) .app(")")); } } throw new IllegalStateException("Invalid math operation is not invertible: " + mOp); } }