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 */ 16package android.databinding.tool.expr; 17 18import android.databinding.tool.BindingTarget; 19import android.databinding.tool.reflection.ModelAnalyzer; 20import android.databinding.tool.reflection.ModelClass; 21import android.databinding.tool.writer.KCode; 22import android.databinding.tool.writer.LayoutBinderWriterKt; 23 24import java.util.HashMap; 25import java.util.List; 26import java.util.Map; 27 28public class ResourceExpr extends Expr { 29 30 private final static Map<String, String> RESOURCE_TYPE_TO_R_OBJECT; 31 static { 32 RESOURCE_TYPE_TO_R_OBJECT = new HashMap<String, String>(); 33 RESOURCE_TYPE_TO_R_OBJECT.put("colorStateList", "color "); 34 RESOURCE_TYPE_TO_R_OBJECT.put("dimenOffset", "dimen "); 35 RESOURCE_TYPE_TO_R_OBJECT.put("dimenSize", "dimen "); 36 RESOURCE_TYPE_TO_R_OBJECT.put("intArray", "array "); 37 RESOURCE_TYPE_TO_R_OBJECT.put("stateListAnimator", "animator "); 38 RESOURCE_TYPE_TO_R_OBJECT.put("stringArray", "array "); 39 RESOURCE_TYPE_TO_R_OBJECT.put("typedArray", "array"); 40 } 41 // lazily initialized 42 private Map<String, ModelClass> mResourceToTypeMapping; 43 44 protected final String mPackage; 45 46 protected final String mResourceType; 47 48 protected final String mResourceId; 49 50 protected final BindingTarget mTarget; 51 52 public ResourceExpr(BindingTarget target, String packageName, String resourceType, 53 String resourceName, List<Expr> args) { 54 super(args); 55 mTarget = target; 56 if ("android".equals(packageName)) { 57 mPackage = "android."; 58 } else { 59 mPackage = ""; 60 } 61 mResourceType = resourceType; 62 mResourceId = resourceName; 63 } 64 65 private Map<String, ModelClass> getResourceToTypeMapping(ModelAnalyzer modelAnalyzer) { 66 if (mResourceToTypeMapping == null) { 67 final Map<String, String> imports = getModel().getImports(); 68 mResourceToTypeMapping = new HashMap<String, ModelClass>(); 69 mResourceToTypeMapping.put("anim", modelAnalyzer.findClass("android.view.animation.Animation", 70 imports)); 71 mResourceToTypeMapping.put("animator", modelAnalyzer.findClass("android.animation.Animator", 72 imports)); 73 mResourceToTypeMapping.put("colorStateList", 74 modelAnalyzer.findClass("android.content.res.ColorStateList", 75 imports)); 76 mResourceToTypeMapping.put("drawable", modelAnalyzer.findClass("android.graphics.drawable.Drawable", 77 imports)); 78 mResourceToTypeMapping.put("stateListAnimator", 79 modelAnalyzer.findClass("android.animation.StateListAnimator", 80 imports)); 81 mResourceToTypeMapping.put("transition", modelAnalyzer.findClass("android.transition.Transition", 82 imports)); 83 mResourceToTypeMapping.put("typedArray", modelAnalyzer.findClass("android.content.res.TypedArray", 84 imports)); 85 mResourceToTypeMapping.put("interpolator", 86 modelAnalyzer.findClass("android.view.animation.Interpolator", imports)); 87 mResourceToTypeMapping.put("bool", modelAnalyzer.findClass(boolean.class)); 88 mResourceToTypeMapping.put("color", modelAnalyzer.findClass(int.class)); 89 mResourceToTypeMapping.put("dimenOffset", modelAnalyzer.findClass(int.class)); 90 mResourceToTypeMapping.put("dimenSize", modelAnalyzer.findClass(int.class)); 91 mResourceToTypeMapping.put("id", modelAnalyzer.findClass(int.class)); 92 mResourceToTypeMapping.put("integer", modelAnalyzer.findClass(int.class)); 93 mResourceToTypeMapping.put("layout", modelAnalyzer.findClass(int.class)); 94 mResourceToTypeMapping.put("dimen", modelAnalyzer.findClass(float.class)); 95 mResourceToTypeMapping.put("fraction", modelAnalyzer.findClass(float.class)); 96 mResourceToTypeMapping.put("intArray", modelAnalyzer.findClass(int[].class)); 97 mResourceToTypeMapping.put("string", modelAnalyzer.findClass(String.class)); 98 mResourceToTypeMapping.put("stringArray", modelAnalyzer.findClass(String[].class)); 99 } 100 return mResourceToTypeMapping; 101 } 102 103 @Override 104 protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) { 105 final Map<String, ModelClass> mapping = getResourceToTypeMapping( 106 modelAnalyzer); 107 final ModelClass modelClass = mapping.get(mResourceType); 108 if (modelClass != null) { 109 return modelClass; 110 } 111 if ("plurals".equals(mResourceType)) { 112 if (getChildren().isEmpty()) { 113 return modelAnalyzer.findClass(int.class); 114 } else { 115 return modelAnalyzer.findClass(String.class); 116 } 117 } 118 return modelAnalyzer.findClass(mResourceType, getModel().getImports()); 119 } 120 121 @Override 122 protected List<Dependency> constructDependencies() { 123 return constructDynamicChildrenDependencies(); 124 } 125 126 @Override 127 protected String computeUniqueKey() { 128 String base = toString(); 129 String view = ""; 130 if (requiresView()) { 131 view = LayoutBinderWriterKt.getFieldName(mTarget); 132 } 133 return join(base, view, computeChildrenKey()); 134 } 135 136 @Override 137 protected KCode generateCode() { 138 return new KCode(toJava()); 139 } 140 141 @Override 142 public Expr cloneToModel(ExprModel model) { 143 String pkg = mPackage.isEmpty() ? "" : "android"; 144 return model.resourceExpr(mTarget, pkg, mResourceType, mResourceId, 145 cloneToModel(model, getChildren())); 146 } 147 148 public String getResourceId() { 149 return mPackage + "R." + getResourceObject() + "." + mResourceId; 150 } 151 152 @Override 153 public String getInvertibleError() { 154 return "Resources may not be the target of a two-way binding expression: " + 155 computeUniqueKey(); 156 } 157 158 private boolean requiresView() { 159 return !mTarget.isBinder() && !("anim".equals(mResourceType) || 160 "animator".equals(mResourceType) || 161 "id".equals(mResourceType) || 162 "interpolator".equals(mResourceType) || 163 "layout".equals(mResourceType) || 164 "stateListAnimator".equals(mResourceType) || 165 "transition".equals(mResourceType)); 166 } 167 168 public String toJava() { 169 final String context = "getRoot().getContext()"; 170 final String viewName = requiresView() ? LayoutBinderWriterKt.getFieldName(mTarget) : 171 "getRoot()"; 172 final String resources = viewName + ".getResources()"; 173 final String resourceName = mPackage + "R." + getResourceObject() + "." + mResourceId; 174 if ("anim".equals(mResourceType)) return "android.view.animation.AnimationUtils.loadAnimation(" + context + ", " + resourceName + ")"; 175 if ("animator".equals(mResourceType)) return "android.animation.AnimatorInflater.loadAnimator(" + context + ", " + resourceName + ")"; 176 if ("bool".equals(mResourceType)) return resources + ".getBoolean(" + resourceName + ")"; 177 if ("color".equals(mResourceType)) return "android.databinding.DynamicUtil.getColorFromResource(" + viewName + ", " + resourceName + ")"; 178 if ("colorStateList".equals(mResourceType)) return "android.databinding.DynamicUtil.getColorStateListFromResource(" + viewName + ", " + resourceName + ")"; 179 if ("dimen".equals(mResourceType)) return resources + ".getDimension(" + resourceName + ")"; 180 if ("dimenOffset".equals(mResourceType)) return resources + ".getDimensionPixelOffset(" + resourceName + ")"; 181 if ("dimenSize".equals(mResourceType)) return resources + ".getDimensionPixelSize(" + resourceName + ")"; 182 if ("drawable".equals(mResourceType)) return "android.databinding.DynamicUtil.getDrawableFromResource(" + viewName + ", " + resourceName + ")"; 183 if ("fraction".equals(mResourceType)) { 184 String base = getChildCode(0, "1"); 185 String pbase = getChildCode(1, "1"); 186 return resources + ".getFraction(" + resourceName + ", " + base + ", " + pbase + 187 ")"; 188 } 189 if ("id".equals(mResourceType)) return resourceName; 190 if ("intArray".equals(mResourceType)) return resources + ".getIntArray(" + resourceName + ")"; 191 if ("integer".equals(mResourceType)) return resources + ".getInteger(" + resourceName + ")"; 192 if ("interpolator".equals(mResourceType)) return "android.view.animation.AnimationUtils.loadInterpolator(" + context + ", " + resourceName + ")"; 193 if ("layout".equals(mResourceType)) return resourceName; 194 if ("plurals".equals(mResourceType)) { 195 if (getChildren().isEmpty()) { 196 return resourceName; 197 } else { 198 return makeParameterCall(resources, resourceName, "getQuantityString"); 199 } 200 } 201 if ("stateListAnimator".equals(mResourceType)) return "android.animation.AnimatorInflater.loadStateListAnimator(" + context + ", " + resourceName + ")"; 202 if ("string".equals(mResourceType)) return makeParameterCall(resources, resourceName, "getString"); 203 if ("stringArray".equals(mResourceType)) return resources + ".getStringArray(" + resourceName + ")"; 204 if ("transition".equals(mResourceType)) return "android.transition.TransitionInflater.from(" + context + ").inflateTransition(" + resourceName + ")"; 205 if ("typedArray".equals(mResourceType)) return resources + ".obtainTypedArray(" + resourceName + ")"; 206 final String property = Character.toUpperCase(mResourceType.charAt(0)) + 207 mResourceType.substring(1); 208 return resources + ".get" + property + "(" + resourceName + ")"; 209 210 } 211 212 private String getChildCode(int childIndex, String defaultValue) { 213 if (getChildren().size() <= childIndex) { 214 return defaultValue; 215 } else { 216 return getChildren().get(childIndex).toCode().generate(); 217 } 218 } 219 220 private String makeParameterCall(String resources, String resourceName, String methodCall) { 221 StringBuilder sb = new StringBuilder(resources); 222 sb.append('.').append(methodCall).append("(").append(resourceName); 223 for (Expr expr : getChildren()) { 224 sb.append(", ").append(expr.toCode().generate()); 225 } 226 sb.append(")"); 227 return sb.toString(); 228 } 229 230 private String getResourceObject() { 231 String rFileObject = RESOURCE_TYPE_TO_R_OBJECT.get(mResourceType); 232 if (rFileObject == null) { 233 rFileObject = mResourceType; 234 } 235 return rFileObject; 236 } 237 238 @Override 239 public String toString() { 240 if (mPackage == null) { 241 return "@" + mResourceType + "/" + mResourceId; 242 } else { 243 return "@" + "android:" + mResourceType + "/" + mResourceId; 244 } 245 } 246} 247