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