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 17package android.databinding.tool.expr; 18 19import android.databinding.tool.reflection.ModelAnalyzer; 20import android.databinding.tool.reflection.ModelClass; 21import android.databinding.tool.solver.ExecutionPath; 22import android.databinding.tool.writer.KCode; 23 24import com.google.common.collect.Lists; 25 26import java.util.ArrayList; 27import java.util.List; 28 29public 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