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.solver.ExecutionPath;
     22 import android.databinding.tool.writer.KCode;
     23 
     24 import com.google.common.collect.Lists;
     25 
     26 import java.util.ArrayList;
     27 import java.util.List;
     28 
     29 public class BracketExpr extends Expr {
     30 
     31     public enum BracketAccessor {
     32         ARRAY,
     33         LIST,
     34         MAP,
     35     }
     36 
     37     private BracketAccessor mAccessor;
     38 
     39     BracketExpr(Expr target, Expr arg) {
     40         super(target, arg);
     41     }
     42 
     43     @Override
     44     protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
     45         ModelClass targetType = getTarget().getResolvedType();
     46         if (targetType.isArray()) {
     47             mAccessor = BracketAccessor.ARRAY;
     48         } else if (targetType.isList()) {
     49             mAccessor = BracketAccessor.LIST;
     50         } else if (targetType.isMap()) {
     51             mAccessor = BracketAccessor.MAP;
     52         } else {
     53             throw new IllegalArgumentException("Cannot determine variable type used in [] " +
     54                     "expression. Cast the value to List, Map, " +
     55                     "or array. Type detected: " + targetType.toJavaCode());
     56         }
     57         return targetType.getComponentType();
     58     }
     59 
     60     @Override
     61     public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
     62         final List<ExecutionPath> targetPaths = getTarget().toExecutionPath(paths);
     63         // after this, we need a null check.
     64         List<ExecutionPath> result = new ArrayList<ExecutionPath>();
     65         if (getTarget() instanceof StaticIdentifierExpr) {
     66             result.addAll(toExecutionPathInOrder(paths, getTarget()));
     67         } else {
     68             for (ExecutionPath path : targetPaths) {
     69                 Expr cmp = getModel().comparison("!=", getTarget(),
     70                         getModel().symbol("null", Object.class));
     71                 path.addPath(cmp);
     72                 final ExecutionPath subPath = path.addBranch(cmp, true);
     73                 if (subPath != null) {
     74                     final List<ExecutionPath> argPath = getArg().toExecutionPath(subPath);
     75                     result.addAll(addJustMeToExecutionPath(argPath));
     76                 }
     77             }
     78         }
     79         return result;
     80     }
     81 
     82     @Override
     83     protected List<Dependency> constructDependencies() {
     84         final List<Dependency> dependencies = constructDynamicChildrenDependencies();
     85         for (Dependency dependency : dependencies) {
     86             if (dependency.getOther() == getTarget()) {
     87                 dependency.setMandatory(true);
     88             }
     89         }
     90         return dependencies;
     91     }
     92 
     93     protected String computeUniqueKey() {
     94         final String targetKey = getTarget().computeUniqueKey();
     95         return join(targetKey, "$", getArg().computeUniqueKey(), "$");
     96     }
     97 
     98     @Override
     99     public String getInvertibleError() {
    100         return null;
    101     }
    102 
    103     public Expr getTarget() {
    104         return getChildren().get(0);
    105     }
    106 
    107     public Expr getArg() {
    108         return getChildren().get(1);
    109     }
    110 
    111     public BracketAccessor getAccessor() {
    112         return mAccessor;
    113     }
    114 
    115     public boolean argCastsInteger() {
    116         return mAccessor != BracketAccessor.MAP && getArg().getResolvedType().isObject();
    117     }
    118 
    119     @Override
    120     protected KCode generateCode() {
    121         String cast = argCastsInteger() ? "(Integer) " : "";
    122         switch (getAccessor()) {
    123             case ARRAY: {
    124                 return new KCode().
    125                         app("getFromArray(", getTarget().toCode()).
    126                         app(", ").
    127                         app(cast, getArg().toCode()).app(")");
    128             }
    129             case LIST: {
    130                 ModelClass listType = ModelAnalyzer.getInstance().findClass(java.util.List.class).
    131                         erasure();
    132                 ModelClass targetType = getTarget().getResolvedType().erasure();
    133                 if (listType.isAssignableFrom(targetType)) {
    134                     return new KCode().
    135                             app("getFromList(", getTarget().toCode()).
    136                             app(", ").
    137                             app(cast, getArg().toCode()).
    138                             app(")");
    139                 } else {
    140                     return new KCode().
    141                             app("", getTarget().toCode()).
    142                             app(".get(").
    143                             app(cast, getArg().toCode()).
    144                             app(")");
    145                 }
    146             }
    147             case MAP:
    148                 return new KCode().
    149                         app("", getTarget().toCode()).
    150                         app(".get(", getArg().toCode()).
    151                         app(")");
    152         }
    153         throw new IllegalStateException("Invalid BracketAccessor type");
    154     }
    155 
    156     @Override
    157     public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
    158         Expr arg = getArg().cloneToModel(model);
    159         arg = argCastsInteger()
    160                 ? model.castExpr("int", model.castExpr("Integer", arg))
    161                 : arg;
    162         StaticIdentifierExpr viewDataBinding =
    163                 model.staticIdentifier(ModelAnalyzer.VIEW_DATA_BINDING);
    164         viewDataBinding.setUserDefinedType(ModelAnalyzer.VIEW_DATA_BINDING);
    165         ModelClass targetType = getTarget().getResolvedType();
    166         if ((targetType.isList() || targetType.isMap()) &&
    167                 value.getResolvedType().isPrimitive()) {
    168             ModelClass boxed = value.getResolvedType().box();
    169             value = model.castExpr(boxed.toJavaCode(), value);
    170         }
    171         List<Expr> args = Lists.newArrayList(getTarget().cloneToModel(model), arg, value);
    172         MethodCallExpr setter = model.methodCall(viewDataBinding, "setTo", args);
    173         setter.setAllowProtected();
    174         return setter;
    175     }
    176 
    177     @Override
    178     public Expr cloneToModel(ExprModel model) {
    179         return model.bracketExpr(getTarget().cloneToModel(model), getArg().cloneToModel(model));
    180     }
    181 
    182     @Override
    183     public String toString() {
    184         return getTarget().toString() + '[' + getArg() + ']';
    185     }
    186 }
    187