SetterStore.java revision e4cd38824a6627b9fef229c549c636e35ad63b5f
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) { 265 L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod); 266 MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod, 267 attributes, takesComponent); 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.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 457 for (int i = 0; i < allAttributes.length; i++) { 458 Integer index = adapter.attributeIndices.get(allAttributes[i]); 459 if (index != null) { 460 matchingAttributes++; 461 final String parameterTypeStr = adapter.parameterTypes[index]; 462 final ModelClass parameterType = eraseType( 463 mClassAnalyzer.findClass(parameterTypeStr, null)); 464 final ModelClass attributeType = attributeValues[i]; 465 if (!parameterType.isAssignableFrom(attributeType)) { 466 if (ModelMethod.isBoxingConversion(parameterType, attributeType)) { 467 // automatic boxing is ok 468 continue; 469 } else if (ModelMethod.isImplicitConversion(attributeType, parameterType)) { 470 // implicit conversion is ok 471 continue; 472 } 473 // Look for a converter 474 conversions[index] = getConversionMethod(attributeType, parameterType, null); 475 if (conversions[index] == null) { 476 if (attributeType.isObject()) { 477 // Cast is allowed also 478 casts[index] = parameterTypeStr; 479 } else { 480 // Parameter type mismatch 481 return null; 482 } 483 } 484 } 485 } 486 } 487 488 if (matchingAttributes != adapter.attributes.length) { 489 return null; 490 } else { 491 return new MultiAttributeSetter(adapter, adapter.attributes, method, conversions, 492 casts); 493 } 494 } 495 496 public SetterCall getSetterCall(String attribute, ModelClass viewType, 497 ModelClass valueType, Map<String, String> imports) { 498 attribute = stripNamespace(attribute); 499 SetterCall setterCall = null; 500 MethodDescription conversionMethod = null; 501 if (viewType != null) { 502 viewType = viewType.erasure(); 503 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 504 ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports); 505 ModelClass bestViewType = null; 506 ModelClass bestValueType = null; 507 if (bestSetterMethod != null) { 508 bestViewType = bestSetterMethod.getDeclaringClass(); 509 bestValueType = bestSetterMethod.getParameterTypes()[0]; 510 setterCall = new ModelMethodSetter(bestSetterMethod); 511 } 512 513 if (adapters != null) { 514 for (AccessorKey key : adapters.keySet()) { 515 try { 516 ModelClass adapterViewType = mClassAnalyzer 517 .findClass(key.viewType, imports).erasure(); 518 if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) { 519 try { 520 L.d("setter parameter type is %s", key.valueType); 521 final ModelClass adapterValueType = eraseType(mClassAnalyzer 522 .findClass(key.valueType, imports)); 523 L.d("setter %s takes type %s, compared to %s", 524 adapters.get(key).method, adapterValueType.toJavaCode(), 525 valueType.toJavaCode()); 526 boolean isBetterView = bestViewType == null || 527 bestValueType.isAssignableFrom(adapterValueType); 528 if (isBetterParameter(valueType, adapterValueType, bestValueType, 529 isBetterView, imports)) { 530 bestViewType = adapterViewType; 531 bestValueType = adapterValueType; 532 MethodDescription adapter = adapters.get(key); 533 setterCall = new AdapterSetter(adapter, adapterValueType); 534 } 535 536 } catch (Exception e) { 537 L.e(e, "Unknown class: %s", key.valueType); 538 } 539 } 540 } catch (Exception e) { 541 L.e(e, "Unknown class: %s", key.viewType); 542 } 543 } 544 } 545 546 conversionMethod = getConversionMethod(valueType, bestValueType, imports); 547 if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) { 548 setterCall.setCast(bestValueType); 549 } 550 } 551 if (setterCall == null) { 552 if (viewType != null && !viewType.isViewDataBinding()) { 553 return null; // no setter found!! 554 } 555 setterCall = new DummySetter(getDefaultSetter(attribute)); 556 } 557 setterCall.setConverter(conversionMethod); 558 return setterCall; 559 } 560 561 public boolean isUntaggable(String viewType) { 562 return mStore.untaggableTypes.containsKey(viewType); 563 } 564 565 private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType, 566 String attribute, Map<String, String> imports) { 567 if (viewType.isGeneric()) { 568 argumentType = eraseType(argumentType, viewType.getTypeArguments()); 569 viewType = viewType.erasure(); 570 } 571 List<String> setterCandidates = new ArrayList<String>(); 572 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 573 if (renamed != null) { 574 for (String className : renamed.keySet()) { 575 try { 576 ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports); 577 if (renamedViewType.erasure().isAssignableFrom(viewType)) { 578 setterCandidates.add(renamed.get(className).method); 579 break; 580 } 581 } catch (Exception e) { 582 //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className); 583 } 584 } 585 } 586 setterCandidates.add(getDefaultSetter(attribute)); 587 setterCandidates.add(trimAttributeNamespace(attribute)); 588 589 ModelMethod bestMethod = null; 590 ModelClass bestParameterType = null; 591 List<ModelClass> args = new ArrayList<ModelClass>(); 592 args.add(argumentType); 593 for (String name : setterCandidates) { 594 ModelMethod[] methods = viewType.getMethods(name, 1); 595 596 for (ModelMethod method : methods) { 597 ModelClass[] parameterTypes = method.getParameterTypes(); 598 ModelClass param = parameterTypes[0]; 599 if (method.isVoid() && 600 isBetterParameter(argumentType, param, bestParameterType, true, imports)) { 601 bestParameterType = param; 602 bestMethod = method; 603 } 604 } 605 } 606 return bestMethod; 607 } 608 609 private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) { 610 List<ModelClass> typeArguments = type.getTypeArguments(); 611 if (typeArguments == null || typeParameters == null) { 612 return type; 613 } 614 for (ModelClass arg : typeArguments) { 615 if (typeParameters.contains(arg)) { 616 return type.erasure(); 617 } 618 } 619 return type; 620 } 621 622 private static String trimAttributeNamespace(String attribute) { 623 final int colonIndex = attribute.indexOf(':'); 624 return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1); 625 } 626 627 private static String getDefaultSetter(String attribute) { 628 return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute)); 629 } 630 631 private boolean isBetterParameter(ModelClass argument, ModelClass parameter, 632 ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) { 633 // Right view type. Check the value 634 if (!isBetterViewTypeMatch && oldParameter.equals(argument)) { 635 return false; 636 } else if (argument.equals(parameter)) { 637 // Exact match 638 return true; 639 } else if (!isBetterViewTypeMatch && 640 ModelMethod.isBoxingConversion(oldParameter, argument)) { 641 return false; 642 } else if (ModelMethod.isBoxingConversion(parameter, argument)) { 643 // Boxing/unboxing is second best 644 return true; 645 } else { 646 int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter); 647 if (ModelMethod.isImplicitConversion(argument, parameter)) { 648 // Better implicit conversion 649 int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter); 650 return oldConversionLevel < 0 || conversionLevel < oldConversionLevel; 651 } else if (oldConversionLevel >= 0) { 652 return false; 653 } else if (parameter.isAssignableFrom(argument)) { 654 // Right type, see if it is better than the current best match. 655 if (oldParameter == null) { 656 return true; 657 } else { 658 return oldParameter.isAssignableFrom(parameter); 659 } 660 } else { 661 MethodDescription conversionMethod = getConversionMethod(argument, parameter, 662 imports); 663 if (conversionMethod != null) { 664 return true; 665 } 666 if (getConversionMethod(argument, oldParameter, imports) != null) { 667 return false; 668 } 669 return argument.isObject() && !parameter.isPrimitive(); 670 } 671 } 672 } 673 674 private MethodDescription getConversionMethod(ModelClass from, ModelClass to, 675 Map<String, String> imports) { 676 if (from != null && to != null) { 677 for (String fromClassName : mStore.conversionMethods.keySet()) { 678 try { 679 ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports); 680 if (canUseForConversion(from, convertFrom)) { 681 HashMap<String, MethodDescription> conversion = 682 mStore.conversionMethods.get(fromClassName); 683 for (String toClassName : conversion.keySet()) { 684 try { 685 ModelClass convertTo = mClassAnalyzer.findClass(toClassName, 686 imports); 687 if (canUseForConversion(convertTo, to)) { 688 return conversion.get(toClassName); 689 } 690 } catch (Exception e) { 691 L.d(e, "Unknown class: %s", toClassName); 692 } 693 } 694 } 695 } catch (Exception e) { 696 L.d(e, "Unknown class: %s", fromClassName); 697 } 698 } 699 } 700 return null; 701 } 702 703 private boolean canUseForConversion(ModelClass from, ModelClass to) { 704 return from.equals(to) || ModelMethod.isBoxingConversion(from, to) || 705 to.isAssignableFrom(from); 706 } 707 708 private static void merge(IntermediateV1 store, Intermediate dumpStore) { 709 IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade(); 710 merge(store.adapterMethods, intermediateV1.adapterMethods); 711 merge(store.renamedMethods, intermediateV1.renamedMethods); 712 merge(store.conversionMethods, intermediateV1.conversionMethods); 713 store.multiValueAdapters.putAll(intermediateV1.multiValueAdapters); 714 store.untaggableTypes.putAll(intermediateV1.untaggableTypes); 715 } 716 717 private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first, 718 HashMap<K, HashMap<V, MethodDescription>> second) { 719 for (K key : second.keySet()) { 720 HashMap<V, MethodDescription> firstVals = first.get(key); 721 HashMap<V, MethodDescription> secondVals = second.get(key); 722 if (firstVals == null) { 723 first.put(key, secondVals); 724 } else { 725 for (V key2 : secondVals.keySet()) { 726 if (!firstVals.containsKey(key2)) { 727 firstVals.put(key2, secondVals.get(key2)); 728 } 729 } 730 } 731 } 732 } 733 734 private static String createAdapterCall(MethodDescription adapter, String bindingAdapterCall, 735 String componentExpression, String viewExpression, String... args) { 736 StringBuilder sb = new StringBuilder(); 737 738 if (adapter.isStatic) { 739 sb.append(adapter.type); 740 } else { 741 sb.append(componentExpression).append('.').append(bindingAdapterCall); 742 } 743 sb.append('.').append(adapter.method).append('('); 744 if (adapter.componentClass != null) { 745 if (!"DataBindingComponent".equals(adapter.componentClass)) { 746 sb.append('(').append(adapter.componentClass).append(") "); 747 } 748 sb.append(componentExpression).append(", "); 749 } 750 sb.append(viewExpression); 751 for (String arg: args) { 752 sb.append(", ").append(arg); 753 } 754 sb.append(')'); 755 return sb.toString(); 756 } 757 758 private static class MultiValueAdapterKey implements Serializable { 759 private static final long serialVersionUID = 1; 760 761 public final String viewType; 762 763 public final String[] attributes; 764 765 public final String[] parameterTypes; 766 767 public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>(); 768 769 public MultiValueAdapterKey(ProcessingEnvironment processingEnv, 770 ExecutableElement method, String[] attributes, boolean takesComponent) { 771 this.attributes = stripAttributes(attributes); 772 List<? extends VariableElement> parameters = method.getParameters(); 773 final int argStart = 1 + (takesComponent ? 1 : 0); 774 this.viewType = getQualifiedName(eraseType(processingEnv, 775 parameters.get(argStart - 1).asType())); 776 this.parameterTypes = new String[parameters.size() - argStart]; 777 for (int i = 0; i < attributes.length; i++) { 778 TypeMirror typeMirror = eraseType(processingEnv, 779 parameters.get(i + argStart).asType()); 780 this.parameterTypes[i] = getQualifiedName(typeMirror); 781 attributeIndices.put(this.attributes[i], i); 782 } 783 } 784 785 @Override 786 public boolean equals(Object obj) { 787 if (!(obj instanceof MultiValueAdapterKey)) { 788 return false; 789 } 790 final MultiValueAdapterKey that = (MultiValueAdapterKey) obj; 791 if (!this.viewType.equals(that.viewType) || 792 this.attributes.length != that.attributes.length || 793 !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) { 794 return false; 795 } 796 797 for (int i = 0; i < this.attributes.length; i++) { 798 final int thatIndex = that.attributeIndices.get(this.attributes[i]); 799 final String thisParameter = parameterTypes[i]; 800 final String thatParameter = that.parameterTypes[thatIndex]; 801 if (!thisParameter.equals(thatParameter)) { 802 return false; 803 } 804 } 805 return true; 806 } 807 808 @Override 809 public int hashCode() { 810 return mergedHashCode(viewType, attributeIndices.keySet()); 811 } 812 } 813 814 private static int mergedHashCode(Object... objects) { 815 return Arrays.hashCode(objects); 816 } 817 818 private static class MethodDescription implements Serializable { 819 820 private static final long serialVersionUID = 1; 821 822 public final String type; 823 824 public final String method; 825 826 public final boolean requiresOldValue; 827 828 public final boolean isStatic; 829 830 public final String componentClass; 831 832 public MethodDescription(String type, String method) { 833 this.type = type; 834 this.method = method; 835 this.requiresOldValue = false; 836 this.isStatic = true; 837 this.componentClass = null; 838 L.d("BINARY created method desc 1 %s %s", type, method ); 839 } 840 841 public MethodDescription(ExecutableElement method, int numAttributes, 842 boolean takesComponent) { 843 TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); 844 this.type = enclosingClass.getQualifiedName().toString(); 845 this.method = method.getSimpleName().toString(); 846 final int argStart = 1 + (takesComponent ? 1 : 0); 847 this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2; 848 this.isStatic = method.getModifiers().contains(Modifier.STATIC); 849 this.componentClass = takesComponent 850 ? getQualifiedName(method.getParameters().get(0).asType()) 851 : null; 852 853 L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); 854 } 855 856 @Override 857 public boolean equals(Object obj) { 858 if (obj instanceof MethodDescription) { 859 MethodDescription that = (MethodDescription) obj; 860 return that.type.equals(this.type) && that.method.equals(this.method); 861 } else { 862 return false; 863 } 864 } 865 866 @Override 867 public int hashCode() { 868 return mergedHashCode(type, method); 869 } 870 871 @Override 872 public String toString() { 873 return type + "." + method + "()"; 874 } 875 } 876 877 private static class AccessorKey implements Serializable { 878 879 private static final long serialVersionUID = 1; 880 881 public final String viewType; 882 883 public final String valueType; 884 885 public AccessorKey(String viewType, String valueType) { 886 this.viewType = viewType; 887 this.valueType = valueType; 888 } 889 890 @Override 891 public int hashCode() { 892 return mergedHashCode(viewType, valueType); 893 } 894 895 @Override 896 public boolean equals(Object obj) { 897 if (obj instanceof AccessorKey) { 898 AccessorKey that = (AccessorKey) obj; 899 return viewType.equals(that.valueType) && valueType.equals(that.valueType); 900 } else { 901 return false; 902 } 903 } 904 905 @Override 906 public String toString() { 907 return "AK(" + viewType + ", " + valueType + ")"; 908 } 909 } 910 911 private interface Intermediate extends Serializable { 912 Intermediate upgrade(); 913 } 914 915 private static class IntermediateV1 implements Serializable, Intermediate { 916 private static final long serialVersionUID = 1; 917 public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = 918 new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); 919 public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = 920 new HashMap<String, HashMap<String, MethodDescription>>(); 921 public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = 922 new HashMap<String, HashMap<String, MethodDescription>>(); 923 public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); 924 public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters = 925 new HashMap<MultiValueAdapterKey, MethodDescription>(); 926 927 public IntermediateV1() { 928 } 929 930 @Override 931 public Intermediate upgrade() { 932 return this; 933 } 934 } 935 936 public static class DummySetter extends SetterCall { 937 private String mMethodName; 938 939 public DummySetter(String methodName) { 940 mMethodName = methodName; 941 } 942 943 @Override 944 public String toJavaInternal(String componentExpression, String viewExpression, 945 String valueExpression) { 946 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 947 } 948 949 @Override 950 public String toJavaInternal(String componentExpression, String viewExpression, 951 String oldValue, String valueExpression) { 952 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 953 } 954 955 @Override 956 public int getMinApi() { 957 return 1; 958 } 959 960 @Override 961 public boolean requiresOldValue() { 962 return false; 963 } 964 965 @Override 966 public ModelClass[] getParameterTypes() { 967 return new ModelClass[] { 968 ModelAnalyzer.getInstance().findClass(Object.class) 969 }; 970 } 971 972 @Override 973 public String getBindingAdapterInstanceClass() { 974 return null; 975 } 976 977 @Override 978 public void setBindingAdapterCall(String method) { 979 } 980 } 981 982 public static class AdapterSetter extends SetterCall { 983 final MethodDescription mAdapter; 984 final ModelClass mParameterType; 985 String mBindingAdapterCall; 986 987 public AdapterSetter(MethodDescription adapter, ModelClass parameterType) { 988 mAdapter = adapter; 989 mParameterType = parameterType; 990 } 991 992 @Override 993 public String toJavaInternal(String componentExpression, String viewExpression, 994 String valueExpression) { 995 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 996 viewExpression, mCastString + valueExpression); 997 } 998 999 @Override 1000 protected String toJavaInternal(String componentExpression, String viewExpression, 1001 String oldValue, String valueExpression) { 1002 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1003 viewExpression, mCastString + oldValue, mCastString + valueExpression); 1004 } 1005 1006 @Override 1007 public int getMinApi() { 1008 return 1; 1009 } 1010 1011 @Override 1012 public boolean requiresOldValue() { 1013 return mAdapter.requiresOldValue; 1014 } 1015 1016 @Override 1017 public ModelClass[] getParameterTypes() { 1018 return new ModelClass[] { mParameterType }; 1019 } 1020 1021 @Override 1022 public String getBindingAdapterInstanceClass() { 1023 return mAdapter.isStatic ? null : mAdapter.type; 1024 } 1025 1026 @Override 1027 public void setBindingAdapterCall(String method) { 1028 mBindingAdapterCall = method; 1029 } 1030 } 1031 1032 public static class ModelMethodSetter extends SetterCall { 1033 final ModelMethod mModelMethod; 1034 1035 public ModelMethodSetter(ModelMethod modelMethod) { 1036 mModelMethod = modelMethod; 1037 } 1038 1039 @Override 1040 public String toJavaInternal(String componentExpression, String viewExpression, 1041 String valueExpression) { 1042 return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + 1043 valueExpression + ")"; 1044 } 1045 1046 @Override 1047 protected String toJavaInternal(String componentExpression, String viewExpression, 1048 String oldValue, String valueExpression) { 1049 return viewExpression + "." + mModelMethod.getName() + "(" + 1050 mCastString + oldValue + ", " + mCastString + valueExpression + ")"; 1051 } 1052 1053 @Override 1054 public int getMinApi() { 1055 return mModelMethod.getMinApi(); 1056 } 1057 1058 @Override 1059 public boolean requiresOldValue() { 1060 return mModelMethod.getParameterTypes().length == 3; 1061 } 1062 1063 @Override 1064 public ModelClass[] getParameterTypes() { 1065 return new ModelClass[] { mModelMethod.getParameterTypes()[0] }; 1066 } 1067 1068 @Override 1069 public String getBindingAdapterInstanceClass() { 1070 return null; 1071 } 1072 1073 @Override 1074 public void setBindingAdapterCall(String method) { 1075 } 1076 } 1077 1078 public interface BindingSetterCall { 1079 String toJava(String componentExpression, String viewExpression, 1080 String... valueExpressions); 1081 1082 int getMinApi(); 1083 1084 boolean requiresOldValue(); 1085 1086 ModelClass[] getParameterTypes(); 1087 1088 String getBindingAdapterInstanceClass(); 1089 1090 void setBindingAdapterCall(String method); 1091 } 1092 1093 public static abstract class SetterCall implements BindingSetterCall { 1094 private MethodDescription mConverter; 1095 protected String mCastString = ""; 1096 1097 public SetterCall() { 1098 } 1099 1100 public void setConverter(MethodDescription converter) { 1101 mConverter = converter; 1102 } 1103 1104 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1105 String converted); 1106 1107 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1108 String oldValue, String converted); 1109 1110 @Override 1111 public final String toJava(String componentExpression, String viewExpression, 1112 String... valueExpression) { 1113 Preconditions.check(valueExpression.length == 2, "value expressions size must be 2"); 1114 if (requiresOldValue()) { 1115 return toJavaInternal(componentExpression, viewExpression, 1116 convertValue(valueExpression[0]), convertValue(valueExpression[1])); 1117 } else { 1118 return toJavaInternal(componentExpression, viewExpression, 1119 convertValue(valueExpression[1])); 1120 } 1121 } 1122 1123 protected String convertValue(String valueExpression) { 1124 return mConverter == null ? valueExpression : 1125 mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; 1126 } 1127 1128 abstract public int getMinApi(); 1129 1130 public void setCast(ModelClass castTo) { 1131 mCastString = "(" + castTo.toJavaCode() + ") "; 1132 } 1133 } 1134 1135 public static class MultiAttributeSetter implements BindingSetterCall { 1136 public final String[] attributes; 1137 private final MethodDescription mAdapter; 1138 private final MethodDescription[] mConverters; 1139 private final String[] mCasts; 1140 private final MultiValueAdapterKey mKey; 1141 String mBindingAdapterCall; 1142 1143 public MultiAttributeSetter(MultiValueAdapterKey key, String[] attributes, 1144 MethodDescription adapter, MethodDescription[] converters, String[] casts) { 1145 Preconditions.check(converters != null && 1146 converters.length == attributes.length && 1147 casts != null && casts.length == attributes.length, 1148 "invalid arguments to create multi attr setter"); 1149 this.attributes = attributes; 1150 this.mAdapter = adapter; 1151 this.mConverters = converters; 1152 this.mCasts = casts; 1153 this.mKey = key; 1154 } 1155 1156 @Override 1157 public final String toJava(String componentExpression, String viewExpression, 1158 String[] valueExpressions) { 1159 Preconditions.check(valueExpressions.length == attributes.length * 2, 1160 "MultiAttributeSetter needs %s items, received %s", 1161 Arrays.toString(attributes), Arrays.toString(valueExpressions)); 1162 final int numAttrs = attributes.length; 1163 String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)]; 1164 1165 final int startIndex = mAdapter.requiresOldValue ? 0 : attributes.length; 1166 StringBuilder argBuilder = new StringBuilder(); 1167 for (int i = startIndex; i < valueExpressions.length; i++) { 1168 argBuilder.setLength(0); 1169 if (mConverters[i % attributes.length] != null) { 1170 final MethodDescription converter = mConverters[i % attributes.length]; 1171 argBuilder.append(converter.type) 1172 .append('.') 1173 .append(converter.method) 1174 .append('(') 1175 .append(valueExpressions[i]) 1176 .append(')'); 1177 } else { 1178 if (mCasts[i % attributes.length] != null) { 1179 argBuilder.append('(') 1180 .append(mCasts[i % attributes.length]) 1181 .append(')'); 1182 } 1183 argBuilder.append(valueExpressions[i]); 1184 } 1185 args[i - startIndex] = argBuilder.toString(); 1186 } 1187 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1188 viewExpression, args); 1189 } 1190 1191 @Override 1192 public int getMinApi() { 1193 return 1; 1194 } 1195 1196 @Override 1197 public boolean requiresOldValue() { 1198 return mAdapter.requiresOldValue; 1199 } 1200 1201 @Override 1202 public ModelClass[] getParameterTypes() { 1203 ModelClass[] parameters = new ModelClass[attributes.length]; 1204 String[] paramTypeStrings = mKey.parameterTypes; 1205 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1206 for (int i = 0; i < attributes.length; i++) { 1207 parameters[i] = modelAnalyzer.findClass(paramTypeStrings[i], null); 1208 } 1209 return parameters; 1210 } 1211 1212 @Override 1213 public String getBindingAdapterInstanceClass() { 1214 return mAdapter.isStatic ? null : mAdapter.type; 1215 } 1216 1217 @Override 1218 public void setBindingAdapterCall(String method) { 1219 mBindingAdapterCall = method; 1220 } 1221 1222 @Override 1223 public String toString() { 1224 return "MultiAttributeSetter{" + 1225 "attributes=" + Arrays.toString(attributes) + 1226 ", mAdapter=" + mAdapter + 1227 ", mConverters=" + Arrays.toString(mConverters) + 1228 ", mCasts=" + Arrays.toString(mCasts) + 1229 ", mKey=" + mKey + 1230 '}'; 1231 } 1232 } 1233} 1234