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