SetterStore.java revision 793e979f25e190162eacf46d6a4efc3efc1d2f91
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.store; 17 18import org.apache.commons.lang3.StringUtils; 19 20import android.databinding.tool.reflection.ModelAnalyzer; 21import android.databinding.tool.reflection.ModelClass; 22import android.databinding.tool.reflection.ModelMethod; 23import android.databinding.tool.util.GenerationalClassUtil; 24import android.databinding.tool.util.L; 25import android.databinding.tool.util.Preconditions; 26 27import java.io.IOException; 28import java.io.Serializable; 29import java.util.ArrayList; 30import java.util.Arrays; 31import java.util.Collections; 32import java.util.Comparator; 33import java.util.HashMap; 34import java.util.Iterator; 35import java.util.List; 36import java.util.Map; 37import java.util.Set; 38import java.util.TreeMap; 39 40import javax.annotation.processing.ProcessingEnvironment; 41import javax.lang.model.element.ExecutableElement; 42import javax.lang.model.element.Modifier; 43import javax.lang.model.element.TypeElement; 44import javax.lang.model.element.VariableElement; 45import javax.lang.model.type.ArrayType; 46import javax.lang.model.type.DeclaredType; 47import javax.lang.model.type.TypeKind; 48import javax.lang.model.type.TypeMirror; 49 50public class SetterStore { 51 private static SetterStore sStore; 52 53 private final IntermediateV1 mStore; 54 private final ModelAnalyzer mClassAnalyzer; 55 56 private Comparator<MultiAttributeSetter> COMPARE_MULTI_ATTRIBUTE_SETTERS = 57 new Comparator<MultiAttributeSetter>() { 58 @Override 59 public int compare(MultiAttributeSetter o1, MultiAttributeSetter o2) { 60 if (o1.attributes.length != o2.attributes.length) { 61 return o2.attributes.length - o1.attributes.length; 62 } 63 ModelClass view1 = mClassAnalyzer.findClass(o1.mKey.viewType, null).erasure(); 64 ModelClass view2 = mClassAnalyzer.findClass(o2.mKey.viewType, null).erasure(); 65 if (!view1.equals(view2)) { 66 if (view1.isAssignableFrom(view2)) { 67 return 1; 68 } else { 69 return -1; 70 } 71 } 72 if (!o1.mKey.attributeIndices.keySet() 73 .equals(o2.mKey.attributeIndices.keySet())) { 74 // order by attribute name 75 Iterator<String> o1Keys = o1.mKey.attributeIndices.keySet().iterator(); 76 Iterator<String> o2Keys = o2.mKey.attributeIndices.keySet().iterator(); 77 while (o1Keys.hasNext()) { 78 String key1 = o1Keys.next(); 79 String key2 = o2Keys.next(); 80 int compare = key1.compareTo(key2); 81 if (compare != 0) { 82 return compare; 83 } 84 } 85 Preconditions.check(false, 86 "The sets don't match! That means the keys shouldn't match also"); 87 } 88 // Same view type. Same attributes 89 for (String attribute : o1.mKey.attributeIndices.keySet()) { 90 final int index1 = o1.mKey.attributeIndices.get(attribute); 91 final int index2 = o2.mKey.attributeIndices.get(attribute); 92 ModelClass type1 = mClassAnalyzer 93 .findClass(o1.mKey.parameterTypes[index1], null); 94 ModelClass type2 = mClassAnalyzer 95 .findClass(o2.mKey.parameterTypes[index2], null); 96 if (type1.equals(type2)) { 97 continue; 98 } 99 if (o1.mCasts[index1] != null) { 100 if (o2.mCasts[index2] == null) { 101 return 1; // o2 is better 102 } else { 103 continue; // both are casts 104 } 105 } else if (o2.mCasts[index2] != null) { 106 return -1; // o1 is better 107 } 108 if (o1.mConverters[index1] != null) { 109 if (o2.mConverters[index2] == null) { 110 return 1; // o2 is better 111 } else { 112 continue; // both are conversions 113 } 114 } else if (o2.mConverters[index2] != null) { 115 return -1; // o1 is better 116 } 117 118 if (type1.isPrimitive()) { 119 if (type2.isPrimitive()) { 120 int type1ConversionLevel = ModelMethod 121 .getImplicitConversionLevel(type1); 122 int type2ConversionLevel = ModelMethod 123 .getImplicitConversionLevel(type2); 124 return type2ConversionLevel - type1ConversionLevel; 125 } else { 126 // type1 is primitive and has higher priority 127 return -1; 128 } 129 } else if (type2.isPrimitive()) { 130 return 1; 131 } 132 if (type1.isAssignableFrom(type2)) { 133 return 1; 134 } else if (type2.isAssignableFrom(type1)) { 135 return -1; 136 } 137 } 138 // hmmm... same view type, same attributes, same parameter types... ? 139 return 0; 140 } 141 }; 142 143 private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV1 store) { 144 mClassAnalyzer = modelAnalyzer; 145 mStore = store; 146 } 147 148 public static SetterStore get(ModelAnalyzer modelAnalyzer) { 149 if (sStore == null) { 150 sStore = load(modelAnalyzer); 151 } 152 return sStore; 153 } 154 155 private static SetterStore load(ModelAnalyzer modelAnalyzer) { 156 IntermediateV1 store = new IntermediateV1(); 157 List<Intermediate> previousStores = GenerationalClassUtil 158 .loadObjects(GenerationalClassUtil.ExtensionFilter.SETTER_STORE); 159 for (Intermediate intermediate : previousStores) { 160 merge(store, intermediate); 161 } 162 return new SetterStore(modelAnalyzer, store); 163 } 164 165 public void addRenamedMethod(String attribute, String declaringClass, String method, 166 TypeElement declaredOn) { 167 attribute = stripNamespace(attribute); 168 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 169 if (renamed == null) { 170 renamed = new HashMap<String, MethodDescription>(); 171 mStore.renamedMethods.put(attribute, renamed); 172 } 173 MethodDescription methodDescription = new MethodDescription( 174 declaredOn.getQualifiedName().toString(), method); 175 L.d("STORE addmethod desc %s", methodDescription); 176 renamed.put(declaringClass, methodDescription); 177 } 178 179 public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute, 180 ExecutableElement bindingMethod, boolean takesComponent) { 181 attribute = stripNamespace(attribute); 182 L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod); 183 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 184 185 if (adapters == null) { 186 adapters = new HashMap<AccessorKey, MethodDescription>(); 187 mStore.adapterMethods.put(attribute, adapters); 188 } 189 List<? extends VariableElement> parameters = bindingMethod.getParameters(); 190 final int viewIndex = takesComponent ? 1 : 0; 191 TypeMirror viewType = eraseType(processingEnv, parameters.get(viewIndex).asType()); 192 String view = getQualifiedName(viewType); 193 TypeMirror parameterType = eraseType(processingEnv, parameters.get(viewIndex + 1).asType()); 194 String value = getQualifiedName(parameterType); 195 196 AccessorKey key = new AccessorKey(view, value); 197 if (adapters.containsKey(key)) { 198 throw new IllegalArgumentException("Already exists!"); 199 } 200 201 adapters.put(key, new MethodDescription(bindingMethod, 1, takesComponent)); 202 } 203 204 private static TypeMirror eraseType(ProcessingEnvironment processingEnv, 205 TypeMirror typeMirror) { 206 if (hasTypeVar(typeMirror)) { 207 return processingEnv.getTypeUtils().erasure(typeMirror); 208 } else { 209 return typeMirror; 210 } 211 } 212 213 private static ModelClass eraseType(ModelClass modelClass) { 214 if (hasTypeVar(modelClass)) { 215 return modelClass.erasure(); 216 } else { 217 return modelClass; 218 } 219 } 220 221 private static boolean hasTypeVar(TypeMirror typeMirror) { 222 TypeKind kind = typeMirror.getKind(); 223 if (kind == TypeKind.TYPEVAR) { 224 return true; 225 } else if (kind == TypeKind.ARRAY) { 226 return hasTypeVar(((ArrayType) typeMirror).getComponentType()); 227 } else if (kind == TypeKind.DECLARED) { 228 DeclaredType declaredType = (DeclaredType) typeMirror; 229 List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); 230 if (typeArguments == null || typeArguments.isEmpty()) { 231 return false; 232 } 233 for (TypeMirror arg : typeArguments) { 234 if (hasTypeVar(arg)) { 235 return true; 236 } 237 } 238 return false; 239 } else { 240 return false; 241 } 242 } 243 244 private static boolean hasTypeVar(ModelClass type) { 245 if (type.isTypeVar()) { 246 return true; 247 } else if (type.isArray()) { 248 return hasTypeVar(type.getComponentType()); 249 } else { 250 List<ModelClass> typeArguments = type.getTypeArguments(); 251 if (typeArguments == null) { 252 return false; 253 } 254 for (ModelClass arg : typeArguments) { 255 if (hasTypeVar(arg)) { 256 return true; 257 } 258 } 259 return false; 260 } 261 } 262 263 public void addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes, 264 ExecutableElement bindingMethod, boolean takesComponent, boolean requireAll) { 265 L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod); 266 MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod, 267 attributes, takesComponent, requireAll); 268 MethodDescription methodDescription = new MethodDescription(bindingMethod, 269 attributes.length, takesComponent); 270 mStore.multiValueAdapters.put(key, methodDescription); 271 } 272 273 private static String[] stripAttributes(String[] attributes) { 274 String[] strippedAttributes = new String[attributes.length]; 275 for (int i = 0; i < attributes.length; i++) { 276 strippedAttributes[i] = stripNamespace(attributes[i]); 277 } 278 return strippedAttributes; 279 } 280 281 public void addUntaggableTypes(String[] typeNames, TypeElement declaredOn) { 282 L.d("STORE addUntaggableTypes %s %s", Arrays.toString(typeNames), declaredOn); 283 String declaredType = declaredOn.getQualifiedName().toString(); 284 for (String type : typeNames) { 285 mStore.untaggableTypes.put(type, declaredType); 286 } 287 } 288 289 private static String getQualifiedName(TypeMirror type) { 290 if (type.getKind() == TypeKind.ARRAY) { 291 return getQualifiedName(((ArrayType) type).getComponentType()) + "[]"; 292 } else { 293 return type.toString(); 294 } 295 } 296 297 public void addConversionMethod(ExecutableElement conversionMethod) { 298 L.d("STORE addConversionMethod %s", conversionMethod); 299 List<? extends VariableElement> parameters = conversionMethod.getParameters(); 300 String fromType = getQualifiedName(parameters.get(0).asType()); 301 String toType = getQualifiedName(conversionMethod.getReturnType()); 302 MethodDescription methodDescription = new MethodDescription(conversionMethod, 1, false); 303 HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType); 304 if (convertTo == null) { 305 convertTo = new HashMap<String, MethodDescription>(); 306 mStore.conversionMethods.put(fromType, convertTo); 307 } 308 convertTo.put(toType, methodDescription); 309 } 310 311 public void clear(Set<String> classes) { 312 ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>(); 313 for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) { 314 for (AccessorKey key : adapters.keySet()) { 315 MethodDescription description = adapters.get(key); 316 if (classes.contains(description.type)) { 317 removedAccessorKeys.add(key); 318 } 319 } 320 removeFromMap(adapters, removedAccessorKeys); 321 } 322 323 ArrayList<String> removedRenamed = new ArrayList<String>(); 324 for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) { 325 for (String key : renamed.keySet()) { 326 if (classes.contains(renamed.get(key).type)) { 327 removedRenamed.add(key); 328 } 329 } 330 removeFromMap(renamed, removedRenamed); 331 } 332 333 ArrayList<String> removedConversions = new ArrayList<String>(); 334 for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) { 335 for (String toType : convertTos.keySet()) { 336 MethodDescription methodDescription = convertTos.get(toType); 337 if (classes.contains(methodDescription.type)) { 338 removedConversions.add(toType); 339 } 340 } 341 removeFromMap(convertTos, removedConversions); 342 } 343 344 ArrayList<String> removedUntaggable = new ArrayList<String>(); 345 for (String typeName : mStore.untaggableTypes.keySet()) { 346 if (classes.contains(mStore.untaggableTypes.get(typeName))) { 347 removedUntaggable.add(typeName); 348 } 349 } 350 removeFromMap(mStore.untaggableTypes, removedUntaggable); 351 } 352 353 private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) { 354 for (K key : keys) { 355 map.remove(key); 356 } 357 keys.clear(); 358 } 359 360 public void write(String projectPackage, ProcessingEnvironment processingEnvironment) 361 throws IOException { 362 GenerationalClassUtil.writeIntermediateFile(processingEnvironment, 363 projectPackage, projectPackage + 364 GenerationalClassUtil.ExtensionFilter.SETTER_STORE.getExtension(), mStore); 365 } 366 367 private static String stripNamespace(String attribute) { 368 if (!attribute.startsWith("android:")) { 369 int colon = attribute.indexOf(':'); 370 if (colon >= 0) { 371 attribute = attribute.substring(colon + 1); 372 } 373 } 374 return attribute; 375 } 376 377 public List<MultiAttributeSetter> getMultiAttributeSetterCalls(String[] attributes, 378 ModelClass viewType, ModelClass[] valueType) { 379 attributes = stripAttributes(attributes); 380 final ArrayList<MultiAttributeSetter> calls = new ArrayList<MultiAttributeSetter>(); 381 if (viewType != null && viewType.isGeneric()) { 382 List<ModelClass> viewGenerics = viewType.getTypeArguments(); 383 for (int i = 0; i < valueType.length; i++) { 384 valueType[i] = eraseType(valueType[i], viewGenerics); 385 } 386 viewType = viewType.erasure(); 387 } 388 ArrayList<MultiAttributeSetter> matching = getMatchingMultiAttributeSetters(attributes, 389 viewType, valueType); 390 Collections.sort(matching, COMPARE_MULTI_ATTRIBUTE_SETTERS); 391 while (!matching.isEmpty()) { 392 MultiAttributeSetter bestMatch = matching.get(0); 393 calls.add(bestMatch); 394 removeConsumedAttributes(matching, bestMatch.attributes); 395 } 396 return calls; 397 } 398 399 // Removes all MultiAttributeSetters that require any of the values in attributes 400 private static void removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching, 401 String[] attributes) { 402 for (int i = matching.size() - 1; i >= 0; i--) { 403 final MultiAttributeSetter setter = matching.get(i); 404 boolean found = false; 405 for (String attribute : attributes) { 406 if (isInArray(attribute, setter.attributes)) { 407 found = true; 408 break; 409 } 410 } 411 if (found) { 412 matching.remove(i); 413 } 414 } 415 } 416 417 // Linear search through the String array for a specific value. 418 private static boolean isInArray(String str, String[] array) { 419 for (String value : array) { 420 if (value.equals(str)) { 421 return true; 422 } 423 } 424 return false; 425 } 426 427 private ArrayList<MultiAttributeSetter> getMatchingMultiAttributeSetters(String[] attributes, 428 ModelClass viewType, ModelClass[] valueType) { 429 final ArrayList<MultiAttributeSetter> setters = new ArrayList<MultiAttributeSetter>(); 430 for (MultiValueAdapterKey adapter : mStore.multiValueAdapters.keySet()) { 431 if (adapter.requireAll && adapter.attributes.length > attributes.length) { 432 continue; 433 } 434 ModelClass viewClass = mClassAnalyzer.findClass(adapter.viewType, null); 435 if (viewClass.isGeneric()) { 436 viewClass = viewClass.erasure(); 437 } 438 if (!viewClass.isAssignableFrom(viewType)) { 439 continue; 440 } 441 final MethodDescription method = mStore.multiValueAdapters.get(adapter); 442 final MultiAttributeSetter setter = createMultiAttributeSetter(method, attributes, 443 valueType, adapter); 444 if (setter != null) { 445 setters.add(setter); 446 } 447 } 448 return setters; 449 } 450 451 private MultiAttributeSetter createMultiAttributeSetter(MethodDescription method, 452 String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter) { 453 int matchingAttributes = 0; 454 String[] casts = new String[adapter.attributes.length]; 455 MethodDescription[] conversions = new MethodDescription[adapter.attributes.length]; 456 boolean[] supplied = new boolean[adapter.attributes.length]; 457 458 for (int i = 0; i < allAttributes.length; i++) { 459 Integer index = adapter.attributeIndices.get(allAttributes[i]); 460 if (index != null) { 461 supplied[index] = true; 462 matchingAttributes++; 463 final String parameterTypeStr = adapter.parameterTypes[index]; 464 final ModelClass parameterType = eraseType( 465 mClassAnalyzer.findClass(parameterTypeStr, null)); 466 final ModelClass attributeType = attributeValues[i]; 467 if (!parameterType.isAssignableFrom(attributeType)) { 468 if (ModelMethod.isBoxingConversion(parameterType, attributeType)) { 469 // automatic boxing is ok 470 continue; 471 } else if (ModelMethod.isImplicitConversion(attributeType, parameterType)) { 472 // implicit conversion is ok 473 continue; 474 } 475 // Look for a converter 476 conversions[index] = getConversionMethod(attributeType, parameterType, null); 477 if (conversions[index] == null) { 478 if (attributeType.isObject()) { 479 // Cast is allowed also 480 casts[index] = parameterTypeStr; 481 } else { 482 // Parameter type mismatch 483 return null; 484 } 485 } 486 } 487 } 488 } 489 490 if ((adapter.requireAll && matchingAttributes != adapter.attributes.length) || 491 matchingAttributes == 0) { 492 return null; 493 } else { 494 return new MultiAttributeSetter(adapter, supplied, method, conversions, casts); 495 } 496 } 497 498 public SetterCall getSetterCall(String attribute, ModelClass viewType, 499 ModelClass valueType, Map<String, String> imports) { 500 attribute = stripNamespace(attribute); 501 SetterCall setterCall = null; 502 MethodDescription conversionMethod = null; 503 if (viewType != null) { 504 viewType = viewType.erasure(); 505 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 506 ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports); 507 ModelClass bestViewType = null; 508 ModelClass bestValueType = null; 509 if (bestSetterMethod != null) { 510 bestViewType = bestSetterMethod.getDeclaringClass(); 511 bestValueType = bestSetterMethod.getParameterTypes()[0]; 512 setterCall = new ModelMethodSetter(bestSetterMethod); 513 } 514 515 if (adapters != null) { 516 for (AccessorKey key : adapters.keySet()) { 517 try { 518 ModelClass adapterViewType = mClassAnalyzer 519 .findClass(key.viewType, imports).erasure(); 520 if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) { 521 try { 522 L.d("setter parameter type is %s", key.valueType); 523 final ModelClass adapterValueType = eraseType(mClassAnalyzer 524 .findClass(key.valueType, imports)); 525 L.d("setter %s takes type %s, compared to %s", 526 adapters.get(key).method, adapterValueType.toJavaCode(), 527 valueType.toJavaCode()); 528 boolean isBetterView = bestViewType == null || 529 bestValueType.isAssignableFrom(adapterValueType); 530 if (isBetterParameter(valueType, adapterValueType, bestValueType, 531 isBetterView, imports)) { 532 bestViewType = adapterViewType; 533 bestValueType = adapterValueType; 534 MethodDescription adapter = adapters.get(key); 535 setterCall = new AdapterSetter(adapter, adapterValueType); 536 } 537 538 } catch (Exception e) { 539 L.e(e, "Unknown class: %s", key.valueType); 540 } 541 } 542 } catch (Exception e) { 543 L.e(e, "Unknown class: %s", key.viewType); 544 } 545 } 546 } 547 548 conversionMethod = getConversionMethod(valueType, bestValueType, imports); 549 if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) { 550 setterCall.setCast(bestValueType); 551 } 552 } 553 if (setterCall == null) { 554 if (viewType != null && !viewType.isViewDataBinding()) { 555 return null; // no setter found!! 556 } 557 setterCall = new DummySetter(getDefaultSetter(attribute)); 558 } 559 setterCall.setConverter(conversionMethod); 560 return setterCall; 561 } 562 563 public boolean isUntaggable(String viewType) { 564 return mStore.untaggableTypes.containsKey(viewType); 565 } 566 567 private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType, 568 String attribute, Map<String, String> imports) { 569 if (viewType.isGeneric()) { 570 argumentType = eraseType(argumentType, viewType.getTypeArguments()); 571 viewType = viewType.erasure(); 572 } 573 List<String> setterCandidates = new ArrayList<String>(); 574 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 575 if (renamed != null) { 576 for (String className : renamed.keySet()) { 577 try { 578 ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports); 579 if (renamedViewType.erasure().isAssignableFrom(viewType)) { 580 setterCandidates.add(renamed.get(className).method); 581 break; 582 } 583 } catch (Exception e) { 584 //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className); 585 } 586 } 587 } 588 setterCandidates.add(getDefaultSetter(attribute)); 589 setterCandidates.add(trimAttributeNamespace(attribute)); 590 591 ModelMethod bestMethod = null; 592 ModelClass bestParameterType = null; 593 List<ModelClass> args = new ArrayList<ModelClass>(); 594 args.add(argumentType); 595 for (String name : setterCandidates) { 596 ModelMethod[] methods = viewType.getMethods(name, 1); 597 598 for (ModelMethod method : methods) { 599 ModelClass[] parameterTypes = method.getParameterTypes(); 600 ModelClass param = parameterTypes[0]; 601 if (method.isVoid() && 602 isBetterParameter(argumentType, param, bestParameterType, true, imports)) { 603 bestParameterType = param; 604 bestMethod = method; 605 } 606 } 607 } 608 return bestMethod; 609 } 610 611 private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) { 612 List<ModelClass> typeArguments = type.getTypeArguments(); 613 if (typeArguments == null || typeParameters == null) { 614 return type; 615 } 616 for (ModelClass arg : typeArguments) { 617 if (typeParameters.contains(arg)) { 618 return type.erasure(); 619 } 620 } 621 return type; 622 } 623 624 private static String trimAttributeNamespace(String attribute) { 625 final int colonIndex = attribute.indexOf(':'); 626 return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1); 627 } 628 629 private static String getDefaultSetter(String attribute) { 630 return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute)); 631 } 632 633 private boolean isBetterParameter(ModelClass argument, ModelClass parameter, 634 ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) { 635 // Right view type. Check the value 636 if (!isBetterViewTypeMatch && oldParameter.equals(argument)) { 637 return false; 638 } else if (argument.equals(parameter)) { 639 // Exact match 640 return true; 641 } else if (!isBetterViewTypeMatch && 642 ModelMethod.isBoxingConversion(oldParameter, argument)) { 643 return false; 644 } else if (ModelMethod.isBoxingConversion(parameter, argument)) { 645 // Boxing/unboxing is second best 646 return true; 647 } else { 648 int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter); 649 if (ModelMethod.isImplicitConversion(argument, parameter)) { 650 // Better implicit conversion 651 int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter); 652 return oldConversionLevel < 0 || conversionLevel < oldConversionLevel; 653 } else if (oldConversionLevel >= 0) { 654 return false; 655 } else if (parameter.isAssignableFrom(argument)) { 656 // Right type, see if it is better than the current best match. 657 if (oldParameter == null) { 658 return true; 659 } else { 660 return oldParameter.isAssignableFrom(parameter); 661 } 662 } else { 663 MethodDescription conversionMethod = getConversionMethod(argument, parameter, 664 imports); 665 if (conversionMethod != null) { 666 return true; 667 } 668 if (getConversionMethod(argument, oldParameter, imports) != null) { 669 return false; 670 } 671 return argument.isObject() && !parameter.isPrimitive(); 672 } 673 } 674 } 675 676 private MethodDescription getConversionMethod(ModelClass from, ModelClass to, 677 Map<String, String> imports) { 678 if (from != null && to != null) { 679 for (String fromClassName : mStore.conversionMethods.keySet()) { 680 try { 681 ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports); 682 if (canUseForConversion(from, convertFrom)) { 683 HashMap<String, MethodDescription> conversion = 684 mStore.conversionMethods.get(fromClassName); 685 for (String toClassName : conversion.keySet()) { 686 try { 687 ModelClass convertTo = mClassAnalyzer.findClass(toClassName, 688 imports); 689 if (canUseForConversion(convertTo, to)) { 690 return conversion.get(toClassName); 691 } 692 } catch (Exception e) { 693 L.d(e, "Unknown class: %s", toClassName); 694 } 695 } 696 } 697 } catch (Exception e) { 698 L.d(e, "Unknown class: %s", fromClassName); 699 } 700 } 701 } 702 return null; 703 } 704 705 private boolean canUseForConversion(ModelClass from, ModelClass to) { 706 return from.equals(to) || ModelMethod.isBoxingConversion(from, to) || 707 to.isAssignableFrom(from); 708 } 709 710 private static void merge(IntermediateV1 store, Intermediate dumpStore) { 711 IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade(); 712 merge(store.adapterMethods, intermediateV1.adapterMethods); 713 merge(store.renamedMethods, intermediateV1.renamedMethods); 714 merge(store.conversionMethods, intermediateV1.conversionMethods); 715 store.multiValueAdapters.putAll(intermediateV1.multiValueAdapters); 716 store.untaggableTypes.putAll(intermediateV1.untaggableTypes); 717 } 718 719 private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first, 720 HashMap<K, HashMap<V, MethodDescription>> second) { 721 for (K key : second.keySet()) { 722 HashMap<V, MethodDescription> firstVals = first.get(key); 723 HashMap<V, MethodDescription> secondVals = second.get(key); 724 if (firstVals == null) { 725 first.put(key, secondVals); 726 } else { 727 for (V key2 : secondVals.keySet()) { 728 if (!firstVals.containsKey(key2)) { 729 firstVals.put(key2, secondVals.get(key2)); 730 } 731 } 732 } 733 } 734 } 735 736 private static String createAdapterCall(MethodDescription adapter, String bindingAdapterCall, 737 String componentExpression, String viewExpression, String... args) { 738 StringBuilder sb = new StringBuilder(); 739 740 if (adapter.isStatic) { 741 sb.append(adapter.type); 742 } else { 743 sb.append(componentExpression).append('.').append(bindingAdapterCall); 744 } 745 sb.append('.').append(adapter.method).append('('); 746 if (adapter.componentClass != null) { 747 if (!"DataBindingComponent".equals(adapter.componentClass)) { 748 sb.append('(').append(adapter.componentClass).append(") "); 749 } 750 sb.append(componentExpression).append(", "); 751 } 752 sb.append(viewExpression); 753 for (String arg: args) { 754 sb.append(", ").append(arg); 755 } 756 sb.append(')'); 757 return sb.toString(); 758 } 759 760 private static class MultiValueAdapterKey implements Serializable { 761 private static final long serialVersionUID = 1; 762 763 public final String viewType; 764 765 public final String[] attributes; 766 767 public final String[] parameterTypes; 768 769 public final boolean requireAll; 770 771 public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>(); 772 773 public MultiValueAdapterKey(ProcessingEnvironment processingEnv, 774 ExecutableElement method, String[] attributes, boolean takesComponent, 775 boolean requireAll) { 776 this.attributes = stripAttributes(attributes); 777 this.requireAll = requireAll; 778 List<? extends VariableElement> parameters = method.getParameters(); 779 final int argStart = 1 + (takesComponent ? 1 : 0); 780 this.viewType = getQualifiedName(eraseType(processingEnv, 781 parameters.get(argStart - 1).asType())); 782 this.parameterTypes = new String[parameters.size() - argStart]; 783 for (int i = 0; i < attributes.length; i++) { 784 TypeMirror typeMirror = eraseType(processingEnv, 785 parameters.get(i + argStart).asType()); 786 this.parameterTypes[i] = getQualifiedName(typeMirror); 787 attributeIndices.put(this.attributes[i], i); 788 } 789 } 790 791 @Override 792 public boolean equals(Object obj) { 793 if (!(obj instanceof MultiValueAdapterKey)) { 794 return false; 795 } 796 final MultiValueAdapterKey that = (MultiValueAdapterKey) obj; 797 if (!this.viewType.equals(that.viewType) || 798 this.attributes.length != that.attributes.length || 799 !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) { 800 return false; 801 } 802 803 for (int i = 0; i < this.attributes.length; i++) { 804 final int thatIndex = that.attributeIndices.get(this.attributes[i]); 805 final String thisParameter = parameterTypes[i]; 806 final String thatParameter = that.parameterTypes[thatIndex]; 807 if (!thisParameter.equals(thatParameter)) { 808 return false; 809 } 810 } 811 return true; 812 } 813 814 @Override 815 public int hashCode() { 816 return mergedHashCode(viewType, attributeIndices.keySet()); 817 } 818 } 819 820 private static int mergedHashCode(Object... objects) { 821 return Arrays.hashCode(objects); 822 } 823 824 private static class MethodDescription implements Serializable { 825 826 private static final long serialVersionUID = 1; 827 828 public final String type; 829 830 public final String method; 831 832 public final boolean requiresOldValue; 833 834 public final boolean isStatic; 835 836 public final String componentClass; 837 838 public MethodDescription(String type, String method) { 839 this.type = type; 840 this.method = method; 841 this.requiresOldValue = false; 842 this.isStatic = true; 843 this.componentClass = null; 844 L.d("BINARY created method desc 1 %s %s", type, method ); 845 } 846 847 public MethodDescription(ExecutableElement method, int numAttributes, 848 boolean takesComponent) { 849 TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); 850 this.type = enclosingClass.getQualifiedName().toString(); 851 this.method = method.getSimpleName().toString(); 852 final int argStart = 1 + (takesComponent ? 1 : 0); 853 this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2; 854 this.isStatic = method.getModifiers().contains(Modifier.STATIC); 855 this.componentClass = takesComponent 856 ? getQualifiedName(method.getParameters().get(0).asType()) 857 : null; 858 859 L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); 860 } 861 862 @Override 863 public boolean equals(Object obj) { 864 if (obj instanceof MethodDescription) { 865 MethodDescription that = (MethodDescription) obj; 866 return that.type.equals(this.type) && that.method.equals(this.method); 867 } else { 868 return false; 869 } 870 } 871 872 @Override 873 public int hashCode() { 874 return mergedHashCode(type, method); 875 } 876 877 @Override 878 public String toString() { 879 return type + "." + method + "()"; 880 } 881 } 882 883 private static class AccessorKey implements Serializable { 884 885 private static final long serialVersionUID = 1; 886 887 public final String viewType; 888 889 public final String valueType; 890 891 public AccessorKey(String viewType, String valueType) { 892 this.viewType = viewType; 893 this.valueType = valueType; 894 } 895 896 @Override 897 public int hashCode() { 898 return mergedHashCode(viewType, valueType); 899 } 900 901 @Override 902 public boolean equals(Object obj) { 903 if (obj instanceof AccessorKey) { 904 AccessorKey that = (AccessorKey) obj; 905 return viewType.equals(that.valueType) && valueType.equals(that.valueType); 906 } else { 907 return false; 908 } 909 } 910 911 @Override 912 public String toString() { 913 return "AK(" + viewType + ", " + valueType + ")"; 914 } 915 } 916 917 private interface Intermediate extends Serializable { 918 Intermediate upgrade(); 919 } 920 921 private static class IntermediateV1 implements Serializable, Intermediate { 922 private static final long serialVersionUID = 1; 923 public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = 924 new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); 925 public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = 926 new HashMap<String, HashMap<String, MethodDescription>>(); 927 public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = 928 new HashMap<String, HashMap<String, MethodDescription>>(); 929 public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); 930 public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters = 931 new HashMap<MultiValueAdapterKey, MethodDescription>(); 932 933 public IntermediateV1() { 934 } 935 936 @Override 937 public Intermediate upgrade() { 938 return this; 939 } 940 } 941 942 public static class DummySetter extends SetterCall { 943 private String mMethodName; 944 945 public DummySetter(String methodName) { 946 mMethodName = methodName; 947 } 948 949 @Override 950 public String toJavaInternal(String componentExpression, String viewExpression, 951 String valueExpression) { 952 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 953 } 954 955 @Override 956 public String toJavaInternal(String componentExpression, String viewExpression, 957 String oldValue, String valueExpression) { 958 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 959 } 960 961 @Override 962 public int getMinApi() { 963 return 1; 964 } 965 966 @Override 967 public boolean requiresOldValue() { 968 return false; 969 } 970 971 @Override 972 public ModelClass[] getParameterTypes() { 973 return new ModelClass[] { 974 ModelAnalyzer.getInstance().findClass(Object.class) 975 }; 976 } 977 978 @Override 979 public String getBindingAdapterInstanceClass() { 980 return null; 981 } 982 983 @Override 984 public void setBindingAdapterCall(String method) { 985 } 986 } 987 988 public static class AdapterSetter extends SetterCall { 989 final MethodDescription mAdapter; 990 final ModelClass mParameterType; 991 String mBindingAdapterCall; 992 993 public AdapterSetter(MethodDescription adapter, ModelClass parameterType) { 994 mAdapter = adapter; 995 mParameterType = parameterType; 996 } 997 998 @Override 999 public String toJavaInternal(String componentExpression, String viewExpression, 1000 String valueExpression) { 1001 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1002 viewExpression, mCastString + valueExpression); 1003 } 1004 1005 @Override 1006 protected String toJavaInternal(String componentExpression, String viewExpression, 1007 String oldValue, String valueExpression) { 1008 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1009 viewExpression, mCastString + oldValue, mCastString + valueExpression); 1010 } 1011 1012 @Override 1013 public int getMinApi() { 1014 return 1; 1015 } 1016 1017 @Override 1018 public boolean requiresOldValue() { 1019 return mAdapter.requiresOldValue; 1020 } 1021 1022 @Override 1023 public ModelClass[] getParameterTypes() { 1024 return new ModelClass[] { mParameterType }; 1025 } 1026 1027 @Override 1028 public String getBindingAdapterInstanceClass() { 1029 return mAdapter.isStatic ? null : mAdapter.type; 1030 } 1031 1032 @Override 1033 public void setBindingAdapterCall(String method) { 1034 mBindingAdapterCall = method; 1035 } 1036 } 1037 1038 public static class ModelMethodSetter extends SetterCall { 1039 final ModelMethod mModelMethod; 1040 1041 public ModelMethodSetter(ModelMethod modelMethod) { 1042 mModelMethod = modelMethod; 1043 } 1044 1045 @Override 1046 public String toJavaInternal(String componentExpression, String viewExpression, 1047 String valueExpression) { 1048 return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + 1049 valueExpression + ")"; 1050 } 1051 1052 @Override 1053 protected String toJavaInternal(String componentExpression, String viewExpression, 1054 String oldValue, String valueExpression) { 1055 return viewExpression + "." + mModelMethod.getName() + "(" + 1056 mCastString + oldValue + ", " + mCastString + valueExpression + ")"; 1057 } 1058 1059 @Override 1060 public int getMinApi() { 1061 return mModelMethod.getMinApi(); 1062 } 1063 1064 @Override 1065 public boolean requiresOldValue() { 1066 return mModelMethod.getParameterTypes().length == 3; 1067 } 1068 1069 @Override 1070 public ModelClass[] getParameterTypes() { 1071 return new ModelClass[] { mModelMethod.getParameterTypes()[0] }; 1072 } 1073 1074 @Override 1075 public String getBindingAdapterInstanceClass() { 1076 return null; 1077 } 1078 1079 @Override 1080 public void setBindingAdapterCall(String method) { 1081 } 1082 } 1083 1084 public interface BindingSetterCall { 1085 String toJava(String componentExpression, String viewExpression, 1086 String... valueExpressions); 1087 1088 int getMinApi(); 1089 1090 boolean requiresOldValue(); 1091 1092 ModelClass[] getParameterTypes(); 1093 1094 String getBindingAdapterInstanceClass(); 1095 1096 void setBindingAdapterCall(String method); 1097 } 1098 1099 public static abstract class SetterCall implements BindingSetterCall { 1100 private MethodDescription mConverter; 1101 protected String mCastString = ""; 1102 1103 public SetterCall() { 1104 } 1105 1106 public void setConverter(MethodDescription converter) { 1107 mConverter = converter; 1108 } 1109 1110 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1111 String converted); 1112 1113 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1114 String oldValue, String converted); 1115 1116 @Override 1117 public final String toJava(String componentExpression, String viewExpression, 1118 String... valueExpression) { 1119 Preconditions.check(valueExpression.length == 2, "value expressions size must be 2"); 1120 if (requiresOldValue()) { 1121 return toJavaInternal(componentExpression, viewExpression, 1122 convertValue(valueExpression[0]), convertValue(valueExpression[1])); 1123 } else { 1124 return toJavaInternal(componentExpression, viewExpression, 1125 convertValue(valueExpression[1])); 1126 } 1127 } 1128 1129 protected String convertValue(String valueExpression) { 1130 return mConverter == null ? valueExpression : 1131 mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; 1132 } 1133 1134 abstract public int getMinApi(); 1135 1136 public void setCast(ModelClass castTo) { 1137 mCastString = "(" + castTo.toJavaCode() + ") "; 1138 } 1139 } 1140 1141 public static class MultiAttributeSetter implements BindingSetterCall { 1142 public final String[] attributes; 1143 private final MethodDescription mAdapter; 1144 private final MethodDescription[] mConverters; 1145 private final String[] mCasts; 1146 private final MultiValueAdapterKey mKey; 1147 private final boolean[] mSupplied; 1148 String mBindingAdapterCall; 1149 1150 public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied, 1151 MethodDescription adapter, MethodDescription[] converters, String[] casts) { 1152 Preconditions.check(converters != null && 1153 converters.length == key.attributes.length && 1154 casts != null && casts.length == key.attributes.length && 1155 supplied.length == key.attributes.length, 1156 "invalid arguments to create multi attr setter"); 1157 this.mAdapter = adapter; 1158 this.mConverters = converters; 1159 this.mCasts = casts; 1160 this.mKey = key; 1161 this.mSupplied = supplied; 1162 if (key.requireAll) { 1163 this.attributes = key.attributes; 1164 } else { 1165 int numSupplied = 0; 1166 for (int i = 0; i < mKey.attributes.length; i++) { 1167 if (supplied[i]) { 1168 numSupplied++; 1169 } 1170 } 1171 if (numSupplied == key.attributes.length) { 1172 this.attributes = key.attributes; 1173 } else { 1174 this.attributes = new String[numSupplied]; 1175 int attrIndex = 0; 1176 for (int i = 0; i < key.attributes.length; i++) { 1177 if (supplied[i]) { 1178 attributes[attrIndex++] = key.attributes[i]; 1179 } 1180 } 1181 } 1182 } 1183 } 1184 1185 @Override 1186 public final String toJava(String componentExpression, String viewExpression, 1187 String[] valueExpressions) { 1188 Preconditions.check(valueExpressions.length == attributes.length * 2, 1189 "MultiAttributeSetter needs %s items, received %s", 1190 Arrays.toString(attributes), Arrays.toString(valueExpressions)); 1191 final int numAttrs = mKey.attributes.length; 1192 String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)]; 1193 1194 final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs; 1195 int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length; 1196 final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1197 StringBuilder argBuilder = new StringBuilder(); 1198 final int endIndex = numAttrs * 2; 1199 for (int i = startIndex; i < endIndex; i++) { 1200 argBuilder.setLength(0); 1201 if (!mSupplied[i % numAttrs]) { 1202 final String paramType = mKey.parameterTypes[i % numAttrs]; 1203 final String defaultValue = modelAnalyzer.getDefaultValue(paramType); 1204 argBuilder.append('(') 1205 .append(paramType) 1206 .append(')') 1207 .append(defaultValue); 1208 } else { 1209 if (mConverters[i % numAttrs] != null) { 1210 final MethodDescription converter = mConverters[i % numAttrs]; 1211 argBuilder.append(converter.type) 1212 .append('.') 1213 .append(converter.method) 1214 .append('(') 1215 .append(valueExpressions[attrIndex]) 1216 .append(')'); 1217 } else { 1218 if (mCasts[i % numAttrs] != null) { 1219 argBuilder.append('(') 1220 .append(mCasts[i % numAttrs]) 1221 .append(')'); 1222 } 1223 argBuilder.append(valueExpressions[attrIndex]); 1224 } 1225 attrIndex++; 1226 } 1227 args[i - startIndex] = argBuilder.toString(); 1228 } 1229 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1230 viewExpression, args); 1231 } 1232 1233 @Override 1234 public int getMinApi() { 1235 return 1; 1236 } 1237 1238 @Override 1239 public boolean requiresOldValue() { 1240 return mAdapter.requiresOldValue; 1241 } 1242 1243 @Override 1244 public ModelClass[] getParameterTypes() { 1245 ModelClass[] parameters = new ModelClass[attributes.length]; 1246 String[] paramTypeStrings = mKey.parameterTypes; 1247 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1248 int attrIndex = 0; 1249 for (int i = 0; i < mKey.attributes.length; i++) { 1250 if (mSupplied[i]) { 1251 parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null); 1252 } 1253 } 1254 return parameters; 1255 } 1256 1257 @Override 1258 public String getBindingAdapterInstanceClass() { 1259 return mAdapter.isStatic ? null : mAdapter.type; 1260 } 1261 1262 @Override 1263 public void setBindingAdapterCall(String method) { 1264 mBindingAdapterCall = method; 1265 } 1266 1267 @Override 1268 public String toString() { 1269 return "MultiAttributeSetter{" + 1270 "attributes=" + Arrays.toString(attributes) + 1271 ", mAdapter=" + mAdapter + 1272 ", mConverters=" + Arrays.toString(mConverters) + 1273 ", mCasts=" + Arrays.toString(mCasts) + 1274 ", mKey=" + mKey + 1275 '}'; 1276 } 1277 } 1278} 1279