Home | History | Annotate | Download | only in expr
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.databinding.tool.expr;
     18 
     19 import android.databinding.tool.reflection.ModelAnalyzer;
     20 import android.databinding.tool.reflection.ModelClass;
     21 import android.databinding.tool.writer.KCode;
     22 
     23 import java.util.List;
     24 
     25 public class MathExpr extends Expr {
     26     final String mOp;
     27     MathExpr(Expr left, String op, Expr right) {
     28         super(left, right);
     29         mOp = op;
     30     }
     31 
     32     @Override
     33     protected String computeUniqueKey() {
     34         return addTwoWay(join(getLeft().getUniqueKey(), mOp, getRight().getUniqueKey()));
     35     }
     36 
     37     @Override
     38     protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
     39         if ("+".equals(mOp)) {
     40             // TODO we need upper casting etc.
     41             if (getLeft().getResolvedType().isString()
     42                     || getRight().getResolvedType().isString()) {
     43                 return modelAnalyzer.findClass(String.class);
     44             }
     45         }
     46         return modelAnalyzer.findCommonParentOf(getLeft().getResolvedType(),
     47                 getRight().getResolvedType());
     48     }
     49 
     50     @Override
     51     protected List<Dependency> constructDependencies() {
     52         return constructDynamicChildrenDependencies();
     53     }
     54 
     55     public String getOp() {
     56         return mOp;
     57     }
     58 
     59     public Expr getLeft() {
     60         return getChildren().get(0);
     61     }
     62 
     63     public Expr getRight() {
     64         return getChildren().get(1);
     65     }
     66 
     67     @Override
     68     protected KCode generateCode(boolean expand) {
     69         return new KCode().app("", getLeft().toCode(expand)).app(mOp, getRight().toCode(expand));
     70     }
     71 
     72     @Override
     73     public String getInvertibleError() {
     74         if (mOp.equals("%")) {
     75             return "The modulus operator (%) is not supported in two-way binding.";
     76         } else if (getResolvedType().isString()) {
     77             return "String concatenation operator (+) is not supported in two-way binding.";
     78         }
     79         if (!getLeft().isDynamic()) {
     80             return getRight().getInvertibleError();
     81         } else if (!getRight().isDynamic()) {
     82             return getLeft().getInvertibleError();
     83         } else {
     84             return "Arithmetic operator " + mOp + " is not supported with two dynamic expressions.";
     85         }
     86     }
     87 
     88     private String inverseCast() {
     89         if (!getLeft().isDynamic()) {
     90             return inverseCast(getRight());
     91         } else {
     92             return inverseCast(getLeft());
     93         }
     94     }
     95 
     96     private String inverseCast(Expr expr) {
     97         if (!expr.getResolvedType().isAssignableFrom(getResolvedType())) {
     98             return "(" + getResolvedType() + ")";
     99         }
    100         return null;
    101     }
    102 
    103     @Override
    104     public KCode toInverseCode(KCode value) {
    105         if (!isDynamic()) {
    106             return toCode();
    107         }
    108         final Expr left = getLeft();
    109         final Expr right = getRight();
    110         final Expr constExpr = left.isDynamic() ? right : left;
    111         final Expr varExpr = left.isDynamic() ? left : right;
    112         final String cast = inverseCast();
    113         if (cast != null) {
    114             value = new KCode(cast).app("(", value).app(")");
    115         }
    116         switch (mOp.charAt(0)) {
    117             case '+': // const + x = value  => x = value - const
    118                 return varExpr.toInverseCode(value.app(" - (", constExpr.toCode()).app(")"));
    119             case '*': // const * x = value => x = value / const
    120                 return varExpr.toInverseCode(value.app(" / (", constExpr.toCode()).app(")"));
    121             case '-':
    122                 if (!left.isDynamic()) { // const - x = value => x = const - value)
    123                     return varExpr.toInverseCode(new KCode()
    124                             .app("(", constExpr.toCode())
    125                             .app(") - (", value)
    126                             .app(")"));
    127                 } else { // x - const = value => x = value + const)
    128                     return varExpr.toInverseCode(value.app(" + ", constExpr.toCode()));
    129                 }
    130             case '/':
    131                 if (!left.isDynamic()) { // const / x = value => x = const / value
    132                     return varExpr.toInverseCode(new KCode("(")
    133                             .app("", constExpr.toCode())
    134                             .app(") / (", value)
    135                             .app(")"));
    136                 } else { // x / const = value => x = value * const
    137                     return varExpr.toInverseCode(new KCode("(")
    138                             .app("", value)
    139                             .app(") * (", constExpr.toCode())
    140                             .app(")"));
    141                 }
    142         }
    143         throw new IllegalStateException("Invalid math operation is not invertible: " + mOp);
    144     }
    145 }
    146