SetterStore.java revision bd42d20f70b1f88e27e3b3c9c3a9c55ec155d128
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 final TypeKind kind = type.getKind(); 291 if (kind == TypeKind.ARRAY) { 292 return getQualifiedName(((ArrayType) type).getComponentType()) + "[]"; 293 } else if (kind == TypeKind.DECLARED && isIncompleteType(type)) { 294 DeclaredType declaredType = (DeclaredType) type; 295 return declaredType.asElement().toString(); 296 } else { 297 return type.toString(); 298 } 299 } 300 301 private static boolean isIncompleteType(TypeMirror type) { 302 final TypeKind kind = type.getKind(); 303 if (kind == TypeKind.TYPEVAR || kind == TypeKind.WILDCARD) { 304 return true; 305 } else if (kind == TypeKind.DECLARED) { 306 DeclaredType declaredType = (DeclaredType) type; 307 List<? extends TypeMirror> typeArgs = declaredType.getTypeArguments(); 308 if (typeArgs == null) { 309 return false; 310 } 311 for (TypeMirror arg : typeArgs) { 312 if (isIncompleteType(arg)) { 313 return true; 314 } 315 } 316 } 317 return false; 318 } 319 320 public void addConversionMethod(ExecutableElement conversionMethod) { 321 L.d("STORE addConversionMethod %s", conversionMethod); 322 List<? extends VariableElement> parameters = conversionMethod.getParameters(); 323 String fromType = getQualifiedName(parameters.get(0).asType()); 324 String toType = getQualifiedName(conversionMethod.getReturnType()); 325 MethodDescription methodDescription = new MethodDescription(conversionMethod, 1, false); 326 HashMap<String, MethodDescription> convertTo = mStore.conversionMethods.get(fromType); 327 if (convertTo == null) { 328 convertTo = new HashMap<String, MethodDescription>(); 329 mStore.conversionMethods.put(fromType, convertTo); 330 } 331 convertTo.put(toType, methodDescription); 332 } 333 334 public void clear(Set<String> classes) { 335 ArrayList<AccessorKey> removedAccessorKeys = new ArrayList<AccessorKey>(); 336 for (HashMap<AccessorKey, MethodDescription> adapters : mStore.adapterMethods.values()) { 337 for (AccessorKey key : adapters.keySet()) { 338 MethodDescription description = adapters.get(key); 339 if (classes.contains(description.type)) { 340 removedAccessorKeys.add(key); 341 } 342 } 343 removeFromMap(adapters, removedAccessorKeys); 344 } 345 346 ArrayList<String> removedRenamed = new ArrayList<String>(); 347 for (HashMap<String, MethodDescription> renamed : mStore.renamedMethods.values()) { 348 for (String key : renamed.keySet()) { 349 if (classes.contains(renamed.get(key).type)) { 350 removedRenamed.add(key); 351 } 352 } 353 removeFromMap(renamed, removedRenamed); 354 } 355 356 ArrayList<String> removedConversions = new ArrayList<String>(); 357 for (HashMap<String, MethodDescription> convertTos : mStore.conversionMethods.values()) { 358 for (String toType : convertTos.keySet()) { 359 MethodDescription methodDescription = convertTos.get(toType); 360 if (classes.contains(methodDescription.type)) { 361 removedConversions.add(toType); 362 } 363 } 364 removeFromMap(convertTos, removedConversions); 365 } 366 367 ArrayList<String> removedUntaggable = new ArrayList<String>(); 368 for (String typeName : mStore.untaggableTypes.keySet()) { 369 if (classes.contains(mStore.untaggableTypes.get(typeName))) { 370 removedUntaggable.add(typeName); 371 } 372 } 373 removeFromMap(mStore.untaggableTypes, removedUntaggable); 374 } 375 376 private static <K, V> void removeFromMap(Map<K, V> map, List<K> keys) { 377 for (K key : keys) { 378 map.remove(key); 379 } 380 keys.clear(); 381 } 382 383 public void write(String projectPackage, ProcessingEnvironment processingEnvironment) 384 throws IOException { 385 GenerationalClassUtil.writeIntermediateFile(processingEnvironment, 386 projectPackage, projectPackage + 387 GenerationalClassUtil.ExtensionFilter.SETTER_STORE.getExtension(), mStore); 388 } 389 390 private static String stripNamespace(String attribute) { 391 if (!attribute.startsWith("android:")) { 392 int colon = attribute.indexOf(':'); 393 if (colon >= 0) { 394 attribute = attribute.substring(colon + 1); 395 } 396 } 397 return attribute; 398 } 399 400 public List<MultiAttributeSetter> getMultiAttributeSetterCalls(String[] attributes, 401 ModelClass viewType, ModelClass[] valueType) { 402 attributes = stripAttributes(attributes); 403 final ArrayList<MultiAttributeSetter> calls = new ArrayList<MultiAttributeSetter>(); 404 if (viewType != null && viewType.isGeneric()) { 405 List<ModelClass> viewGenerics = viewType.getTypeArguments(); 406 for (int i = 0; i < valueType.length; i++) { 407 valueType[i] = eraseType(valueType[i], viewGenerics); 408 } 409 viewType = viewType.erasure(); 410 } 411 ArrayList<MultiAttributeSetter> matching = getMatchingMultiAttributeSetters(attributes, 412 viewType, valueType); 413 Collections.sort(matching, COMPARE_MULTI_ATTRIBUTE_SETTERS); 414 while (!matching.isEmpty()) { 415 MultiAttributeSetter bestMatch = matching.get(0); 416 calls.add(bestMatch); 417 removeConsumedAttributes(matching, bestMatch.attributes); 418 } 419 return calls; 420 } 421 422 // Removes all MultiAttributeSetters that require any of the values in attributes 423 private static void removeConsumedAttributes(ArrayList<MultiAttributeSetter> matching, 424 String[] attributes) { 425 for (int i = matching.size() - 1; i >= 0; i--) { 426 final MultiAttributeSetter setter = matching.get(i); 427 boolean found = false; 428 for (String attribute : attributes) { 429 if (isInArray(attribute, setter.attributes)) { 430 found = true; 431 break; 432 } 433 } 434 if (found) { 435 matching.remove(i); 436 } 437 } 438 } 439 440 // Linear search through the String array for a specific value. 441 private static boolean isInArray(String str, String[] array) { 442 for (String value : array) { 443 if (value.equals(str)) { 444 return true; 445 } 446 } 447 return false; 448 } 449 450 private ArrayList<MultiAttributeSetter> getMatchingMultiAttributeSetters(String[] attributes, 451 ModelClass viewType, ModelClass[] valueType) { 452 final ArrayList<MultiAttributeSetter> setters = new ArrayList<MultiAttributeSetter>(); 453 for (MultiValueAdapterKey adapter : mStore.multiValueAdapters.keySet()) { 454 if (adapter.requireAll && adapter.attributes.length > attributes.length) { 455 continue; 456 } 457 ModelClass viewClass = mClassAnalyzer.findClass(adapter.viewType, null); 458 if (viewClass.isGeneric()) { 459 viewClass = viewClass.erasure(); 460 } 461 if (!viewClass.isAssignableFrom(viewType)) { 462 continue; 463 } 464 final MethodDescription method = mStore.multiValueAdapters.get(adapter); 465 final MultiAttributeSetter setter = createMultiAttributeSetter(method, attributes, 466 valueType, adapter); 467 if (setter != null) { 468 setters.add(setter); 469 } 470 } 471 return setters; 472 } 473 474 private MultiAttributeSetter createMultiAttributeSetter(MethodDescription method, 475 String[] allAttributes, ModelClass[] attributeValues, MultiValueAdapterKey adapter) { 476 int matchingAttributes = 0; 477 String[] casts = new String[adapter.attributes.length]; 478 MethodDescription[] conversions = new MethodDescription[adapter.attributes.length]; 479 boolean[] supplied = new boolean[adapter.attributes.length]; 480 481 for (int i = 0; i < allAttributes.length; i++) { 482 Integer index = adapter.attributeIndices.get(allAttributes[i]); 483 if (index != null) { 484 supplied[index] = true; 485 matchingAttributes++; 486 final String parameterTypeStr = adapter.parameterTypes[index]; 487 final ModelClass parameterType = eraseType( 488 mClassAnalyzer.findClass(parameterTypeStr, null)); 489 final ModelClass attributeType = attributeValues[i]; 490 if (!parameterType.isAssignableFrom(attributeType)) { 491 if (ModelMethod.isBoxingConversion(parameterType, attributeType)) { 492 // automatic boxing is ok 493 continue; 494 } else if (ModelMethod.isImplicitConversion(attributeType, parameterType)) { 495 // implicit conversion is ok 496 continue; 497 } 498 // Look for a converter 499 conversions[index] = getConversionMethod(attributeType, parameterType, null); 500 if (conversions[index] == null) { 501 if (attributeType.isObject()) { 502 // Cast is allowed also 503 casts[index] = parameterTypeStr; 504 } else { 505 // Parameter type mismatch 506 return null; 507 } 508 } 509 } 510 } 511 } 512 513 if ((adapter.requireAll && matchingAttributes != adapter.attributes.length) || 514 matchingAttributes == 0) { 515 return null; 516 } else { 517 return new MultiAttributeSetter(adapter, supplied, method, conversions, casts); 518 } 519 } 520 521 public SetterCall getSetterCall(String attribute, ModelClass viewType, 522 ModelClass valueType, Map<String, String> imports) { 523 attribute = stripNamespace(attribute); 524 SetterCall setterCall = null; 525 MethodDescription conversionMethod = null; 526 if (viewType != null) { 527 viewType = viewType.erasure(); 528 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 529 ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports); 530 ModelClass bestViewType = null; 531 ModelClass bestValueType = null; 532 if (bestSetterMethod != null) { 533 bestViewType = bestSetterMethod.getDeclaringClass(); 534 bestValueType = bestSetterMethod.getParameterTypes()[0]; 535 setterCall = new ModelMethodSetter(bestSetterMethod); 536 } 537 538 if (adapters != null) { 539 for (AccessorKey key : adapters.keySet()) { 540 try { 541 ModelClass adapterViewType = mClassAnalyzer 542 .findClass(key.viewType, imports).erasure(); 543 if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) { 544 try { 545 L.d("setter parameter type is %s", key.valueType); 546 final ModelClass adapterValueType = eraseType(mClassAnalyzer 547 .findClass(key.valueType, imports)); 548 L.d("setter %s takes type %s, compared to %s", 549 adapters.get(key).method, adapterValueType.toJavaCode(), 550 valueType.toJavaCode()); 551 boolean isBetterView = bestViewType == null || 552 bestValueType.isAssignableFrom(adapterValueType); 553 if (isBetterParameter(valueType, adapterValueType, bestValueType, 554 isBetterView, imports)) { 555 bestViewType = adapterViewType; 556 bestValueType = adapterValueType; 557 MethodDescription adapter = adapters.get(key); 558 setterCall = new AdapterSetter(adapter, adapterValueType); 559 } 560 561 } catch (Exception e) { 562 L.e(e, "Unknown class: %s", key.valueType); 563 } 564 } 565 } catch (Exception e) { 566 L.e(e, "Unknown class: %s", key.viewType); 567 } 568 } 569 } 570 571 conversionMethod = getConversionMethod(valueType, bestValueType, imports); 572 if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) { 573 setterCall.setCast(bestValueType); 574 } 575 } 576 if (setterCall == null) { 577 if (viewType != null && !viewType.isViewDataBinding()) { 578 return null; // no setter found!! 579 } 580 setterCall = new DummySetter(getDefaultSetter(attribute)); 581 } 582 setterCall.setConverter(conversionMethod); 583 return setterCall; 584 } 585 586 public boolean isUntaggable(String viewType) { 587 return mStore.untaggableTypes.containsKey(viewType); 588 } 589 590 private ModelMethod getBestSetter(ModelClass viewType, ModelClass argumentType, 591 String attribute, Map<String, String> imports) { 592 if (viewType.isGeneric()) { 593 argumentType = eraseType(argumentType, viewType.getTypeArguments()); 594 viewType = viewType.erasure(); 595 } 596 List<String> setterCandidates = new ArrayList<String>(); 597 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 598 if (renamed != null) { 599 for (String className : renamed.keySet()) { 600 try { 601 ModelClass renamedViewType = mClassAnalyzer.findClass(className, imports); 602 if (renamedViewType.erasure().isAssignableFrom(viewType)) { 603 setterCandidates.add(renamed.get(className).method); 604 break; 605 } 606 } catch (Exception e) { 607 //printMessage(Diagnostic.Kind.NOTE, "Unknown class: " + className); 608 } 609 } 610 } 611 setterCandidates.add(getDefaultSetter(attribute)); 612 setterCandidates.add(trimAttributeNamespace(attribute)); 613 614 ModelMethod bestMethod = null; 615 ModelClass bestParameterType = null; 616 List<ModelClass> args = new ArrayList<ModelClass>(); 617 args.add(argumentType); 618 for (String name : setterCandidates) { 619 ModelMethod[] methods = viewType.getMethods(name, 1); 620 621 for (ModelMethod method : methods) { 622 ModelClass[] parameterTypes = method.getParameterTypes(); 623 ModelClass param = parameterTypes[0]; 624 if (method.isVoid() && 625 isBetterParameter(argumentType, param, bestParameterType, true, imports)) { 626 bestParameterType = param; 627 bestMethod = method; 628 } 629 } 630 } 631 return bestMethod; 632 } 633 634 private static ModelClass eraseType(ModelClass type, List<ModelClass> typeParameters) { 635 List<ModelClass> typeArguments = type.getTypeArguments(); 636 if (typeArguments == null || typeParameters == null) { 637 return type; 638 } 639 for (ModelClass arg : typeArguments) { 640 if (typeParameters.contains(arg)) { 641 return type.erasure(); 642 } 643 } 644 return type; 645 } 646 647 private static String trimAttributeNamespace(String attribute) { 648 final int colonIndex = attribute.indexOf(':'); 649 return colonIndex == -1 ? attribute : attribute.substring(colonIndex + 1); 650 } 651 652 private static String getDefaultSetter(String attribute) { 653 return "set" + StringUtils.capitalize(trimAttributeNamespace(attribute)); 654 } 655 656 private boolean isBetterParameter(ModelClass argument, ModelClass parameter, 657 ModelClass oldParameter, boolean isBetterViewTypeMatch, Map<String, String> imports) { 658 // Right view type. Check the value 659 if (!isBetterViewTypeMatch && oldParameter.equals(argument)) { 660 return false; 661 } else if (argument.equals(parameter)) { 662 // Exact match 663 return true; 664 } else if (!isBetterViewTypeMatch && 665 ModelMethod.isBoxingConversion(oldParameter, argument)) { 666 return false; 667 } else if (ModelMethod.isBoxingConversion(parameter, argument)) { 668 // Boxing/unboxing is second best 669 return true; 670 } else { 671 int oldConversionLevel = ModelMethod.getImplicitConversionLevel(oldParameter); 672 if (ModelMethod.isImplicitConversion(argument, parameter)) { 673 // Better implicit conversion 674 int conversionLevel = ModelMethod.getImplicitConversionLevel(parameter); 675 return oldConversionLevel < 0 || conversionLevel < oldConversionLevel; 676 } else if (oldConversionLevel >= 0) { 677 return false; 678 } else if (parameter.isAssignableFrom(argument)) { 679 // Right type, see if it is better than the current best match. 680 if (oldParameter == null) { 681 return true; 682 } else { 683 return oldParameter.isAssignableFrom(parameter); 684 } 685 } else { 686 MethodDescription conversionMethod = getConversionMethod(argument, parameter, 687 imports); 688 if (conversionMethod != null) { 689 return true; 690 } 691 if (getConversionMethod(argument, oldParameter, imports) != null) { 692 return false; 693 } 694 return argument.isObject() && !parameter.isPrimitive(); 695 } 696 } 697 } 698 699 private MethodDescription getConversionMethod(ModelClass from, ModelClass to, 700 Map<String, String> imports) { 701 if (from != null && to != null) { 702 if (to.isObject()) { 703 return null; 704 } 705 for (String fromClassName : mStore.conversionMethods.keySet()) { 706 try { 707 ModelClass convertFrom = mClassAnalyzer.findClass(fromClassName, imports); 708 if (canUseForConversion(from, convertFrom)) { 709 HashMap<String, MethodDescription> conversion = 710 mStore.conversionMethods.get(fromClassName); 711 for (String toClassName : conversion.keySet()) { 712 try { 713 ModelClass convertTo = mClassAnalyzer.findClass(toClassName, 714 imports); 715 if (canUseForConversion(convertTo, to)) { 716 return conversion.get(toClassName); 717 } 718 } catch (Exception e) { 719 L.d(e, "Unknown class: %s", toClassName); 720 } 721 } 722 } 723 } catch (Exception e) { 724 L.d(e, "Unknown class: %s", fromClassName); 725 } 726 } 727 } 728 return null; 729 } 730 731 private boolean canUseForConversion(ModelClass from, ModelClass to) { 732 if (from.isIncomplete() || to.isIncomplete()) { 733 from = from.erasure(); 734 to = to.erasure(); 735 } 736 return from.equals(to) || ModelMethod.isBoxingConversion(from, to) || 737 to.isAssignableFrom(from); 738 } 739 740 private static void merge(IntermediateV1 store, Intermediate dumpStore) { 741 IntermediateV1 intermediateV1 = (IntermediateV1) dumpStore.upgrade(); 742 merge(store.adapterMethods, intermediateV1.adapterMethods); 743 merge(store.renamedMethods, intermediateV1.renamedMethods); 744 merge(store.conversionMethods, intermediateV1.conversionMethods); 745 store.multiValueAdapters.putAll(intermediateV1.multiValueAdapters); 746 store.untaggableTypes.putAll(intermediateV1.untaggableTypes); 747 } 748 749 private static <K, V> void merge(HashMap<K, HashMap<V, MethodDescription>> first, 750 HashMap<K, HashMap<V, MethodDescription>> second) { 751 for (K key : second.keySet()) { 752 HashMap<V, MethodDescription> firstVals = first.get(key); 753 HashMap<V, MethodDescription> secondVals = second.get(key); 754 if (firstVals == null) { 755 first.put(key, secondVals); 756 } else { 757 for (V key2 : secondVals.keySet()) { 758 if (!firstVals.containsKey(key2)) { 759 firstVals.put(key2, secondVals.get(key2)); 760 } 761 } 762 } 763 } 764 } 765 766 private static String createAdapterCall(MethodDescription adapter, String bindingAdapterCall, 767 String componentExpression, String viewExpression, String... args) { 768 StringBuilder sb = new StringBuilder(); 769 770 if (adapter.isStatic) { 771 sb.append(adapter.type); 772 } else { 773 sb.append(componentExpression).append('.').append(bindingAdapterCall); 774 } 775 sb.append('.').append(adapter.method).append('('); 776 if (adapter.componentClass != null) { 777 if (!"DataBindingComponent".equals(adapter.componentClass)) { 778 sb.append('(').append(adapter.componentClass).append(") "); 779 } 780 sb.append(componentExpression).append(", "); 781 } 782 sb.append(viewExpression); 783 for (String arg: args) { 784 sb.append(", ").append(arg); 785 } 786 sb.append(')'); 787 return sb.toString(); 788 } 789 790 private static class MultiValueAdapterKey implements Serializable { 791 private static final long serialVersionUID = 1; 792 793 public final String viewType; 794 795 public final String[] attributes; 796 797 public final String[] parameterTypes; 798 799 public final boolean requireAll; 800 801 public final TreeMap<String, Integer> attributeIndices = new TreeMap<String, Integer>(); 802 803 public MultiValueAdapterKey(ProcessingEnvironment processingEnv, 804 ExecutableElement method, String[] attributes, boolean takesComponent, 805 boolean requireAll) { 806 this.attributes = stripAttributes(attributes); 807 this.requireAll = requireAll; 808 List<? extends VariableElement> parameters = method.getParameters(); 809 final int argStart = 1 + (takesComponent ? 1 : 0); 810 this.viewType = getQualifiedName(eraseType(processingEnv, 811 parameters.get(argStart - 1).asType())); 812 this.parameterTypes = new String[parameters.size() - argStart]; 813 for (int i = 0; i < attributes.length; i++) { 814 TypeMirror typeMirror = eraseType(processingEnv, 815 parameters.get(i + argStart).asType()); 816 this.parameterTypes[i] = getQualifiedName(typeMirror); 817 attributeIndices.put(this.attributes[i], i); 818 } 819 } 820 821 @Override 822 public boolean equals(Object obj) { 823 if (!(obj instanceof MultiValueAdapterKey)) { 824 return false; 825 } 826 final MultiValueAdapterKey that = (MultiValueAdapterKey) obj; 827 if (!this.viewType.equals(that.viewType) || 828 this.attributes.length != that.attributes.length || 829 !this.attributeIndices.keySet().equals(that.attributeIndices.keySet())) { 830 return false; 831 } 832 833 for (int i = 0; i < this.attributes.length; i++) { 834 final int thatIndex = that.attributeIndices.get(this.attributes[i]); 835 final String thisParameter = parameterTypes[i]; 836 final String thatParameter = that.parameterTypes[thatIndex]; 837 if (!thisParameter.equals(thatParameter)) { 838 return false; 839 } 840 } 841 return true; 842 } 843 844 @Override 845 public int hashCode() { 846 return mergedHashCode(viewType, attributeIndices.keySet()); 847 } 848 } 849 850 private static int mergedHashCode(Object... objects) { 851 return Arrays.hashCode(objects); 852 } 853 854 private static class MethodDescription implements Serializable { 855 856 private static final long serialVersionUID = 1; 857 858 public final String type; 859 860 public final String method; 861 862 public final boolean requiresOldValue; 863 864 public final boolean isStatic; 865 866 public final String componentClass; 867 868 public MethodDescription(String type, String method) { 869 this.type = type; 870 this.method = method; 871 this.requiresOldValue = false; 872 this.isStatic = true; 873 this.componentClass = null; 874 L.d("BINARY created method desc 1 %s %s", type, method ); 875 } 876 877 public MethodDescription(ExecutableElement method, int numAttributes, 878 boolean takesComponent) { 879 TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); 880 this.type = enclosingClass.getQualifiedName().toString(); 881 this.method = method.getSimpleName().toString(); 882 final int argStart = 1 + (takesComponent ? 1 : 0); 883 this.requiresOldValue = method.getParameters().size() - argStart == numAttributes * 2; 884 this.isStatic = method.getModifiers().contains(Modifier.STATIC); 885 this.componentClass = takesComponent 886 ? getQualifiedName(method.getParameters().get(0).asType()) 887 : null; 888 889 L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); 890 } 891 892 @Override 893 public boolean equals(Object obj) { 894 if (obj instanceof MethodDescription) { 895 MethodDescription that = (MethodDescription) obj; 896 return that.type.equals(this.type) && that.method.equals(this.method); 897 } else { 898 return false; 899 } 900 } 901 902 @Override 903 public int hashCode() { 904 return mergedHashCode(type, method); 905 } 906 907 @Override 908 public String toString() { 909 return type + "." + method + "()"; 910 } 911 } 912 913 private static class AccessorKey implements Serializable { 914 915 private static final long serialVersionUID = 1; 916 917 public final String viewType; 918 919 public final String valueType; 920 921 public AccessorKey(String viewType, String valueType) { 922 this.viewType = viewType; 923 this.valueType = valueType; 924 } 925 926 @Override 927 public int hashCode() { 928 return mergedHashCode(viewType, valueType); 929 } 930 931 @Override 932 public boolean equals(Object obj) { 933 if (obj instanceof AccessorKey) { 934 AccessorKey that = (AccessorKey) obj; 935 return viewType.equals(that.valueType) && valueType.equals(that.valueType); 936 } else { 937 return false; 938 } 939 } 940 941 @Override 942 public String toString() { 943 return "AK(" + viewType + ", " + valueType + ")"; 944 } 945 } 946 947 private interface Intermediate extends Serializable { 948 Intermediate upgrade(); 949 } 950 951 private static class IntermediateV1 implements Serializable, Intermediate { 952 private static final long serialVersionUID = 1; 953 public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = 954 new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); 955 public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = 956 new HashMap<String, HashMap<String, MethodDescription>>(); 957 public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = 958 new HashMap<String, HashMap<String, MethodDescription>>(); 959 public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); 960 public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters = 961 new HashMap<MultiValueAdapterKey, MethodDescription>(); 962 963 public IntermediateV1() { 964 } 965 966 @Override 967 public Intermediate upgrade() { 968 return this; 969 } 970 } 971 972 public static class DummySetter extends SetterCall { 973 private String mMethodName; 974 975 public DummySetter(String methodName) { 976 mMethodName = methodName; 977 } 978 979 @Override 980 public String toJavaInternal(String componentExpression, String viewExpression, 981 String valueExpression) { 982 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 983 } 984 985 @Override 986 public String toJavaInternal(String componentExpression, String viewExpression, 987 String oldValue, String valueExpression) { 988 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 989 } 990 991 @Override 992 public int getMinApi() { 993 return 1; 994 } 995 996 @Override 997 public boolean requiresOldValue() { 998 return false; 999 } 1000 1001 @Override 1002 public ModelClass[] getParameterTypes() { 1003 return new ModelClass[] { 1004 ModelAnalyzer.getInstance().findClass(Object.class) 1005 }; 1006 } 1007 1008 @Override 1009 public String getBindingAdapterInstanceClass() { 1010 return null; 1011 } 1012 1013 @Override 1014 public void setBindingAdapterCall(String method) { 1015 } 1016 } 1017 1018 public static class AdapterSetter extends SetterCall { 1019 final MethodDescription mAdapter; 1020 final ModelClass mParameterType; 1021 String mBindingAdapterCall; 1022 1023 public AdapterSetter(MethodDescription adapter, ModelClass parameterType) { 1024 mAdapter = adapter; 1025 mParameterType = parameterType; 1026 } 1027 1028 @Override 1029 public String toJavaInternal(String componentExpression, String viewExpression, 1030 String valueExpression) { 1031 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1032 viewExpression, mCastString + valueExpression); 1033 } 1034 1035 @Override 1036 protected String toJavaInternal(String componentExpression, String viewExpression, 1037 String oldValue, String valueExpression) { 1038 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1039 viewExpression, mCastString + oldValue, mCastString + valueExpression); 1040 } 1041 1042 @Override 1043 public int getMinApi() { 1044 return 1; 1045 } 1046 1047 @Override 1048 public boolean requiresOldValue() { 1049 return mAdapter.requiresOldValue; 1050 } 1051 1052 @Override 1053 public ModelClass[] getParameterTypes() { 1054 return new ModelClass[] { mParameterType }; 1055 } 1056 1057 @Override 1058 public String getBindingAdapterInstanceClass() { 1059 return mAdapter.isStatic ? null : mAdapter.type; 1060 } 1061 1062 @Override 1063 public void setBindingAdapterCall(String method) { 1064 mBindingAdapterCall = method; 1065 } 1066 } 1067 1068 public static class ModelMethodSetter extends SetterCall { 1069 final ModelMethod mModelMethod; 1070 1071 public ModelMethodSetter(ModelMethod modelMethod) { 1072 mModelMethod = modelMethod; 1073 } 1074 1075 @Override 1076 public String toJavaInternal(String componentExpression, String viewExpression, 1077 String valueExpression) { 1078 return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + 1079 valueExpression + ")"; 1080 } 1081 1082 @Override 1083 protected String toJavaInternal(String componentExpression, String viewExpression, 1084 String oldValue, String valueExpression) { 1085 return viewExpression + "." + mModelMethod.getName() + "(" + 1086 mCastString + oldValue + ", " + mCastString + valueExpression + ")"; 1087 } 1088 1089 @Override 1090 public int getMinApi() { 1091 return mModelMethod.getMinApi(); 1092 } 1093 1094 @Override 1095 public boolean requiresOldValue() { 1096 return mModelMethod.getParameterTypes().length == 3; 1097 } 1098 1099 @Override 1100 public ModelClass[] getParameterTypes() { 1101 return new ModelClass[] { mModelMethod.getParameterTypes()[0] }; 1102 } 1103 1104 @Override 1105 public String getBindingAdapterInstanceClass() { 1106 return null; 1107 } 1108 1109 @Override 1110 public void setBindingAdapterCall(String method) { 1111 } 1112 } 1113 1114 public interface BindingSetterCall { 1115 String toJava(String componentExpression, String viewExpression, 1116 String... valueExpressions); 1117 1118 int getMinApi(); 1119 1120 boolean requiresOldValue(); 1121 1122 ModelClass[] getParameterTypes(); 1123 1124 String getBindingAdapterInstanceClass(); 1125 1126 void setBindingAdapterCall(String method); 1127 } 1128 1129 public static abstract class SetterCall implements BindingSetterCall { 1130 private MethodDescription mConverter; 1131 protected String mCastString = ""; 1132 1133 public SetterCall() { 1134 } 1135 1136 public void setConverter(MethodDescription converter) { 1137 mConverter = converter; 1138 } 1139 1140 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1141 String converted); 1142 1143 protected abstract String toJavaInternal(String componentExpression, String viewExpression, 1144 String oldValue, String converted); 1145 1146 @Override 1147 public final String toJava(String componentExpression, String viewExpression, 1148 String... valueExpression) { 1149 Preconditions.check(valueExpression.length == 2, "value expressions size must be 2"); 1150 if (requiresOldValue()) { 1151 return toJavaInternal(componentExpression, viewExpression, 1152 convertValue(valueExpression[0]), convertValue(valueExpression[1])); 1153 } else { 1154 return toJavaInternal(componentExpression, viewExpression, 1155 convertValue(valueExpression[1])); 1156 } 1157 } 1158 1159 protected String convertValue(String valueExpression) { 1160 return mConverter == null ? valueExpression : 1161 mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; 1162 } 1163 1164 abstract public int getMinApi(); 1165 1166 public void setCast(ModelClass castTo) { 1167 mCastString = "(" + castTo.toJavaCode() + ") "; 1168 } 1169 } 1170 1171 public static class MultiAttributeSetter implements BindingSetterCall { 1172 public final String[] attributes; 1173 private final MethodDescription mAdapter; 1174 private final MethodDescription[] mConverters; 1175 private final String[] mCasts; 1176 private final MultiValueAdapterKey mKey; 1177 private final boolean[] mSupplied; 1178 String mBindingAdapterCall; 1179 1180 public MultiAttributeSetter(MultiValueAdapterKey key, boolean[] supplied, 1181 MethodDescription adapter, MethodDescription[] converters, String[] casts) { 1182 Preconditions.check(converters != null && 1183 converters.length == key.attributes.length && 1184 casts != null && casts.length == key.attributes.length && 1185 supplied.length == key.attributes.length, 1186 "invalid arguments to create multi attr setter"); 1187 this.mAdapter = adapter; 1188 this.mConverters = converters; 1189 this.mCasts = casts; 1190 this.mKey = key; 1191 this.mSupplied = supplied; 1192 if (key.requireAll) { 1193 this.attributes = key.attributes; 1194 } else { 1195 int numSupplied = 0; 1196 for (int i = 0; i < mKey.attributes.length; i++) { 1197 if (supplied[i]) { 1198 numSupplied++; 1199 } 1200 } 1201 if (numSupplied == key.attributes.length) { 1202 this.attributes = key.attributes; 1203 } else { 1204 this.attributes = new String[numSupplied]; 1205 int attrIndex = 0; 1206 for (int i = 0; i < key.attributes.length; i++) { 1207 if (supplied[i]) { 1208 attributes[attrIndex++] = key.attributes[i]; 1209 } 1210 } 1211 } 1212 } 1213 } 1214 1215 @Override 1216 public final String toJava(String componentExpression, String viewExpression, 1217 String[] valueExpressions) { 1218 Preconditions.check(valueExpressions.length == attributes.length * 2, 1219 "MultiAttributeSetter needs %s items, received %s", 1220 Arrays.toString(attributes), Arrays.toString(valueExpressions)); 1221 final int numAttrs = mKey.attributes.length; 1222 String[] args = new String[numAttrs + (requiresOldValue() ? numAttrs : 0)]; 1223 1224 final int startIndex = mAdapter.requiresOldValue ? 0 : numAttrs; 1225 int attrIndex = mAdapter.requiresOldValue ? 0 : attributes.length; 1226 final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1227 StringBuilder argBuilder = new StringBuilder(); 1228 final int endIndex = numAttrs * 2; 1229 for (int i = startIndex; i < endIndex; i++) { 1230 argBuilder.setLength(0); 1231 if (!mSupplied[i % numAttrs]) { 1232 final String paramType = mKey.parameterTypes[i % numAttrs]; 1233 final String defaultValue = modelAnalyzer.getDefaultValue(paramType); 1234 argBuilder.append('(') 1235 .append(paramType) 1236 .append(')') 1237 .append(defaultValue); 1238 } else { 1239 if (mConverters[i % numAttrs] != null) { 1240 final MethodDescription converter = mConverters[i % numAttrs]; 1241 argBuilder.append(converter.type) 1242 .append('.') 1243 .append(converter.method) 1244 .append('(') 1245 .append(valueExpressions[attrIndex]) 1246 .append(')'); 1247 } else { 1248 if (mCasts[i % numAttrs] != null) { 1249 argBuilder.append('(') 1250 .append(mCasts[i % numAttrs]) 1251 .append(')'); 1252 } 1253 argBuilder.append(valueExpressions[attrIndex]); 1254 } 1255 attrIndex++; 1256 } 1257 args[i - startIndex] = argBuilder.toString(); 1258 } 1259 return createAdapterCall(mAdapter, mBindingAdapterCall, componentExpression, 1260 viewExpression, args); 1261 } 1262 1263 @Override 1264 public int getMinApi() { 1265 return 1; 1266 } 1267 1268 @Override 1269 public boolean requiresOldValue() { 1270 return mAdapter.requiresOldValue; 1271 } 1272 1273 @Override 1274 public ModelClass[] getParameterTypes() { 1275 ModelClass[] parameters = new ModelClass[attributes.length]; 1276 String[] paramTypeStrings = mKey.parameterTypes; 1277 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1278 int attrIndex = 0; 1279 for (int i = 0; i < mKey.attributes.length; i++) { 1280 if (mSupplied[i]) { 1281 parameters[attrIndex++] = modelAnalyzer.findClass(paramTypeStrings[i], null); 1282 } 1283 } 1284 return parameters; 1285 } 1286 1287 @Override 1288 public String getBindingAdapterInstanceClass() { 1289 return mAdapter.isStatic ? null : mAdapter.type; 1290 } 1291 1292 @Override 1293 public void setBindingAdapterCall(String method) { 1294 mBindingAdapterCall = method; 1295 } 1296 1297 @Override 1298 public String toString() { 1299 return "MultiAttributeSetter{" + 1300 "attributes=" + Arrays.toString(attributes) + 1301 ", mAdapter=" + mAdapter + 1302 ", mConverters=" + Arrays.toString(mConverters) + 1303 ", mCasts=" + Arrays.toString(mCasts) + 1304 ", mKey=" + mKey + 1305 '}'; 1306 } 1307 } 1308} 1309