SetterStore.java revision 97758524d3953793b50e3e0121ef3cbdc047b35b
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 if (to.isObject()) { 680 return null; 681 } 682 for (String fromClassName : mStore.conversionMethods.keySet()) { 683 try { 684 ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports); 685 if (canUseForConversion(from, convertFrom)) { 686 HashMap<String, MethodDescription> conversion = 687 mStore.conversionMethods.get(fromClassName); 688 for (String toClassName : conversion.keySet()) { 689 try { 690 ModelClass convertTo = mClassAnalyzer.findClass(toClassName, 691 imports); 692 if (canUseForConversion(convertTo, to)) { 693 return conversion.get(toClassName); 694 } 695 } catch (Exception e) { 696 L.d(e, "Unknown class: %s", toClassName); 697 } 698 } 699 } 700 } catch (Exception e) { 701 L.d(e, "Unknown class: %s", fromClassName); 702 } 703 } 704 } 705 return null; 706 } 707 708 private boolean canUseForConversion(ModelClass from, ModelClass to) { 709 return from.equals(to) || ModelMethod.isBoxingConversion(from, to) || 710 to.isAssignableFrom(from); 711 } 712 713 private static void merge(IntermediateV1 store, Intermediate dumpStore) { 714 IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade(); 715 merge(store.adapterMethods, intermediateV1.adapterMethods); 716 merge(store.renamedMethods, intermediateV1.renamedMethods); 717 merge(store.conversionMethods, intermediateV1.conversionMethods); 718 store.multiValueAdapters.putAll(intermediateV1.multiValueAdapters); 719 store.untaggableTypes.putAll(intermediateV1.untaggableTypes); 720 } 721 722 private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first, 723 HashMap<K, HashMap<V, MethodDescription>> second) { 724 for (K key : second.keySet()) { 725 HashMap<V, MethodDescription> firstVals = first.get(key); 726 HashMap<V, MethodDescription> secondVals = second.get(key); 727 if (firstVals == null) { 728 first.put(key, secondVals); 729 } else { 730 for (V key2 : secondVals.keySet()) { 731 if (!firstVals.containsKey(key2)) { 732 firstVals.put(key2, secondVals.get(key2)); 733 } 734 } 735 } 736 } 737 } 738 739 private static String createAdapterCall(MethodDescription adapter, String bindingAdapterCall, 740 String componentExpression, String viewExpression, String... args) { 741 StringBuilder sb = new StringBuilder(); 742 743 if (adapter.isStatic) { 744 sb.append(adapter.type); 745 } else { 746 sb.append(componentExpression).append('.').append(bindingAdapterCall); 747 } 748 sb.append('.').append(adapter.method).append('('); 749 if (adapter.componentClass != null) { 750 if (!"DataBindingComponent".equals(adapter.componentClass)) { 751 sb.append('(').append(adapter.componentClass).append(") "); 752 } 753 sb.append(componentExpression).append(", "); 754 } 755 sb.append(viewExpression); 756 for (String arg: args) { 757 sb.append(", ").append(arg); 758 } 759 sb.append(')'); 760 return sb.toString(); 761 } 762 763 private static class MultiValueAdapterKey implements Serializable { 764 private static final long serialVersionUID = 1; 765 766 public final String viewType; 767 768 public final String[] attributes; 769 770 public final String[] parameterTypes; 771 772 public final boolean requireAll; 773 774 public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>(); 775 776 public MultiValueAdapterKey(ProcessingEnvironment processingEnv, 777 ExecutableElement method, String[] attributes, boolean takesComponent, 778 boolean requireAll) { 779 this.attributes = stripAttributes(attributes); 780 this.requireAll = requireAll; 781 List<? extends VariableElement> parameters = method.getParameters(); 782 final int argStart = 1 + (takesComponent ? 1 : 0); 783 this.viewType = getQualifiedName(eraseType(processingEnv, 784 parameters.get(argStart - 1).asType())); 785 this.parameterTypes = new String[parameters.size() - argStart]; 786 for (int i = 0; i < attributes.length; i++) { 787 TypeMirror typeMirror = eraseType(processingEnv, 788 parameters.get(i + argStart).asType()); 789 this.parameterTypes[i] = getQualifiedName(typeMirror); 790 attributeIndices.put(this.attributes[i], i); 791 } 792 } 793 794 @Override 795 public boolean equals(Object obj) { 796 if (!(obj instanceof MultiValueAdapterKey)) { 797 return false; 798 } 799 final MultiValueAdapterKey that = (MultiValueAdapterKey) obj; 800 if (!this.viewType.equals(that.viewType) || 801 this.attributes.length != that.attributes.length || 802 !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) { 803 return false; 804 } 805 806 for (int i = 0; i < this.attributes.length; i++) { 807 final int thatIndex = that.attributeIndices.get(this.attributes[i]); 808 final String thisParameter = parameterTypes[i]; 809 final String thatParameter = that.parameterTypes[thatIndex]; 810 if (!thisParameter.equals(thatParameter)) { 811 return false; 812 } 813 } 814 return true; 815 } 816 817 @Override 818 public int hashCode() { 819 return mergedHashCode(viewType, attributeIndices.keySet()); 820 } 821 } 822 823 private static int mergedHashCode(Object... objects) { 824 return Arrays.hashCode(objects); 825 } 826 827 private static class MethodDescription implements Serializable { 828 829 private static final long serialVersionUID = 1; 830 831 public final String type; 832 833 public final String method; 834 835 public final boolean requiresOldValue; 836 837 public final boolean isStatic; 838 839 public final String componentClass; 840 841 public MethodDescription(String type, String method) { 842 this.type = type; 843 this.method = method; 844 this.requiresOldValue = false; 845 this.isStatic = true; 846 this.componentClass = null; 847 L.d("BINARY created method desc 1 %s %s", type, method ); 848 } 849 850 public MethodDescription(ExecutableElement method, int numAttributes, 851 boolean takesComponent) { 852 TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); 853 this.type = enclosingClass.getQualifiedName().toString(); 854 this.method = method.getSimpleName().toString(); 855 final int argStart = 1 + (takesComponent ? 1 : 0); 856 this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2; 857 this.isStatic = method.getModifiers().contains(Modifier.STATIC); 858 this.componentClass = takesComponent 859 ? getQualifiedName(method.getParameters().get(0).asType()) 860 : null; 861 862 L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); 863 } 864 865 @Override 866 public boolean equals(Object obj) { 867 if (obj instanceof MethodDescription) { 868 MethodDescription that = (MethodDescription) obj; 869 return that.type.equals(this.type) && that.method.equals(this.method); 870 } else { 871 return false; 872 } 873 } 874 875 @Override 876 public int hashCode() { 877 return mergedHashCode(type, method); 878 } 879 880 @Override 881 public String toString() { 882 return type + "." + method + "()"; 883 } 884 } 885 886 private static class AccessorKey implements Serializable { 887 888 private static final long serialVersionUID = 1; 889 890 public final String viewType; 891 892 public final String valueType; 893 894 public AccessorKey(String viewType, String valueType) { 895 this.viewType = viewType; 896 this.valueType = valueType; 897 } 898 899 @Override 900 public int hashCode() { 901 return mergedHashCode(viewType, valueType); 902 } 903 904 @Override 905 public boolean equals(Object obj) { 906 if (obj instanceof AccessorKey) { 907 AccessorKey that = (AccessorKey) obj; 908 return viewType.equals(that.valueType) && valueType.equals(that.valueType); 909 } else { 910 return false; 911 } 912 } 913 914 @Override 915 public String toString() { 916 return "AK(" + viewType + ", " + valueType + ")"; 917 } 918 } 919 920 private interface Intermediate extends Serializable { 921 Intermediate upgrade(); 922 } 923 924 private static class IntermediateV1 implements Serializable, Intermediate { 925 private static final long serialVersionUID = 1; 926 public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = 927 new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); 928 public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = 929 new HashMap<String, HashMap<String, MethodDescription>>(); 930 public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = 931 new HashMap<String, HashMap<String, MethodDescription>>(); 932 public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); 933 public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters = 934 new HashMap<MultiValueAdapterKey, MethodDescription>(); 935 936 public IntermediateV1() { 937 } 938 939 @Override 940 public Intermediate upgrade() { 941 return this; 942 } 943 } 944 945 public static class DummySetter extends SetterCall { 946 private String mMethodName; 947 948 public DummySetter(String methodName) { 949 mMethodName = methodName; 950 } 951 952 @Override 953 public String toJavaInternal(String componentExpression, String viewExpression, 954 String valueExpression) { 955 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 956 } 957 958 @Override 959 public String toJavaInternal(String componentExpression, String viewExpression, 960 String oldValue, String valueExpression) { 961 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 962 } 963 964 @Override 965 public int getMinApi() { 966 return 1; 967 } 968 969 @Override 970 public boolean requiresOldValue() { 971 return false; 972 } 973 974 @Override 975 public ModelClass[] getParameterTypes() { 976 return new ModelClass[] { 977 ModelAnalyzer.getInstance().findClass(Object.class) 978 }; 979 } 980 981 @Override 982 public String getBindingAdapterInstanceClass() { 983 return null; 984 } 985 986 @Override 987 public void setBindingAdapterCall(String method) { 988 } 989 } 990 991 public static class AdapterSetter extends SetterCall { 992 final MethodDescription mAdapter; 993 final ModelClass mParameterType; 994 String mBindingAdapterCall; 995 996 public AdapterSetter(MethodDescription adapter, ModelClass parameterType) { 997 mAdapter = adapter; 998 mParameterType = parameterType; 999 } 1000 1001 @Override 1002 public String toJavaInternal(String componentExpression, String viewExpression, 1003 String valueExpression) { 1004 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1005 viewExpression, mCastString + valueExpression); 1006 } 1007 1008 @Override 1009 protected String toJavaInternal(String componentExpression, String viewExpression, 1010 String oldValue, String valueExpression) { 1011 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1012 viewExpression, mCastString + oldValue, mCastString + valueExpression); 1013 } 1014 1015 @Override 1016 public int getMinApi() { 1017 return 1; 1018 } 1019 1020 @Override 1021 public boolean requiresOldValue() { 1022 return mAdapter.requiresOldValue; 1023 } 1024 1025 @Override 1026 public ModelClass[] getParameterTypes() { 1027 return new ModelClass[] { mParameterType }; 1028 } 1029 1030 @Override 1031 public String getBindingAdapterInstanceClass() { 1032 return mAdapter.isStatic ? null : mAdapter.type; 1033 } 1034 1035 @Override 1036 public void setBindingAdapterCall(String method) { 1037 mBindingAdapterCall = method; 1038 } 1039 } 1040 1041 public static class ModelMethodSetter extends SetterCall { 1042 final ModelMethod mModelMethod; 1043 1044 public ModelMethodSetter(ModelMethod modelMethod) { 1045 mModelMethod = modelMethod; 1046 } 1047 1048 @Override 1049 public String toJavaInternal(String componentExpression, String viewExpression, 1050 String valueExpression) { 1051 return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + 1052 valueExpression + ")"; 1053 } 1054 1055 @Override 1056 protected String toJavaInternal(String componentExpression, String viewExpression, 1057 String oldValue, String valueExpression) { 1058 return viewExpression + "." + mModelMethod.getName() + "(" + 1059 mCastString + oldValue + ", " + mCastString + valueExpression + ")"; 1060 } 1061 1062 @Override 1063 public int getMinApi() { 1064 return mModelMethod.getMinApi(); 1065 } 1066 1067 @Override 1068 public boolean requiresOldValue() { 1069 return mModelMethod.getParameterTypes().length == 3; 1070 } 1071 1072 @Override 1073 public ModelClass[] getParameterTypes() { 1074 return new ModelClass[] { mModelMethod.getParameterTypes()[0] }; 1075 } 1076 1077 @Override 1078 public String getBindingAdapterInstanceClass() { 1079 return null; 1080 } 1081 1082 @Override 1083 public void setBindingAdapterCall(String method) { 1084 } 1085 } 1086 1087 public interface BindingSetterCall { 1088 String toJava(String componentExpression, String viewExpression, 1089 String... valueExpressions); 1090 1091 int getMinApi(); 1092 1093 boolean requiresOldValue(); 1094 1095 ModelClass[] getParameterTypes(); 1096 1097 String getBindingAdapterInstanceClass(); 1098 1099 void setBindingAdapterCall(String method); 1100 } 1101 1102 public static abstract class SetterCall implements BindingSetterCall { 1103 private MethodDescription mConverter; 1104 protected String mCastString = ""; 1105 1106 public SetterCall() { 1107 } 1108 1109 public void setConverter(MethodDescription converter) { 1110 mConverter = converter; 1111 } 1112 1113 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1114 String converted); 1115 1116 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1117 String oldValue, String converted); 1118 1119 @Override 1120 public final String toJava(String componentExpression, String viewExpression, 1121 String... valueExpression) { 1122 Preconditions.check(valueExpression.length == 2, "value expressions size must be 2"); 1123 if (requiresOldValue()) { 1124 return toJavaInternal(componentExpression, viewExpression, 1125 convertValue(valueExpression[0]), convertValue(valueExpression[1])); 1126 } else { 1127 return toJavaInternal(componentExpression, viewExpression, 1128 convertValue(valueExpression[1])); 1129 } 1130 } 1131 1132 protected String convertValue(String valueExpression) { 1133 return mConverter == null ? valueExpression : 1134 mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; 1135 } 1136 1137 abstract public int getMinApi(); 1138 1139 public void setCast(ModelClass castTo) { 1140 mCastString = "(" + castTo.toJavaCode() + ") "; 1141 } 1142 } 1143 1144 public static class MultiAttributeSetter implements BindingSetterCall { 1145 public final String[] attributes; 1146 private final MethodDescription mAdapter; 1147 private final MethodDescription[] mConverters; 1148 private final String[] mCasts; 1149 private final MultiValueAdapterKey mKey; 1150 private final boolean[] mSupplied; 1151 String mBindingAdapterCall; 1152 1153 public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied, 1154 MethodDescription adapter, MethodDescription[] converters, String[] casts) { 1155 Preconditions.check(converters != null && 1156 converters.length == key.attributes.length && 1157 casts != null && casts.length == key.attributes.length && 1158 supplied.length == key.attributes.length, 1159 "invalid arguments to create multi attr setter"); 1160 this.mAdapter = adapter; 1161 this.mConverters = converters; 1162 this.mCasts = casts; 1163 this.mKey = key; 1164 this.mSupplied = supplied; 1165 if (key.requireAll) { 1166 this.attributes = key.attributes; 1167 } else { 1168 int numSupplied = 0; 1169 for (int i = 0; i < mKey.attributes.length; i++) { 1170 if (supplied[i]) { 1171 numSupplied++; 1172 } 1173 } 1174 if (numSupplied == key.attributes.length) { 1175 this.attributes = key.attributes; 1176 } else { 1177 this.attributes = new String[numSupplied]; 1178 int attrIndex = 0; 1179 for (int i = 0; i < key.attributes.length; i++) { 1180 if (supplied[i]) { 1181 attributes[attrIndex++] = key.attributes[i]; 1182 } 1183 } 1184 } 1185 } 1186 } 1187 1188 @Override 1189 public final String toJava(String componentExpression, String viewExpression, 1190 String[] valueExpressions) { 1191 Preconditions.check(valueExpressions.length == attributes.length * 2, 1192 "MultiAttributeSetter needs %s items, received %s", 1193 Arrays.toString(attributes), Arrays.toString(valueExpressions)); 1194 final int numAttrs = mKey.attributes.length; 1195 String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)]; 1196 1197 final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs; 1198 int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length; 1199 final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1200 StringBuilder argBuilder = new StringBuilder(); 1201 final int endIndex = numAttrs * 2; 1202 for (int i = startIndex; i < endIndex; i++) { 1203 argBuilder.setLength(0); 1204 if (!mSupplied[i % numAttrs]) { 1205 final String paramType = mKey.parameterTypes[i % numAttrs]; 1206 final String defaultValue = modelAnalyzer.getDefaultValue(paramType); 1207 argBuilder.append('(') 1208 .append(paramType) 1209 .append(')') 1210 .append(defaultValue); 1211 } else { 1212 if (mConverters[i % numAttrs] != null) { 1213 final MethodDescription converter = mConverters[i % numAttrs]; 1214 argBuilder.append(converter.type) 1215 .append('.') 1216 .append(converter.method) 1217 .append('(') 1218 .append(valueExpressions[attrIndex]) 1219 .append(')'); 1220 } else { 1221 if (mCasts[i % numAttrs] != null) { 1222 argBuilder.append('(') 1223 .append(mCasts[i % numAttrs]) 1224 .append(')'); 1225 } 1226 argBuilder.append(valueExpressions[attrIndex]); 1227 } 1228 attrIndex++; 1229 } 1230 args[i - startIndex] = argBuilder.toString(); 1231 } 1232 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1233 viewExpression, args); 1234 } 1235 1236 @Override 1237 public int getMinApi() { 1238 return 1; 1239 } 1240 1241 @Override 1242 public boolean requiresOldValue() { 1243 return mAdapter.requiresOldValue; 1244 } 1245 1246 @Override 1247 public ModelClass[] getParameterTypes() { 1248 ModelClass[] parameters = new ModelClass[attributes.length]; 1249 String[] paramTypeStrings = mKey.parameterTypes; 1250 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1251 int attrIndex = 0; 1252 for (int i = 0; i < mKey.attributes.length; i++) { 1253 if (mSupplied[i]) { 1254 parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null); 1255 } 1256 } 1257 return parameters; 1258 } 1259 1260 @Override 1261 public String getBindingAdapterInstanceClass() { 1262 return mAdapter.isStatic ? null : mAdapter.type; 1263 } 1264 1265 @Override 1266 public void setBindingAdapterCall(String method) { 1267 mBindingAdapterCall = method; 1268 } 1269 1270 @Override 1271 public String toString() { 1272 return "MultiAttributeSetter{" + 1273 "attributes=" + Arrays.toString(attributes) + 1274 ", mAdapter=" + mAdapter + 1275 ", mConverters=" + Arrays.toString(mConverters) + 1276 ", mCasts=" + Arrays.toString(mCasts) + 1277 ", mKey=" + mKey + 1278 '}'; 1279 } 1280 } 1281} 1282