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