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