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 package android.databinding.tool.store; 17 18 import android.databinding.InverseBindingListener; 19 import android.databinding.tool.reflection.ModelAnalyzer; 20 import android.databinding.tool.reflection.ModelClass; 21 import android.databinding.tool.reflection.ModelMethod; 22 import android.databinding.tool.util.GenerationalClassUtil; 23 import android.databinding.tool.util.L; 24 import android.databinding.tool.util.Preconditions; 25 import android.databinding.tool.util.StringUtils; 26 27 import java.io.IOException; 28 import java.io.Serializable; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.Collections; 32 import java.util.Comparator; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.Iterator; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.TreeMap; 40 41 import javax.annotation.processing.ProcessingEnvironment; 42 import javax.lang.model.element.ExecutableElement; 43 import javax.lang.model.element.Modifier; 44 import javax.lang.model.element.TypeElement; 45 import javax.lang.model.element.VariableElement; 46 import javax.lang.model.type.ArrayType; 47 import javax.lang.model.type.DeclaredType; 48 import javax.lang.model.type.TypeKind; 49 import javax.lang.model.type.TypeMirror; 50 51 public class SetterStore { 52 private static SetterStore sStore; 53 54 private final IntermediateV2 mStore; 55 private final ModelAnalyzer mClassAnalyzer; 56 private HashMap<String, List<String>> mInstanceAdapters; 57 private final HashSet<String> mInverseEventAttributes = new HashSet<String>(); 58 59 private Comparator<MultiAttributeSetter> COMPARE_MULTI_ATTRIBUTE_SETTERS = 60 new Comparator<MultiAttributeSetter>() { 61 @Override 62 public int compare(MultiAttributeSetter o1, MultiAttributeSetter o2) { 63 if (o1.attributes.length != o2.attributes.length) { 64 return o2.attributes.length - o1.attributes.length; 65 } 66 ModelClass view1 = mClassAnalyzer.findClass(o1.mKey.viewType, null).erasure(); 67 ModelClass view2 = mClassAnalyzer.findClass(o2.mKey.viewType, null).erasure(); 68 if (!view1.equals(view2)) { 69 if (view1.isAssignableFrom(view2)) { 70 return 1; 71 } else { 72 return -1; 73 } 74 } 75 if (!o1.mKey.attributeIndices.keySet() 76 .equals(o2.mKey.attributeIndices.keySet())) { 77 // order by attribute name 78 Iterator<String> o1Keys = o1.mKey.attributeIndices.keySet().iterator(); 79 Iterator<String> o2Keys = o2.mKey.attributeIndices.keySet().iterator(); 80 while (o1Keys.hasNext()) { 81 String key1 = o1Keys.next(); 82 String key2 = o2Keys.next(); 83 int compare = key1.compareTo(key2); 84 if (compare != 0) { 85 return compare; 86 } 87 } 88 Preconditions.check(false, 89 "The sets don't match! That means the keys shouldn't match also"); 90 } 91 // Same view type. Same attributes 92 for (String attribute : o1.mKey.attributeIndices.keySet()) { 93 final int index1 = o1.mKey.attributeIndices.get(attribute); 94 final int index2 = o2.mKey.attributeIndices.get(attribute); 95 ModelClass type1 = mClassAnalyzer 96 .findClass(o1.mKey.parameterTypes[index1], null); 97 ModelClass type2 = mClassAnalyzer 98 .findClass(o2.mKey.parameterTypes[index2], null); 99 if (type1.equals(type2)) { 100 continue; 101 } 102 if (o1.mCasts[index1] != null) { 103 if (o2.mCasts[index2] == null) { 104 return 1; // o2 is better 105 } else { 106 continue; // both are casts 107 } 108 } else if (o2.mCasts[index2] != null) { 109 return -1; // o1 is better 110 } 111 if (o1.mConverters[index1] != null) { 112 if (o2.mConverters[index2] == null) { 113 return 1; // o2 is better 114 } else { 115 continue; // both are conversions 116 } 117 } else if (o2.mConverters[index2] != null) { 118 return -1; // o1 is better 119 } 120 121 if (type1.isPrimitive()) { 122 if (type2.isPrimitive()) { 123 int type1ConversionLevel = ModelMethod 124 .getImplicitConversionLevel(type1); 125 int type2ConversionLevel = ModelMethod 126 .getImplicitConversionLevel(type2); 127 return type2ConversionLevel - type1ConversionLevel; 128 } else { 129 // type1 is primitive and has higher priority 130 return -1; 131 } 132 } else if (type2.isPrimitive()) { 133 return 1; 134 } 135 if (type1.isAssignableFrom(type2)) { 136 return 1; 137 } else if (type2.isAssignableFrom(type1)) { 138 return -1; 139 } 140 } 141 // hmmm... same view type, same attributes, same parameter types... ? 142 return 0; 143 } 144 }; 145 SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV2 store)146 private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV2 store) { 147 mClassAnalyzer = modelAnalyzer; 148 mStore = store; 149 for (HashMap<AccessorKey, InverseDescription> adapter : mStore.inverseAdapters.values()) { 150 for (InverseDescription inverseDescription : adapter.values()) { 151 mInverseEventAttributes.add(inverseDescription.event); 152 } 153 } 154 for (HashMap<String, InverseDescription> method : mStore.inverseMethods.values()) { 155 for (InverseDescription inverseDescription : method.values()) { 156 mInverseEventAttributes.add(inverseDescription.event); 157 } 158 } 159 } 160 get(ModelAnalyzer modelAnalyzer)161 public static SetterStore get(ModelAnalyzer modelAnalyzer) { 162 if (sStore == null) { 163 sStore = load(modelAnalyzer); 164 } 165 return sStore; 166 } 167 load(ModelAnalyzer modelAnalyzer)168 private static SetterStore load(ModelAnalyzer modelAnalyzer) { 169 IntermediateV2 store = new IntermediateV2(); 170 List<Intermediate> previousStores = GenerationalClassUtil 171 .loadObjects(GenerationalClassUtil.ExtensionFilter.SETTER_STORE); 172 for (Intermediate intermediate : previousStores) { 173 merge(store, intermediate); 174 } 175 return new SetterStore(modelAnalyzer, store); 176 } 177 addRenamedMethod(String attribute, String declaringClass, String method, TypeElement declaredOn)178 public void addRenamedMethod(String attribute, String declaringClass, String method, 179 TypeElement declaredOn) { 180 attribute = stripNamespace(attribute); 181 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 182 if (renamed == null) { 183 renamed = new HashMap<String, MethodDescription>(); 184 mStore.renamedMethods.put(attribute, renamed); 185 } 186 MethodDescription methodDescription = new MethodDescription( 187 declaredOn.getQualifiedName().toString(), method); 188 L.d("STORE addmethod desc %s", methodDescription); 189 renamed.put(declaringClass, methodDescription); 190 } 191 addInverseMethod(String attribute, String event, String declaringClass, String method, TypeElement declaredOn)192 public void addInverseMethod(String attribute, String event, String declaringClass, 193 String method, TypeElement declaredOn) { 194 attribute = stripNamespace(attribute); 195 event = stripNamespace(event); 196 HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute); 197 if (inverseMethods == null) { 198 inverseMethods = new HashMap<String, InverseDescription>(); 199 mStore.inverseMethods.put(attribute, inverseMethods); 200 } 201 InverseDescription methodDescription = new InverseDescription( 202 declaredOn.getQualifiedName().toString(), method, event); 203 L.d("STORE addInverseMethod desc %s", methodDescription); 204 inverseMethods.put(declaringClass, methodDescription); 205 } 206 addBindingAdapter(ProcessingEnvironment processingEnv, String attribute, ExecutableElement bindingMethod, boolean takesComponent)207 public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute, 208 ExecutableElement bindingMethod, boolean takesComponent) { 209 attribute = stripNamespace(attribute); 210 L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod); 211 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 212 213 if (adapters == null) { 214 adapters = new HashMap<AccessorKey, MethodDescription>(); 215 mStore.adapterMethods.put(attribute, adapters); 216 } 217 List<? extends VariableElement> parameters = bindingMethod.getParameters(); 218 final int viewIndex = takesComponent ? 1 : 0; 219 TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType()); 220 String view = getQualifiedName(viewType); 221 TypeMirror parameterType = eraseType(processingEnv, parameters.get(viewIndex + 1).asType()); 222 String value = getQualifiedName(parameterType); 223 224 AccessorKey key = new AccessorKey(view, value); 225 if (adapters.containsKey(key)) { 226 throw new IllegalArgumentException("Already exists!"); 227 } 228 229 adapters.put(key, new MethodDescription(bindingMethod, 1, takesComponent)); 230 } 231 addInverseAdapter(ProcessingEnvironment processingEnv, String attribute, String event, ExecutableElement bindingMethod, boolean takesComponent)232 public void addInverseAdapter(ProcessingEnvironment processingEnv, String attribute, 233 String event, ExecutableElement bindingMethod, boolean takesComponent) { 234 attribute = stripNamespace(attribute); 235 event = stripNamespace(event); 236 L.d("STORE addInverseAdapter %s %s", attribute, bindingMethod); 237 HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute); 238 239 if (adapters == null) { 240 adapters = new HashMap<AccessorKey, InverseDescription>(); 241 mStore.inverseAdapters.put(attribute, adapters); 242 } 243 List<? extends VariableElement> parameters = bindingMethod.getParameters(); 244 final int viewIndex = takesComponent ? 1 : 0; 245 TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType()); 246 String view = getQualifiedName(viewType); 247 TypeMirror returnType = eraseType(processingEnv, bindingMethod.getReturnType()); 248 String value = getQualifiedName(returnType); 249 250 AccessorKey key = new AccessorKey(view, value); 251 if (adapters.containsKey(key)) { 252 throw new IllegalArgumentException("Already exists!"); 253 } 254 255 adapters.put(key, new InverseDescription(bindingMethod, event, takesComponent)); 256 } 257 eraseType(ProcessingEnvironment processingEnv, TypeMirror typeMirror)258 private static TypeMirror eraseType(ProcessingEnvironment processingEnv, 259 TypeMirror typeMirror) { 260 if (hasTypeVar(typeMirror)) { 261 return processingEnv.getTypeUtils().erasure(typeMirror); 262 } else { 263 return typeMirror; 264 } 265 } 266 eraseType(ModelClass modelClass)267 private static ModelClass eraseType(ModelClass modelClass) { 268 if (hasTypeVar(modelClass)) { 269 return modelClass.erasure(); 270 } else { 271 return modelClass; 272 } 273 } 274 hasTypeVar(TypeMirror typeMirror)275 private static boolean hasTypeVar(TypeMirror typeMirror) { 276 TypeKind kind = typeMirror.getKind(); 277 if (kind == TypeKind.TYPEVAR) { 278 return true; 279 } else if (kind == TypeKind.ARRAY) { 280 return hasTypeVar(((ArrayType) typeMirror).getComponentType()); 281 } else if (kind == TypeKind.DECLARED) { 282 DeclaredType declaredType = (DeclaredType) typeMirror; 283 List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); 284 if (typeArguments == null || typeArguments.isEmpty()) { 285 return false; 286 } 287 for (TypeMirror arg : typeArguments) { 288 if (hasTypeVar(arg)) { 289 return true; 290 } 291 } 292 return false; 293 } else { 294 return false; 295 } 296 } 297 hasTypeVar(ModelClass type)298 private static boolean hasTypeVar(ModelClass type) { 299 if (type.isTypeVar()) { 300 return true; 301 } else if (type.isArray()) { 302 return hasTypeVar(type.getComponentType()); 303 } else { 304 List<ModelClass> typeArguments = type.getTypeArguments(); 305 if (typeArguments == null) { 306 return false; 307 } 308 for (ModelClass arg : typeArguments) { 309 if (hasTypeVar(arg)) { 310 return true; 311 } 312 } 313 return false; 314 } 315 } 316 addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes, ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll)317 public void addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes, 318 ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll) { 319 L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod); 320 MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod, 321 attributes, takesComponent, requireAll); 322 MethodDescription methodDescription = new MethodDescription(bindingMethod, 323 attributes.length, takesComponent); 324 mStore.multiValueAdapters.put(key, methodDescription); 325 } 326 stripAttributes(String[] attributes)327 private static String[] stripAttributes(String[] attributes) { 328 String[] strippedAttributes = new String[attributes.length]; 329 for (int i = 0; i < attributes.length; i++) { 330 if (attributes[i] != null) { 331 strippedAttributes[i] = stripNamespace(attributes[i]); 332 } 333 } 334 return strippedAttributes; 335 } 336 addUntaggableTypes(String[] typeNames, TypeElement declaredOn)337 public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) { 338 L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn); 339 String declaredType = declaredOn.getQualifiedName().toString(); 340 for (String type : typeNames) { 341 mStore.untaggableTypes.put(type, declaredType); 342 } 343 } 344 getQualifiedName(TypeMirror type)345 private static String getQualifiedName(TypeMirror type) { 346 final TypeKind kind = type.getKind(); 347 if (kind == TypeKind.ARRAY) { 348 return getQualifiedName(((ArrayType) type).getComponentType()) + "[]"; 349 } else if (kind == TypeKind.DECLARED && isIncompleteType(type)) { 350 DeclaredType declaredType = (DeclaredType) type; 351 return declaredType.asElement().toString(); 352 } else { 353 return type.toString(); 354 } 355 } 356 isIncompleteType(TypeMirror type)357 private static boolean isIncompleteType(TypeMirror type) { 358 final TypeKind kind = type.getKind(); 359 if (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD) { 360 return true; 361 } else if (kind == TypeKind.DECLARED) { 362 DeclaredType declaredType = (DeclaredType) type; 363 List<? extends TypeMirror> typeArgs = declaredType.getTypeArguments(); 364 if (typeArgs == null) { 365 return false; 366 } 367 for (TypeMirror arg : typeArgs) { 368 if (isIncompleteType(arg)) { 369 return true; 370 } 371 } 372 } 373 return false; 374 } 375 addConversionMethod(ExecutableElement conversionMethod)376 public void addConversionMethod(ExecutableElement conversionMethod) { 377 L.d("STORE addConversionMethod %s", conversionMethod); 378 List<? extends VariableElement> parameters = conversionMethod.getParameters(); 379 String fromType = getQualifiedName(parameters.get(0).asType()); 380 String toType = getQualifiedName(conversionMethod.getReturnType()); 381 MethodDescription methodDescription = new MethodDescription(conversionMethod, 1, false); 382 HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType); 383 if (convertTo == null) { 384 convertTo = new HashMap<String, MethodDescription>(); 385 mStore.conversionMethods.put(fromType, convertTo); 386 } 387 convertTo.put(toType, methodDescription); 388 } 389 clear(Set<String> classes)390 public void clear(Set<String> classes) { 391 ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>(); 392 for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) { 393 for (AccessorKey key : adapters.keySet()) { 394 MethodDescription description = adapters.get(key); 395 if (classes.contains(description.type)) { 396 removedAccessorKeys.add(key); 397 } 398 } 399 removeFromMap(adapters, removedAccessorKeys); 400 } 401 402 ArrayList<String> removedRenamed = new ArrayList<String>(); 403 for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) { 404 for (String key : renamed.keySet()) { 405 if (classes.contains(renamed.get(key).type)) { 406 removedRenamed.add(key); 407 } 408 } 409 removeFromMap(renamed, removedRenamed); 410 } 411 412 ArrayList<String> removedConversions = new ArrayList<String>(); 413 for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) { 414 for (String toType : convertTos.keySet()) { 415 MethodDescription methodDescription = convertTos.get(toType); 416 if (classes.contains(methodDescription.type)) { 417 removedConversions.add(toType); 418 } 419 } 420 removeFromMap(convertTos, removedConversions); 421 } 422 423 ArrayList<String> removedUntaggable = new ArrayList<String>(); 424 for (String typeName : mStore.untaggableTypes.keySet()) { 425 if (classes.contains(mStore.untaggableTypes.get(typeName))) { 426 removedUntaggable.add(typeName); 427 } 428 } 429 removeFromMap(mStore.untaggableTypes, removedUntaggable); 430 } 431 removeFromMap(Map<K, V> map, List<K> keys)432 private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) { 433 for (K key : keys) { 434 map.remove(key); 435 } 436 keys.clear(); 437 } 438 write(String projectPackage, ProcessingEnvironment processingEnvironment)439 public void write(String projectPackage, ProcessingEnvironment processingEnvironment) 440 throws IOException { 441 GenerationalClassUtil.writeIntermediateFile(processingEnvironment, 442 projectPackage, projectPackage + 443 GenerationalClassUtil.ExtensionFilter.SETTER_STORE.getExtension(), mStore); 444 } 445 stripNamespace(String attribute)446 private static String stripNamespace(String attribute) { 447 if (!attribute.startsWith("android:")) { 448 int colon = attribute.indexOf(':'); 449 if (colon >= 0) { 450 attribute = attribute.substring(colon + 1); 451 } 452 } 453 return attribute; 454 } 455 isTwoWayEventAttribute(String attribute)456 public boolean isTwoWayEventAttribute(String attribute) { 457 attribute = stripNamespace(attribute); 458 return mInverseEventAttributes.contains(attribute); 459 } getMultiAttributeSetterCalls(String[] attributes, ModelClass viewType, ModelClass[] valueType)460 public List<MultiAttributeSetter> getMultiAttributeSetterCalls(String[] attributes, 461 ModelClass viewType, ModelClass[] valueType) { 462 attributes = stripAttributes(attributes); 463 final ArrayList<MultiAttributeSetter> calls = new ArrayList<MultiAttributeSetter>(); 464 if (viewType != null && viewType.isGeneric()) { 465 List<ModelClass> viewGenerics = viewType.getTypeArguments(); 466 for (int i = 0; i < valueType.length; i++) { 467 valueType[i] = eraseType(valueType[i], viewGenerics); 468 } 469 viewType = viewType.erasure(); 470 } 471 ArrayList<MultiAttributeSetter> matching = getMatchingMultiAttributeSetters(attributes, 472 viewType, valueType); 473 Collections.sort(matching, COMPARE_MULTI_ATTRIBUTE_SETTERS); 474 while (!matching.isEmpty()) { 475 MultiAttributeSetter bestMatch = matching.get(0); 476 calls.add(bestMatch); 477 removeConsumedAttributes(matching, bestMatch.attributes); 478 } 479 return calls; 480 } 481 simpleName(String className)482 private static String simpleName(String className) { 483 int dotIndex = className.lastIndexOf('.'); 484 if (dotIndex < 0) { 485 return className; 486 } else { 487 return className.substring(dotIndex + 1); 488 } 489 } 490 getComponentBindingAdapters()491 public Map<String, List<String>> getComponentBindingAdapters() { 492 ensureInstanceAdapters(); 493 return mInstanceAdapters; 494 } 495 getBindingAdapterCall(String className)496 private String getBindingAdapterCall(String className) { 497 ensureInstanceAdapters(); 498 final String simpleName = simpleName(className); 499 List<String> adapters = mInstanceAdapters.get(simpleName); 500 if (adapters.size() == 1) { 501 return "get" + simpleName + "()"; 502 } else { 503 int index = adapters.indexOf(className) + 1; 504 return "get" + simpleName + index + "()"; 505 } 506 } 507 ensureInstanceAdapters()508 private void ensureInstanceAdapters() { 509 if (mInstanceAdapters == null) { 510 HashSet<String> adapters = new HashSet<String>(); 511 for (HashMap<AccessorKey, MethodDescription> methods : mStore.adapterMethods.values()) { 512 for (MethodDescription method : methods.values()) { 513 if (!method.isStatic) { 514 adapters.add(method.type); 515 } 516 } 517 } 518 for (MethodDescription method : mStore.multiValueAdapters.values()) { 519 if (!method.isStatic) { 520 adapters.add(method.type); 521 } 522 } 523 for (Map<AccessorKey, InverseDescription> methods : mStore.inverseAdapters.values()) { 524 for (InverseDescription method : methods.values()) { 525 if (!method.isStatic) { 526 adapters.add(method.type); 527 } 528 } 529 } 530 mInstanceAdapters = new HashMap<String, List<String>>(); 531 for (String adapter : adapters) { 532 final String simpleName = simpleName(adapter); 533 List<String> list = mInstanceAdapters.get(simpleName); 534 if (list == null) { 535 list = new ArrayList<String>(); 536 mInstanceAdapters.put(simpleName, list); 537 } 538 list.add(adapter); 539 } 540 for (List<String> list : mInstanceAdapters.values()) { 541 if (list.size() > 1) { 542 Collections.sort(list); 543 } 544 } 545 } 546 } 547 548 // Removes all MultiAttributeSetters that require any of the values in attributes removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching, String[] attributes)549 private static void removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching, 550 String[] attributes) { 551 for (int i = matching.size() - 1; i >= 0; i--) { 552 final MultiAttributeSetter setter = matching.get(i); 553 boolean found = false; 554 for (String attribute : attributes) { 555 if (isInArray(attribute, setter.attributes)) { 556 found = true; 557 break; 558 } 559 } 560 if (found) { 561 matching.remove(i); 562 } 563 } 564 } 565 566 // Linear search through the String array for a specific value. isInArray(String str, String[] array)567 private static boolean isInArray(String str, String[] array) { 568 for (String value : array) { 569 if (value.equals(str)) { 570 return true; 571 } 572 } 573 return false; 574 } 575 getMatchingMultiAttributeSetters(String[] attributes, ModelClass viewType, ModelClass[] valueType)576 private ArrayList<MultiAttributeSetter> getMatchingMultiAttributeSetters(String[] attributes, 577 ModelClass viewType, ModelClass[] valueType) { 578 final ArrayList<MultiAttributeSetter> setters = new ArrayList<MultiAttributeSetter>(); 579 for (MultiValueAdapterKey adapter : mStore.multiValueAdapters.keySet()) { 580 if (adapter.requireAll && adapter.attributes.length > attributes.length) { 581 continue; 582 } 583 ModelClass viewClass = mClassAnalyzer.findClass(adapter.viewType, null); 584 if (viewClass.isGeneric()) { 585 viewClass = viewClass.erasure(); 586 } 587 if (!viewClass.isAssignableFrom(viewType)) { 588 continue; 589 } 590 final MethodDescription method = mStore.multiValueAdapters.get(adapter); 591 final MultiAttributeSetter setter = createMultiAttributeSetter(method, attributes, 592 valueType, adapter); 593 if (setter != null) { 594 setters.add(setter); 595 } 596 } 597 return setters; 598 } 599 createMultiAttributeSetter(MethodDescription method, String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter)600 private MultiAttributeSetter createMultiAttributeSetter(MethodDescription method, 601 String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter) { 602 int matchingAttributes = 0; 603 String[] casts = new String[adapter.attributes.length]; 604 MethodDescription[] conversions = new MethodDescription[adapter.attributes.length]; 605 boolean[] supplied = new boolean[adapter.attributes.length]; 606 607 for (int i = 0; i < allAttributes.length; i++) { 608 Integer index = adapter.attributeIndices.get(allAttributes[i]); 609 if (index != null) { 610 supplied[index] = true; 611 matchingAttributes++; 612 final String parameterTypeStr = adapter.parameterTypes[index]; 613 final ModelClass parameterType = eraseType( 614 mClassAnalyzer.findClass(parameterTypeStr, null)); 615 final ModelClass attributeType = attributeValues[i]; 616 if (!parameterType.isAssignableFrom(attributeType)) { 617 if (ModelMethod.isBoxingConversion(parameterType, attributeType)) { 618 // automatic boxing is ok 619 continue; 620 } else if (ModelMethod.isImplicitConversion(attributeType, parameterType)) { 621 // implicit conversion is ok 622 continue; 623 } 624 // Look for a converter 625 conversions[index] = getConversionMethod(attributeType, parameterType, null); 626 if (conversions[index] == null) { 627 if (attributeType.isObject()) { 628 // Cast is allowed also 629 casts[index] = parameterTypeStr; 630 } else { 631 // Parameter type mismatch 632 return null; 633 } 634 } 635 } 636 } 637 } 638 639 if ((adapter.requireAll && matchingAttributes != adapter.attributes.length) || 640 matchingAttributes == 0) { 641 return null; 642 } else { 643 return new MultiAttributeSetter(adapter, supplied, method, conversions, casts); 644 } 645 } 646 getSetterCall(String attribute, ModelClass viewType, ModelClass valueType, Map<String, String> imports)647 public SetterCall getSetterCall(String attribute, ModelClass viewType, 648 ModelClass valueType, Map<String, String> imports) { 649 attribute = stripNamespace(attribute); 650 SetterCall setterCall = null; 651 MethodDescription conversionMethod = null; 652 if (viewType != null) { 653 viewType = viewType.erasure(); 654 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 655 ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports); 656 ModelClass bestViewType = null; 657 ModelClass bestValueType = null; 658 if (bestSetterMethod != null) { 659 bestViewType = bestSetterMethod.getDeclaringClass(); 660 bestValueType = bestSetterMethod.getParameterTypes()[0]; 661 setterCall = new ModelMethodSetter(bestSetterMethod); 662 } 663 664 if (adapters != null) { 665 for (AccessorKey key : adapters.keySet()) { 666 try { 667 ModelClass adapterViewType = mClassAnalyzer 668 .findClass(key.viewType, imports).erasure(); 669 if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) { 670 try { 671 L.d("setter parameter type is %s", key.valueType); 672 final ModelClass adapterValueType = eraseType(mClassAnalyzer 673 .findClass(key.valueType, imports)); 674 L.d("setter %s takes type %s, compared to %s", 675 adapters.get(key).method, adapterValueType.toJavaCode(), 676 valueType.toJavaCode()); 677 boolean isBetterView = bestViewType == null || 678 bestViewType.isAssignableFrom(adapterViewType); 679 if (isBetterParameter(valueType, adapterValueType, bestValueType, 680 isBetterView, imports)) { 681 bestViewType = adapterViewType; 682 bestValueType = adapterValueType; 683 MethodDescription adapter = adapters.get(key); 684 setterCall = new AdapterSetter(adapter, adapterValueType); 685 } 686 687 } catch (Exception e) { 688 L.e(e, "Unknown class: %s", key.valueType); 689 } 690 } 691 } catch (Exception e) { 692 L.e(e, "Unknown class: %s", key.viewType); 693 } 694 } 695 } 696 697 conversionMethod = getConversionMethod(valueType, bestValueType, imports); 698 if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) { 699 setterCall.setCast(bestValueType); 700 } 701 } 702 if (setterCall == null) { 703 if (viewType != null && !viewType.isViewDataBinding()) { 704 return null; // no setter found!! 705 } 706 setterCall = new DummySetter(getDefaultSetter(attribute)); 707 } 708 setterCall.setConverter(conversionMethod); 709 return setterCall; 710 } 711 getGetterCall(String attribute, ModelClass viewType, ModelClass valueType, Map<String, String> imports)712 public BindingGetterCall getGetterCall(String attribute, ModelClass viewType, 713 ModelClass valueType, Map<String, String> imports) { 714 if (viewType == null) { 715 return null; 716 } else if (viewType.isViewDataBinding()) { 717 return new ViewDataBindingGetterCall(viewType, attribute); 718 } 719 720 attribute = stripNamespace(attribute); 721 viewType = viewType.erasure(); 722 723 InverseMethod bestMethod = getBestGetter(viewType, valueType, attribute, imports); 724 HashMap<AccessorKey, InverseDescription> adapters = mStore.inverseAdapters.get(attribute); 725 if (adapters != null) { 726 for (AccessorKey key : adapters.keySet()) { 727 try { 728 ModelClass adapterViewType = mClassAnalyzer 729 .findClass(key.viewType, imports).erasure(); 730 if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) { 731 try { 732 L.d("getter return type is %s", key.valueType); 733 final ModelClass adapterValueType = eraseType(mClassAnalyzer 734 .findClass(key.valueType, imports)); 735 L.d("getter %s returns type %s, compared to %s", 736 adapters.get(key).method, adapterValueType.toJavaCode(), 737 valueType); 738 boolean isBetterView = bestMethod.viewType == null || 739 bestMethod.viewType.isAssignableFrom(adapterViewType); 740 if (valueType == null || 741 isBetterParameter(adapterValueType, valueType, 742 bestMethod.returnType, isBetterView, imports)) { 743 bestMethod.viewType = adapterViewType; 744 bestMethod.returnType = adapterValueType; 745 InverseDescription inverseDescription = adapters.get(key); 746 ModelClass listenerType = ModelAnalyzer.getInstance().findClass( 747 InverseBindingListener.class); 748 BindingSetterCall eventCall = getSetterCall( 749 inverseDescription.event, viewType, listenerType, imports); 750 if (eventCall == null) { 751 List<MultiAttributeSetter> setters = 752 getMultiAttributeSetterCalls( 753 new String[]{inverseDescription.event}, 754 viewType, new ModelClass[] {listenerType}); 755 if (setters.size() != 1) { 756 L.e("Could not find event '%s' on View type '%s'", 757 inverseDescription.event, 758 viewType.getCanonicalName()); 759 } else { 760 bestMethod.call = new AdapterGetter(inverseDescription, 761 setters.get(0), key.valueType); 762 } 763 } else { 764 bestMethod.call = new AdapterGetter(inverseDescription, 765 eventCall, key.valueType); 766 } 767 } 768 769 } catch (Exception e) { 770 L.e(e, "Unknown class: %s", key.valueType); 771 } 772 } 773 } catch (Exception e) { 774 L.e(e, "Unknown class: %s", key.viewType); 775 } 776 } 777 } 778 779 return bestMethod.call; 780 } 781 isUntaggable(String viewType)782 public boolean isUntaggable(String viewType) { 783 return mStore.untaggableTypes.containsKey(viewType); 784 } 785 getBestSetter(ModelClass viewType, ModelClass argumentType, String attribute, Map<String, String> imports)786 private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType, 787 String attribute, Map<String, String> imports) { 788 if (viewType.isGeneric()) { 789 argumentType = eraseType(argumentType, viewType.getTypeArguments()); 790 viewType = viewType.erasure(); 791 } 792 List<String> setterCandidates = new ArrayList<String>(); 793 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 794 if (renamed != null) { 795 for (String className : renamed.keySet()) { 796 try { 797 ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports); 798 if (renamedViewType.erasure().isAssignableFrom(viewType)) { 799 setterCandidates.add(renamed.get(className).method); 800 break; 801 } 802 } catch (Exception e) { 803 //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className); 804 } 805 } 806 } 807 setterCandidates.add(getDefaultSetter(attribute)); 808 setterCandidates.add(trimAttributeNamespace(attribute)); 809 810 ModelMethod bestMethod = null; 811 ModelClass bestParameterType = null; 812 List<ModelClass> args = new ArrayList<ModelClass>(); 813 args.add(argumentType); 814 for (String name : setterCandidates) { 815 ModelMethod[] methods = viewType.getMethods(name, 1); 816 817 for (ModelMethod method : methods) { 818 ModelClass[] parameterTypes = method.getParameterTypes(); 819 ModelClass param = parameterTypes[0]; 820 if (method.isVoid() && 821 isBetterParameter(argumentType, param, bestParameterType, true, imports)) { 822 bestParameterType = param; 823 bestMethod = method; 824 } 825 } 826 } 827 return bestMethod; 828 } 829 getBestGetter(ModelClass viewType, ModelClass valueType, String attribute, Map<String, String> imports)830 private InverseMethod getBestGetter(ModelClass viewType, ModelClass valueType, 831 String attribute, Map<String, String> imports) { 832 if (viewType.isGeneric()) { 833 if (valueType != null) { 834 valueType = eraseType(valueType, viewType.getTypeArguments()); 835 } 836 viewType = viewType.erasure(); 837 } 838 ModelClass bestReturnType = null; 839 InverseDescription bestDescription = null; 840 ModelClass bestViewType = null; 841 ModelMethod bestMethod = null; 842 843 HashMap<String, InverseDescription> inverseMethods = mStore.inverseMethods.get(attribute); 844 if (inverseMethods != null) { 845 for (String className : inverseMethods.keySet()) { 846 try { 847 ModelClass methodViewType = mClassAnalyzer.findClass(className, imports); 848 if (methodViewType.erasure().isAssignableFrom(viewType)) { 849 boolean isBetterViewType = bestViewType == null || 850 bestViewType.isAssignableFrom(methodViewType); 851 final InverseDescription inverseDescription = inverseMethods.get(className); 852 final String name = inverseDescription.method.isEmpty() ? 853 trimAttributeNamespace(attribute) : inverseDescription.method; 854 ModelMethod method = methodViewType.findInstanceGetter(name); 855 ModelClass returnType = method.getReturnType(null); // no parameters 856 if (valueType == null || bestReturnType == null || 857 isBetterParameter(returnType, valueType, bestReturnType, 858 isBetterViewType, imports)) { 859 bestDescription = inverseDescription; 860 bestReturnType = returnType; 861 bestViewType = methodViewType; 862 bestMethod = method; 863 } 864 } 865 } catch (Exception e) { 866 //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className); 867 } 868 } 869 } 870 871 BindingGetterCall call = null; 872 if (bestDescription != null) { 873 final ModelClass listenerType = ModelAnalyzer.getInstance().findClass( 874 InverseBindingListener.class); 875 SetterCall eventSetter = getSetterCall(bestDescription.event, viewType, 876 listenerType, imports); 877 if (eventSetter == null) { 878 List<MultiAttributeSetter> setters = getMultiAttributeSetterCalls( 879 new String[] {bestDescription.event}, viewType, 880 new ModelClass[] {listenerType}); 881 if (setters.size() != 1) { 882 L.e("Could not find event '%s' on View type '%s'", bestDescription.event, 883 viewType.getCanonicalName()); 884 bestViewType = null; 885 bestReturnType = null; 886 } else { 887 call = new ViewGetterCall(bestDescription, bestMethod, setters.get(0)); 888 } 889 } else { 890 call = new ViewGetterCall(bestDescription, bestMethod, eventSetter); 891 } 892 } 893 return new InverseMethod(call, bestReturnType, bestViewType); 894 } 895 eraseType(ModelClass type, List<ModelClass> typeParameters)896 private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) { 897 List<ModelClass> typeArguments = type.getTypeArguments(); 898 if (typeArguments == null || typeParameters == null) { 899 return type; 900 } 901 for (ModelClass arg : typeArguments) { 902 if (typeParameters.contains(arg)) { 903 return type.erasure(); 904 } 905 } 906 return type; 907 } 908 trimAttributeNamespace(String attribute)909 private static String trimAttributeNamespace(String attribute) { 910 final int colonIndex = attribute.indexOf(':'); 911 return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1); 912 } 913 getDefaultSetter(String attribute)914 private static String getDefaultSetter(String attribute) { 915 return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute)); 916 } 917 isBetterParameter(ModelClass argument, ModelClass parameter, ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports)918 private boolean isBetterParameter(ModelClass argument, ModelClass parameter, 919 ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) { 920 // Right view type. Check the value 921 if (!isBetterViewTypeMatch && oldParameter.equals(argument)) { 922 return false; 923 } else if (argument.equals(parameter)) { 924 // Exact match 925 return true; 926 } else if (!isBetterViewTypeMatch && 927 ModelMethod.isBoxingConversion(oldParameter, argument)) { 928 return false; 929 } else if (ModelMethod.isBoxingConversion(parameter, argument)) { 930 // Boxing/unboxing is second best 931 return true; 932 } else { 933 int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter); 934 if (ModelMethod.isImplicitConversion(argument, parameter)) { 935 // Better implicit conversion 936 int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter); 937 return oldConversionLevel < 0 || conversionLevel < oldConversionLevel; 938 } else if (oldConversionLevel >= 0) { 939 return false; 940 } else if (parameter.isAssignableFrom(argument)) { 941 // Right type, see if it is better than the current best match. 942 if (oldParameter == null) { 943 return true; 944 } else { 945 return oldParameter.isAssignableFrom(parameter); 946 } 947 } else { 948 MethodDescription conversionMethod = getConversionMethod(argument, parameter, 949 imports); 950 if (conversionMethod != null) { 951 return true; 952 } 953 if (getConversionMethod(argument, oldParameter, imports) != null) { 954 return false; 955 } 956 return argument.isObject() && !parameter.isPrimitive(); 957 } 958 } 959 } 960 getConversionMethod(ModelClass from, ModelClass to, Map<String, String> imports)961 private MethodDescription getConversionMethod(ModelClass from, ModelClass to, 962 Map<String, String> imports) { 963 if (from != null && to != null) { 964 if (to.isObject()) { 965 return null; 966 } 967 for (String fromClassName : mStore.conversionMethods.keySet()) { 968 try { 969 ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports); 970 if (canUseForConversion(from, convertFrom)) { 971 HashMap<String, MethodDescription> conversion = 972 mStore.conversionMethods.get(fromClassName); 973 for (String toClassName : conversion.keySet()) { 974 try { 975 ModelClass convertTo = mClassAnalyzer.findClass(toClassName, 976 imports); 977 if (canUseForConversion(convertTo, to)) { 978 return conversion.get(toClassName); 979 } 980 } catch (Exception e) { 981 L.d(e, "Unknown class: %s", toClassName); 982 } 983 } 984 } 985 } catch (Exception e) { 986 L.d(e, "Unknown class: %s", fromClassName); 987 } 988 } 989 } 990 return null; 991 } 992 canUseForConversion(ModelClass from, ModelClass to)993 private boolean canUseForConversion(ModelClass from, ModelClass to) { 994 if (from.isIncomplete() || to.isIncomplete()) { 995 from = from.erasure(); 996 to = to.erasure(); 997 } 998 return from.equals(to) || ModelMethod.isBoxingConversion(from, to) || 999 to.isAssignableFrom(from); 1000 } 1001 merge(IntermediateV2 store, Intermediate dumpStore)1002 private static void merge(IntermediateV2 store, Intermediate dumpStore) { 1003 IntermediateV2 intermediateV2 = (IntermediateV2) dumpStore.upgrade(); 1004 merge(store.adapterMethods, intermediateV2.adapterMethods); 1005 merge(store.renamedMethods, intermediateV2.renamedMethods); 1006 merge(store.conversionMethods, intermediateV2.conversionMethods); 1007 store.multiValueAdapters.putAll(intermediateV2.multiValueAdapters); 1008 store.untaggableTypes.putAll(intermediateV2.untaggableTypes); 1009 merge(store.inverseAdapters, intermediateV2.inverseAdapters); 1010 merge(store.inverseMethods, intermediateV2.inverseMethods); 1011 } 1012 merge(HashMap<K, HashMap<V, D>> first, HashMap<K, HashMap<V, D>> second)1013 private static <K, V, D> void merge(HashMap<K, HashMap<V, D>> first, 1014 HashMap<K, HashMap<V, D>> second) { 1015 for (K key : second.keySet()) { 1016 HashMap<V, D> firstVals = first.get(key); 1017 HashMap<V, D> secondVals = second.get(key); 1018 if (firstVals == null) { 1019 first.put(key, secondVals); 1020 } else { 1021 for (V key2 : secondVals.keySet()) { 1022 if (!firstVals.containsKey(key2)) { 1023 firstVals.put(key2, secondVals.get(key2)); 1024 } 1025 } 1026 } 1027 } 1028 } 1029 createAdapterCall(MethodDescription adapter, String componentExpression, String viewExpression, String... args)1030 private static String createAdapterCall(MethodDescription adapter, 1031 String componentExpression, String viewExpression, String... args) { 1032 StringBuilder sb = new StringBuilder(); 1033 1034 if (adapter.isStatic) { 1035 sb.append(adapter.type); 1036 } else { 1037 final SetterStore setterStore = SetterStore.get(ModelAnalyzer.getInstance()); 1038 final String binderCall = setterStore.getBindingAdapterCall(adapter.type); 1039 sb.append(componentExpression).append('.').append(binderCall); 1040 } 1041 sb.append('.').append(adapter.method).append('('); 1042 if (adapter.componentClass != null) { 1043 if (!"DataBindingComponent".equals(adapter.componentClass)) { 1044 sb.append('(').append(adapter.componentClass).append(") "); 1045 } 1046 sb.append(componentExpression).append(", "); 1047 } 1048 sb.append(viewExpression); 1049 for (String arg: args) { 1050 sb.append(", ").append(arg); 1051 } 1052 sb.append(')'); 1053 return sb.toString(); 1054 } 1055 1056 private static class MultiValueAdapterKey implements Serializable { 1057 private static final long serialVersionUID = 1; 1058 1059 public final String viewType; 1060 1061 public final String[] attributes; 1062 1063 public final String[] parameterTypes; 1064 1065 public final boolean requireAll; 1066 1067 public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>(); 1068 MultiValueAdapterKey(ProcessingEnvironment processingEnv, ExecutableElement method, String[] attributes, boolean takesComponent, boolean requireAll)1069 public MultiValueAdapterKey(ProcessingEnvironment processingEnv, 1070 ExecutableElement method, String[] attributes, boolean takesComponent, 1071 boolean requireAll) { 1072 this.attributes = stripAttributes(attributes); 1073 this.requireAll = requireAll; 1074 List<? extends VariableElement> parameters = method.getParameters(); 1075 final int argStart = 1 + (takesComponent ? 1 : 0); 1076 this.viewType = getQualifiedName(eraseType(processingEnv, 1077 parameters.get(argStart - 1).asType())); 1078 this.parameterTypes = new String[parameters.size() - argStart]; 1079 for (int i = 0; i < attributes.length; i++) { 1080 TypeMirror typeMirror = eraseType(processingEnv, 1081 parameters.get(i + argStart).asType()); 1082 this.parameterTypes[i] = getQualifiedName(typeMirror); 1083 attributeIndices.put(this.attributes[i], i); 1084 } 1085 } 1086 1087 @Override equals(Object obj)1088 public boolean equals(Object obj) { 1089 if (!(obj instanceof MultiValueAdapterKey)) { 1090 return false; 1091 } 1092 final MultiValueAdapterKey that = (MultiValueAdapterKey) obj; 1093 if (!this.viewType.equals(that.viewType) || 1094 this.attributes.length != that.attributes.length || 1095 !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) { 1096 return false; 1097 } 1098 1099 for (int i = 0; i < this.attributes.length; i++) { 1100 final int thatIndex = that.attributeIndices.get(this.attributes[i]); 1101 final String thisParameter = parameterTypes[i]; 1102 final String thatParameter = that.parameterTypes[thatIndex]; 1103 if (!thisParameter.equals(thatParameter)) { 1104 return false; 1105 } 1106 } 1107 return true; 1108 } 1109 1110 @Override hashCode()1111 public int hashCode() { 1112 return mergedHashCode(viewType, attributeIndices.keySet()); 1113 } 1114 } 1115 mergedHashCode(Object... objects)1116 private static int mergedHashCode(Object... objects) { 1117 return Arrays.hashCode(objects); 1118 } 1119 1120 private static class MethodDescription implements Serializable { 1121 1122 private static final long serialVersionUID = 1; 1123 1124 public final String type; 1125 1126 public final String method; 1127 1128 public final boolean requiresOldValue; 1129 1130 public final boolean isStatic; 1131 1132 public final String componentClass; 1133 MethodDescription(String type, String method)1134 public MethodDescription(String type, String method) { 1135 this.type = type; 1136 this.method = method; 1137 this.requiresOldValue = false; 1138 this.isStatic = true; 1139 this.componentClass = null; 1140 L.d("BINARY created method desc 1 %s %s", type, method ); 1141 } 1142 MethodDescription(ExecutableElement method, int numAttributes, boolean takesComponent)1143 public MethodDescription(ExecutableElement method, int numAttributes, 1144 boolean takesComponent) { 1145 TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); 1146 this.type = enclosingClass.getQualifiedName().toString(); 1147 this.method = method.getSimpleName().toString(); 1148 final int argStart = 1 + (takesComponent ? 1 : 0); 1149 this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2; 1150 this.isStatic = method.getModifiers().contains(Modifier.STATIC); 1151 this.componentClass = takesComponent 1152 ? getQualifiedName(method.getParameters().get(0).asType()) 1153 : null; 1154 1155 L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); 1156 } 1157 1158 @Override equals(Object obj)1159 public boolean equals(Object obj) { 1160 if (obj instanceof MethodDescription) { 1161 MethodDescription that = (MethodDescription) obj; 1162 return that.type.equals(this.type) && that.method.equals(this.method); 1163 } else { 1164 return false; 1165 } 1166 } 1167 1168 @Override hashCode()1169 public int hashCode() { 1170 return mergedHashCode(type, method); 1171 } 1172 1173 @Override toString()1174 public String toString() { 1175 return type + "." + method + "()"; 1176 } 1177 } 1178 1179 private static class InverseDescription extends MethodDescription { 1180 private static final long serialVersionUID = 1; 1181 1182 public final String event; 1183 InverseDescription(String type, String method, String event)1184 public InverseDescription(String type, String method, String event) { 1185 super(type, method); 1186 this.event = event; 1187 } 1188 InverseDescription(ExecutableElement method, String event, boolean takesComponent)1189 public InverseDescription(ExecutableElement method, String event, boolean takesComponent) { 1190 super(method, 1, takesComponent); 1191 this.event = event; 1192 } 1193 1194 @Override equals(Object obj)1195 public boolean equals(Object obj) { 1196 if (!super.equals(obj) || !(obj instanceof InverseDescription)) { 1197 return false; 1198 } 1199 return event.equals(((InverseDescription) obj).event); 1200 } 1201 1202 @Override hashCode()1203 public int hashCode() { 1204 return mergedHashCode(type, method, event); 1205 } 1206 } 1207 1208 private static class AccessorKey implements Serializable { 1209 1210 private static final long serialVersionUID = 1; 1211 1212 public final String viewType; 1213 1214 public final String valueType; 1215 AccessorKey(String viewType, String valueType)1216 public AccessorKey(String viewType, String valueType) { 1217 this.viewType = viewType; 1218 this.valueType = valueType; 1219 } 1220 1221 @Override hashCode()1222 public int hashCode() { 1223 return mergedHashCode(viewType, valueType); 1224 } 1225 1226 @Override equals(Object obj)1227 public boolean equals(Object obj) { 1228 if (obj instanceof AccessorKey) { 1229 AccessorKey that = (AccessorKey) obj; 1230 return viewType.equals(that.valueType) && valueType.equals(that.valueType); 1231 } else { 1232 return false; 1233 } 1234 } 1235 1236 @Override toString()1237 public String toString() { 1238 return "AK(" + viewType + ", " + valueType + ")"; 1239 } 1240 } 1241 1242 private interface Intermediate extends Serializable { upgrade()1243 Intermediate upgrade(); 1244 } 1245 1246 private static class IntermediateV1 implements Serializable, Intermediate { 1247 private static final long serialVersionUID = 1; 1248 public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = 1249 new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); 1250 public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = 1251 new HashMap<String, HashMap<String, MethodDescription>>(); 1252 public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = 1253 new HashMap<String, HashMap<String, MethodDescription>>(); 1254 public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); 1255 public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters = 1256 new HashMap<MultiValueAdapterKey, MethodDescription>(); 1257 IntermediateV1()1258 public IntermediateV1() { 1259 } 1260 1261 @Override upgrade()1262 public Intermediate upgrade() { 1263 IntermediateV2 v2 = new IntermediateV2(); 1264 v2.adapterMethods.putAll(adapterMethods); 1265 v2.renamedMethods.putAll(renamedMethods); 1266 v2.conversionMethods.putAll(conversionMethods); 1267 v2.untaggableTypes.putAll(untaggableTypes); 1268 v2.multiValueAdapters.putAll(multiValueAdapters); 1269 return v2; 1270 } 1271 } 1272 1273 private static class IntermediateV2 extends IntermediateV1 { 1274 private static final long serialVersionUID = 0xA45C2EB637E35C07L; 1275 public final HashMap<String, HashMap<AccessorKey, InverseDescription>> inverseAdapters = 1276 new HashMap<String, HashMap<AccessorKey, InverseDescription>>(); 1277 public final HashMap<String, HashMap<String, InverseDescription>> inverseMethods = 1278 new HashMap<String, HashMap<String, InverseDescription>>(); 1279 1280 @Override upgrade()1281 public Intermediate upgrade() { 1282 return this; 1283 } 1284 } 1285 1286 public static class DummySetter extends SetterCall { 1287 private String mMethodName; 1288 DummySetter(String methodName)1289 public DummySetter(String methodName) { 1290 mMethodName = methodName; 1291 } 1292 1293 @Override toJavaInternal(String componentExpression, String viewExpression, String valueExpression)1294 public String toJavaInternal(String componentExpression, String viewExpression, 1295 String valueExpression) { 1296 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 1297 } 1298 1299 @Override toJavaInternal(String componentExpression, String viewExpression, String oldValue, String valueExpression)1300 public String toJavaInternal(String componentExpression, String viewExpression, 1301 String oldValue, String valueExpression) { 1302 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 1303 } 1304 1305 @Override getMinApi()1306 public int getMinApi() { 1307 return 1; 1308 } 1309 1310 @Override requiresOldValue()1311 public boolean requiresOldValue() { 1312 return false; 1313 } 1314 1315 @Override getParameterTypes()1316 public ModelClass[] getParameterTypes() { 1317 return new ModelClass[] { 1318 ModelAnalyzer.getInstance().findClass(Object.class) 1319 }; 1320 } 1321 1322 @Override getBindingAdapterInstanceClass()1323 public String getBindingAdapterInstanceClass() { 1324 return null; 1325 } 1326 } 1327 1328 public static class AdapterSetter extends SetterCall { 1329 final MethodDescription mAdapter; 1330 final ModelClass mParameterType; 1331 AdapterSetter(MethodDescription adapter, ModelClass parameterType)1332 public AdapterSetter(MethodDescription adapter, ModelClass parameterType) { 1333 mAdapter = adapter; 1334 mParameterType = parameterType; 1335 } 1336 1337 @Override toJavaInternal(String componentExpression, String viewExpression, String valueExpression)1338 public String toJavaInternal(String componentExpression, String viewExpression, 1339 String valueExpression) { 1340 return createAdapterCall(mAdapter, componentExpression, 1341 viewExpression, mCastString + valueExpression); 1342 } 1343 1344 @Override toJavaInternal(String componentExpression, String viewExpression, String oldValue, String valueExpression)1345 protected String toJavaInternal(String componentExpression, String viewExpression, 1346 String oldValue, String valueExpression) { 1347 return createAdapterCall(mAdapter, componentExpression, 1348 viewExpression, mCastString + oldValue, mCastString + valueExpression); 1349 } 1350 1351 @Override getMinApi()1352 public int getMinApi() { 1353 return 1; 1354 } 1355 1356 @Override requiresOldValue()1357 public boolean requiresOldValue() { 1358 return mAdapter.requiresOldValue; 1359 } 1360 1361 @Override getParameterTypes()1362 public ModelClass[] getParameterTypes() { 1363 return new ModelClass[] { mParameterType }; 1364 } 1365 1366 @Override getBindingAdapterInstanceClass()1367 public String getBindingAdapterInstanceClass() { 1368 return mAdapter.isStatic ? null : mAdapter.type; 1369 } 1370 } 1371 1372 public static class ModelMethodSetter extends SetterCall { 1373 final ModelMethod mModelMethod; 1374 ModelMethodSetter(ModelMethod modelMethod)1375 public ModelMethodSetter(ModelMethod modelMethod) { 1376 mModelMethod = modelMethod; 1377 } 1378 1379 @Override toJavaInternal(String componentExpression, String viewExpression, String valueExpression)1380 public String toJavaInternal(String componentExpression, String viewExpression, 1381 String valueExpression) { 1382 return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + 1383 valueExpression + ")"; 1384 } 1385 1386 @Override toJavaInternal(String componentExpression, String viewExpression, String oldValue, String valueExpression)1387 protected String toJavaInternal(String componentExpression, String viewExpression, 1388 String oldValue, String valueExpression) { 1389 return viewExpression + "." + mModelMethod.getName() + "(" + 1390 mCastString + oldValue + ", " + mCastString + valueExpression + ")"; 1391 } 1392 1393 @Override getMinApi()1394 public int getMinApi() { 1395 return mModelMethod.getMinApi(); 1396 } 1397 1398 @Override requiresOldValue()1399 public boolean requiresOldValue() { 1400 return mModelMethod.getParameterTypes().length == 3; 1401 } 1402 1403 @Override getParameterTypes()1404 public ModelClass[] getParameterTypes() { 1405 return new ModelClass[] { mModelMethod.getParameterTypes()[0] }; 1406 } 1407 1408 @Override getBindingAdapterInstanceClass()1409 public String getBindingAdapterInstanceClass() { 1410 return null; 1411 } 1412 } 1413 1414 public interface BindingSetterCall { toJava(String componentExpression, String viewExpression, String... valueExpressions)1415 String toJava(String componentExpression, String viewExpression, 1416 String... valueExpressions); 1417 getMinApi()1418 int getMinApi(); 1419 requiresOldValue()1420 boolean requiresOldValue(); 1421 getParameterTypes()1422 ModelClass[] getParameterTypes(); 1423 getBindingAdapterInstanceClass()1424 String getBindingAdapterInstanceClass(); 1425 } 1426 1427 public static abstract class SetterCall implements BindingSetterCall { 1428 private MethodDescription mConverter; 1429 protected String mCastString = ""; 1430 SetterCall()1431 public SetterCall() { 1432 } 1433 setConverter(MethodDescription converter)1434 public void setConverter(MethodDescription converter) { 1435 mConverter = converter; 1436 } 1437 toJavaInternal(String componentExpression, String viewExpression, String converted)1438 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1439 String converted); 1440 toJavaInternal(String componentExpression, String viewExpression, String oldValue, String converted)1441 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1442 String oldValue, String converted); 1443 1444 @Override toJava(String componentExpression, String viewExpression, String... valueExpression)1445 public final String toJava(String componentExpression, String viewExpression, 1446 String... valueExpression) { 1447 Preconditions.check(valueExpression.length == 2, "value expressions size must be 2"); 1448 if (requiresOldValue()) { 1449 return toJavaInternal(componentExpression, viewExpression, 1450 convertValue(valueExpression[0]), convertValue(valueExpression[1])); 1451 } else { 1452 return toJavaInternal(componentExpression, viewExpression, 1453 convertValue(valueExpression[1])); 1454 } 1455 } 1456 convertValue(String valueExpression)1457 protected String convertValue(String valueExpression) { 1458 return mConverter == null ? valueExpression : 1459 mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; 1460 } 1461 getMinApi()1462 abstract public int getMinApi(); 1463 setCast(ModelClass castTo)1464 public void setCast(ModelClass castTo) { 1465 mCastString = "(" + castTo.toJavaCode() + ") "; 1466 } 1467 } 1468 1469 public static class MultiAttributeSetter implements BindingSetterCall { 1470 public final String[] attributes; 1471 private final MethodDescription mAdapter; 1472 private final MethodDescription[] mConverters; 1473 private final String[] mCasts; 1474 private final MultiValueAdapterKey mKey; 1475 private final boolean[] mSupplied; 1476 MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied, MethodDescription adapter, MethodDescription[] converters, String[] casts)1477 public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied, 1478 MethodDescription adapter, MethodDescription[] converters, String[] casts) { 1479 Preconditions.check(converters != null && 1480 converters.length == key.attributes.length && 1481 casts != null && casts.length == key.attributes.length && 1482 supplied.length == key.attributes.length, 1483 "invalid arguments to create multi attr setter"); 1484 this.mAdapter = adapter; 1485 this.mConverters = converters; 1486 this.mCasts = casts; 1487 this.mKey = key; 1488 this.mSupplied = supplied; 1489 if (key.requireAll) { 1490 this.attributes = key.attributes; 1491 } else { 1492 int numSupplied = 0; 1493 for (int i = 0; i < mKey.attributes.length; i++) { 1494 if (supplied[i]) { 1495 numSupplied++; 1496 } 1497 } 1498 if (numSupplied == key.attributes.length) { 1499 this.attributes = key.attributes; 1500 } else { 1501 this.attributes = new String[numSupplied]; 1502 int attrIndex = 0; 1503 for (int i = 0; i < key.attributes.length; i++) { 1504 if (supplied[i]) { 1505 attributes[attrIndex++] = key.attributes[i]; 1506 } 1507 } 1508 } 1509 } 1510 } 1511 1512 @Override toJava(String componentExpression, String viewExpression, String[] valueExpressions)1513 public final String toJava(String componentExpression, String viewExpression, 1514 String[] valueExpressions) { 1515 Preconditions.check(valueExpressions.length == attributes.length * 2, 1516 "MultiAttributeSetter needs %s items, received %s", 1517 Arrays.toString(attributes), Arrays.toString(valueExpressions)); 1518 final int numAttrs = mKey.attributes.length; 1519 String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)]; 1520 1521 final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs; 1522 int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length; 1523 final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1524 StringBuilder argBuilder = new StringBuilder(); 1525 final int endIndex = numAttrs * 2; 1526 for (int i = startIndex; i < endIndex; i++) { 1527 argBuilder.setLength(0); 1528 if (!mSupplied[i % numAttrs]) { 1529 final String paramType = mKey.parameterTypes[i % numAttrs]; 1530 final String defaultValue = modelAnalyzer.getDefaultValue(paramType); 1531 argBuilder.append('(') 1532 .append(paramType) 1533 .append(')') 1534 .append(defaultValue); 1535 } else { 1536 if (mConverters[i % numAttrs] != null) { 1537 final MethodDescription converter = mConverters[i % numAttrs]; 1538 argBuilder.append(converter.type) 1539 .append('.') 1540 .append(converter.method) 1541 .append('(') 1542 .append(valueExpressions[attrIndex]) 1543 .append(')'); 1544 } else { 1545 if (mCasts[i % numAttrs] != null) { 1546 argBuilder.append('(') 1547 .append(mCasts[i % numAttrs]) 1548 .append(')'); 1549 } 1550 argBuilder.append(valueExpressions[attrIndex]); 1551 } 1552 attrIndex++; 1553 } 1554 args[i - startIndex] = argBuilder.toString(); 1555 } 1556 return createAdapterCall(mAdapter, componentExpression, viewExpression, args); 1557 } 1558 1559 @Override getMinApi()1560 public int getMinApi() { 1561 return 1; 1562 } 1563 1564 @Override requiresOldValue()1565 public boolean requiresOldValue() { 1566 return mAdapter.requiresOldValue; 1567 } 1568 1569 @Override getParameterTypes()1570 public ModelClass[] getParameterTypes() { 1571 ModelClass[] parameters = new ModelClass[attributes.length]; 1572 String[] paramTypeStrings = mKey.parameterTypes; 1573 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1574 int attrIndex = 0; 1575 for (int i = 0; i < mKey.attributes.length; i++) { 1576 if (mSupplied[i]) { 1577 parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null); 1578 } 1579 } 1580 return parameters; 1581 } 1582 1583 @Override getBindingAdapterInstanceClass()1584 public String getBindingAdapterInstanceClass() { 1585 return mAdapter.isStatic ? null : mAdapter.type; 1586 } 1587 1588 @Override toString()1589 public String toString() { 1590 return "MultiAttributeSetter{" + 1591 "attributes=" + Arrays.toString(attributes) + 1592 ", mAdapter=" + mAdapter + 1593 ", mConverters=" + Arrays.toString(mConverters) + 1594 ", mCasts=" + Arrays.toString(mCasts) + 1595 ", mKey=" + mKey + 1596 '}'; 1597 } 1598 } 1599 1600 public static class ViewDataBindingEventSetter implements BindingSetterCall { 1601 ViewDataBindingEventSetter()1602 public ViewDataBindingEventSetter() { 1603 } 1604 1605 @Override toJava(String componentExpression, String viewExpression, String... valueExpressions)1606 public String toJava(String componentExpression, String viewExpression, 1607 String... valueExpressions) { 1608 return "setBindingInverseListener(" + viewExpression + ", " + 1609 valueExpressions[0] + ", " + valueExpressions[1] + ")"; 1610 } 1611 1612 @Override getMinApi()1613 public int getMinApi() { 1614 return 0; 1615 } 1616 1617 @Override requiresOldValue()1618 public boolean requiresOldValue() { 1619 return true; 1620 } 1621 1622 @Override getParameterTypes()1623 public ModelClass[] getParameterTypes() { 1624 ModelClass[] parameterTypes = new ModelClass[1]; 1625 parameterTypes[0] = ModelAnalyzer.getInstance().findClass( 1626 "android.databinding.ViewDataBinder.PropertyChangedInverseListener", null); 1627 return parameterTypes; 1628 } 1629 1630 @Override getBindingAdapterInstanceClass()1631 public String getBindingAdapterInstanceClass() { 1632 return null; 1633 } 1634 } 1635 1636 public interface BindingGetterCall { toJava(String componentExpression, String viewExpression)1637 String toJava(String componentExpression, String viewExpression); 1638 getGetterType()1639 String getGetterType(); 1640 getMinApi()1641 int getMinApi(); 1642 getBindingAdapterInstanceClass()1643 String getBindingAdapterInstanceClass(); 1644 setBindingAdapterCall(String method)1645 void setBindingAdapterCall(String method); 1646 getEvent()1647 BindingSetterCall getEvent(); 1648 getEventAttribute()1649 String getEventAttribute(); 1650 } 1651 1652 public static class ViewDataBindingGetterCall implements BindingGetterCall { 1653 private final String mGetter; 1654 private final BindingSetterCall mEventSetter; 1655 private final String mAttribute; 1656 private final ModelClass mBindingClass; 1657 ViewDataBindingGetterCall(ModelClass bindingClass, String attribute)1658 public ViewDataBindingGetterCall(ModelClass bindingClass, String attribute) { 1659 final int colonIndex = attribute.indexOf(':'); 1660 mAttribute = attribute.substring(colonIndex + 1); 1661 mGetter = "get" + StringUtils.capitalize(mAttribute); 1662 mEventSetter = new ViewDataBindingEventSetter(); 1663 mBindingClass = bindingClass; 1664 } 1665 1666 @Override toJava(String componentExpression, String viewExpression)1667 public String toJava(String componentExpression, String viewExpression) { 1668 return viewExpression + "." + mGetter + "()"; 1669 } 1670 1671 @Override getGetterType()1672 public String getGetterType() { 1673 return mBindingClass.findInstanceGetter(mGetter).getReturnType().toJavaCode(); 1674 } 1675 1676 @Override getMinApi()1677 public int getMinApi() { 1678 return 0; 1679 } 1680 1681 @Override getBindingAdapterInstanceClass()1682 public String getBindingAdapterInstanceClass() { 1683 return null; 1684 } 1685 1686 @Override setBindingAdapterCall(String method)1687 public void setBindingAdapterCall(String method) { 1688 } 1689 1690 @Override getEvent()1691 public BindingSetterCall getEvent() { 1692 return mEventSetter; 1693 } 1694 1695 @Override getEventAttribute()1696 public String getEventAttribute() { 1697 return mAttribute; 1698 } 1699 } 1700 1701 public static class ViewGetterCall implements BindingGetterCall { 1702 private final InverseDescription mInverseDescription; 1703 private final BindingSetterCall mEventCall; 1704 private final ModelMethod mMethod; 1705 ViewGetterCall(InverseDescription inverseDescription, ModelMethod method, BindingSetterCall eventCall)1706 public ViewGetterCall(InverseDescription inverseDescription, ModelMethod method, 1707 BindingSetterCall eventCall) { 1708 mInverseDescription = inverseDescription; 1709 mEventCall = eventCall; 1710 mMethod = method; 1711 } 1712 1713 @Override getEvent()1714 public BindingSetterCall getEvent() { 1715 return mEventCall; 1716 } 1717 1718 @Override getEventAttribute()1719 public String getEventAttribute() { 1720 return mInverseDescription.event; 1721 } 1722 1723 @Override toJava(String componentExpression, String viewExpression)1724 public String toJava(String componentExpression, String viewExpression) { 1725 return viewExpression + "." + mMethod.getName() + "()"; 1726 } 1727 1728 @Override getGetterType()1729 public String getGetterType() { 1730 return mMethod.getReturnType().toJavaCode(); 1731 } 1732 1733 @Override getMinApi()1734 public int getMinApi() { 1735 return mMethod.getMinApi(); 1736 } 1737 1738 @Override getBindingAdapterInstanceClass()1739 public String getBindingAdapterInstanceClass() { 1740 return null; 1741 } 1742 1743 @Override setBindingAdapterCall(String method)1744 public void setBindingAdapterCall(String method) { 1745 } 1746 } 1747 1748 public static class AdapterGetter implements BindingGetterCall { 1749 private final InverseDescription mInverseDescription; 1750 private String mBindingAdapterCall; 1751 private final BindingSetterCall mEventCall; 1752 private final String mGetterType; 1753 AdapterGetter(InverseDescription description, BindingSetterCall eventCall, String getterType)1754 public AdapterGetter(InverseDescription description, BindingSetterCall eventCall, 1755 String getterType) { 1756 mInverseDescription = description; 1757 mEventCall = eventCall; 1758 mGetterType = getterType; 1759 } 1760 1761 @Override getGetterType()1762 public String getGetterType() { 1763 return mGetterType; 1764 } 1765 1766 @Override toJava(String componentExpression, String viewExpression)1767 public String toJava(String componentExpression, String viewExpression) { 1768 StringBuilder sb = new StringBuilder(); 1769 1770 if (mInverseDescription.isStatic) { 1771 sb.append(mInverseDescription.type); 1772 } else { 1773 sb.append(componentExpression).append('.').append(mBindingAdapterCall); 1774 } 1775 sb.append('.').append(mInverseDescription.method).append('('); 1776 if (mInverseDescription.componentClass != null) { 1777 if (!"DataBindingComponent".equals(mInverseDescription.componentClass)) { 1778 sb.append('(').append(mInverseDescription.componentClass).append(") "); 1779 } 1780 sb.append(componentExpression).append(", "); 1781 } 1782 sb.append(viewExpression).append(')'); 1783 return sb.toString(); 1784 } 1785 1786 @Override getMinApi()1787 public int getMinApi() { 1788 return 1; 1789 } 1790 1791 @Override getBindingAdapterInstanceClass()1792 public String getBindingAdapterInstanceClass() { 1793 return mInverseDescription.isStatic ? null : mInverseDescription.type; 1794 } 1795 1796 @Override setBindingAdapterCall(String method)1797 public void setBindingAdapterCall(String method) { 1798 mBindingAdapterCall = method; 1799 } 1800 1801 @Override getEvent()1802 public BindingSetterCall getEvent() { 1803 return mEventCall; 1804 } 1805 1806 @Override getEventAttribute()1807 public String getEventAttribute() { 1808 return mInverseDescription.event; 1809 } 1810 } 1811 1812 private static class InverseMethod { 1813 public BindingGetterCall call; 1814 public ModelClass returnType; 1815 public ModelClass viewType; 1816 InverseMethod(BindingGetterCall call, ModelClass returnType, ModelClass viewType)1817 public InverseMethod(BindingGetterCall call, ModelClass returnType, ModelClass viewType) { 1818 this.call = call; 1819 this.returnType = returnType; 1820 this.viewType = viewType; 1821 } 1822 } 1823 } 1824