SetterStore.java revision 716ba89e7f459f49ea85070d4710c1d79d715298
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.VariableElement; 46import javax.lang.model.type.ArrayType; 47import javax.lang.model.type.DeclaredType; 48import javax.lang.model.type.TypeKind; 49import javax.lang.model.type.TypeMirror; 50 51public class SetterStore { 52 53 public static final String SETTER_STORE_FILE_EXT = "-setter_store.bin"; 54 55 private static SetterStore sStore; 56 57 private final IntermediateV1 mStore; 58 private final ModelAnalyzer mClassAnalyzer; 59 60 private Comparator<MultiAttributeSetter> COMPARE_MULTI_ATTRIBUTE_SETTERS = 61 new Comparator<MultiAttributeSetter>() { 62 @Override 63 public int compare(MultiAttributeSetter o1, MultiAttributeSetter o2) { 64 if (o1.attributes.length != o2.attributes.length) { 65 return o2.attributes.length - o1.attributes.length; 66 } 67 ModelClass view1 = mClassAnalyzer.findClass(o1.mKey.viewType, null).erasure(); 68 ModelClass view2 = mClassAnalyzer.findClass(o2.mKey.viewType, null).erasure(); 69 if (!view1.equals(view2)) { 70 if (view1.isAssignableFrom(view2)) { 71 return 1; 72 } else { 73 return -1; 74 } 75 } 76 if (!o1.mKey.attributeIndices.keySet() 77 .equals(o2.mKey.attributeIndices.keySet())) { 78 // order by attribute name 79 Iterator<String> o1Keys = o1.mKey.attributeIndices.keySet().iterator(); 80 Iterator<String> o2Keys = o2.mKey.attributeIndices.keySet().iterator(); 81 while (o1Keys.hasNext()) { 82 String key1 = o1Keys.next(); 83 String key2 = o2Keys.next(); 84 int compare = key1.compareTo(key2); 85 if (compare != 0) { 86 return compare; 87 } 88 } 89 Preconditions.checkState(false, 90 "The sets don't match! That means the keys shouldn't match also"); 91 } 92 // Same view type. Same attributes 93 for (String attribute : o1.mKey.attributeIndices.keySet()) { 94 final int index1 = o1.mKey.attributeIndices.get(attribute); 95 final int index2 = o2.mKey.attributeIndices.get(attribute); 96 ModelClass type1 = mClassAnalyzer 97 .findClass(o1.mKey.parameterTypes[index1], null); 98 ModelClass type2 = mClassAnalyzer 99 .findClass(o2.mKey.parameterTypes[index2], null); 100 if (type1.equals(type2)) { 101 continue; 102 } 103 if (o1.mCasts[index1] != null) { 104 if (o2.mCasts[index2] == null) { 105 return 1; // o2 is better 106 } else { 107 continue; // both are casts 108 } 109 } else if (o2.mCasts[index2] != null) { 110 return -1; // o1 is better 111 } 112 if (o1.mConverters[index1] != null) { 113 if (o2.mConverters[index2] == null) { 114 return 1; // o2 is better 115 } else { 116 continue; // both are conversions 117 } 118 } else if (o2.mConverters[index2] != null) { 119 return -1; // o1 is better 120 } 121 122 if (type1.isPrimitive()) { 123 if (type2.isPrimitive()) { 124 int type1ConversionLevel = ModelMethod 125 .getImplicitConversionLevel(type1); 126 int type2ConversionLevel = ModelMethod 127 .getImplicitConversionLevel(type2); 128 return type2ConversionLevel - type1ConversionLevel; 129 } else { 130 // type1 is primitive and has higher priority 131 return -1; 132 } 133 } else if (type2.isPrimitive()) { 134 return 1; 135 } 136 if (type1.isAssignableFrom(type2)) { 137 return 1; 138 } else if (type2.isAssignableFrom(type1)) { 139 return -1; 140 } 141 } 142 // hmmm... same view type, same attributes, same parameter types... ? 143 return 0; 144 } 145 }; 146 147 private SetterStore(ModelAnalyzer modelAnalyzer, IntermediateV1 store) { 148 mClassAnalyzer = modelAnalyzer; 149 mStore = store; 150 } 151 152 public static SetterStore get(ModelAnalyzer modelAnalyzer) { 153 if (sStore == null) { 154 sStore = load(modelAnalyzer, SetterStore.class.getClassLoader()); 155 } 156 return sStore; 157 } 158 159 private static SetterStore load(ModelAnalyzer modelAnalyzer, ClassLoader classLoader) { 160 IntermediateV1 store = new IntermediateV1(); 161 List<Intermediate> previousStores = GenerationalClassUtil 162 .loadObjects(classLoader, 163 new GenerationalClassUtil.ExtensionFilter(SETTER_STORE_FILE_EXT)); 164 for (Intermediate intermediate : previousStores) { 165 merge(store, intermediate); 166 } 167 return new SetterStore(modelAnalyzer, store); 168 } 169 170 public void addRenamedMethod(String attribute, String declaringClass, String method, 171 TypeElement declaredOn) { 172 attribute = stripNamespace(attribute); 173 HashMap<String, MethodDescription> renamed = mStore.renamedMethods.get(attribute); 174 if (renamed == null) { 175 renamed = new HashMap<String, MethodDescription>(); 176 mStore.renamedMethods.put(attribute, renamed); 177 } 178 MethodDescription methodDescription = 179 new MethodDescription(declaredOn.getQualifiedName().toString(), method, false); 180 L.d("STORE addmethod desc %s", methodDescription); 181 renamed.put(declaringClass, methodDescription); 182 } 183 184 public void addBindingAdapter(ProcessingEnvironment processingEnv, String attribute, 185 ExecutableElement bindingMethod) { 186 attribute = stripNamespace(attribute); 187 L.d("STORE addBindingAdapter %s %s", attribute, bindingMethod); 188 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 189 190 if (adapters == null) { 191 adapters = new HashMap<AccessorKey, MethodDescription>(); 192 mStore.adapterMethods.put(attribute, adapters); 193 } 194 List<? extends VariableElement> parameters = bindingMethod.getParameters(); 195 TypeMirror viewType = eraseType(processingEnv, parameters.get(0).asType()); 196 String view = getQualifiedName(viewType); 197 TypeMirror parameterType = eraseType(processingEnv, parameters.get(1).asType()); 198 String value = getQualifiedName(parameterType); 199 200 AccessorKey key = new AccessorKey(view, value); 201 if (adapters.containsKey(key)) { 202 throw new IllegalArgumentException("Already exists!"); 203 } 204 205 adapters.put(key, new MethodDescription(bindingMethod, 1)); 206 } 207 208 private static TypeMirror eraseType(ProcessingEnvironment processingEnv, 209 TypeMirror typeMirror) { 210 if (hasTypeVar(typeMirror)) { 211 return processingEnv.getTypeUtils().erasure(typeMirror); 212 } else { 213 return typeMirror; 214 } 215 } 216 217 private static ModelClass eraseType(ModelClass modelClass) { 218 if (hasTypeVar(modelClass)) { 219 return modelClass.erasure(); 220 } else { 221 return modelClass; 222 } 223 } 224 225 private static boolean hasTypeVar(TypeMirror typeMirror) { 226 TypeKind kind = typeMirror.getKind(); 227 if (kind == TypeKind.TYPEVAR) { 228 return true; 229 } else if (kind == TypeKind.ARRAY) { 230 return hasTypeVar(((ArrayType) typeMirror).getComponentType()); 231 } else if (kind == TypeKind.DECLARED) { 232 DeclaredType declaredType = (DeclaredType) typeMirror; 233 List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); 234 if (typeArguments == null || typeArguments.isEmpty()) { 235 return false; 236 } 237 for (TypeMirror arg : typeArguments) { 238 if (hasTypeVar(arg)) { 239 return true; 240 } 241 } 242 return false; 243 } else { 244 return false; 245 } 246 } 247 248 private static boolean hasTypeVar(ModelClass type) { 249 if (type.isTypeVar()) { 250 return true; 251 } else if (type.isArray()) { 252 return hasTypeVar(type.getComponentType()); 253 } else { 254 List<ModelClass> typeArguments = type.getTypeArguments(); 255 if (typeArguments == null) { 256 return false; 257 } 258 for (ModelClass arg : typeArguments) { 259 if (hasTypeVar(arg)) { 260 return true; 261 } 262 } 263 return false; 264 } 265 } 266 267 public void addBindingAdapter(ProcessingEnvironment processingEnv, String[] attributes, 268 ExecutableElement bindingMethod) { 269 L.d("STORE add multi-value BindingAdapter %d %s", attributes.length, bindingMethod); 270 MultiValueAdapterKey key = new MultiValueAdapterKey(processingEnv, bindingMethod, 271 attributes); 272 MethodDescription methodDescription = new MethodDescription(bindingMethod, 273 attributes.length); 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, 1); 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 viewType = viewType.erasure(); 519 HashMap<AccessorKey, MethodDescription> adapters = mStore.adapterMethods.get(attribute); 520 ModelMethod bestSetterMethod = getBestSetter(viewType, valueType, attribute, imports); 521 ModelClass bestViewType = null; 522 ModelClass bestValueType = null; 523 if (bestSetterMethod != null) { 524 bestViewType = bestSetterMethod.getDeclaringClass(); 525 bestValueType = bestSetterMethod.getParameterTypes()[0]; 526 setterCall = new ModelMethodSetter(bestSetterMethod); 527 } 528 529 if (adapters != null) { 530 for (AccessorKey key : adapters.keySet()) { 531 try { 532 ModelClass adapterViewType = mClassAnalyzer 533 .findClass(key.viewType, imports).erasure(); 534 if (adapterViewType != null && adapterViewType.isAssignableFrom(viewType)) { 535 try { 536 L.d("setter parameter type is %s", key.valueType); 537 final ModelClass adapterValueType = eraseType(mClassAnalyzer 538 .findClass(key.valueType, imports)); 539 L.d("setter %s takes type %s, compared to %s", 540 adapters.get(key).method, adapterValueType.toJavaCode(), 541 valueType.toJavaCode()); 542 boolean isBetterView = bestViewType == null || 543 bestValueType.isAssignableFrom(adapterValueType); 544 if (isBetterParameter(valueType, adapterValueType, bestValueType, 545 isBetterView, imports)) { 546 bestViewType = adapterViewType; 547 bestValueType = adapterValueType; 548 MethodDescription adapter = adapters.get(key); 549 setterCall = new AdapterSetter(adapter, adapterValueType); 550 } 551 552 } catch (Exception e) { 553 L.e(e, "Unknown class: %s", key.valueType); 554 } 555 } 556 } catch (Exception e) { 557 L.e(e, "Unknown class: %s", key.viewType); 558 } 559 } 560 } 561 562 conversionMethod = getConversionMethod(valueType, bestValueType, imports); 563 if (valueType.isObject() && setterCall != null && bestValueType.isNullable()) { 564 setterCall.setCast(bestValueType); 565 } 566 } 567 if (setterCall == null) { 568 if (viewType != null && !viewType.isViewDataBinding()) { 569 return null; // no setter found!! 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.erasure().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 < attributes.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 final boolean requiresOldValue; 812 813 public MethodDescription(String type, String method, boolean requiresOldValue) { 814 this.type = type; 815 this.method = method; 816 this.requiresOldValue = requiresOldValue; 817 L.d("BINARY created method desc 1 %s %s", type, method ); 818 } 819 820 public MethodDescription(ExecutableElement method, int numAttributes) { 821 TypeElement enclosingClass = (TypeElement) method.getEnclosingElement(); 822 this.type = enclosingClass.getQualifiedName().toString(); 823 this.method = method.getSimpleName().toString(); 824 this.requiresOldValue = method.getParameters().size() == (numAttributes * 2) + 1; 825 L.d("BINARY created method desc 2 %s %s, %s", type, this.method, method); 826 } 827 828 @Override 829 public boolean equals(Object obj) { 830 if (obj instanceof MethodDescription) { 831 MethodDescription that = (MethodDescription) obj; 832 return that.type.equals(this.type) && that.method.equals(this.method); 833 } else { 834 return false; 835 } 836 } 837 838 @Override 839 public int hashCode() { 840 return Objects.hashCode(type, method); 841 } 842 843 @Override 844 public String toString() { 845 return type + "." + method + "()"; 846 } 847 } 848 849 private static class AccessorKey implements Serializable { 850 851 private static final long serialVersionUID = 1; 852 853 public final String viewType; 854 855 public final String valueType; 856 857 public AccessorKey(String viewType, String valueType) { 858 this.viewType = viewType; 859 this.valueType = valueType; 860 } 861 862 @Override 863 public int hashCode() { 864 return Objects.hashCode(viewType, valueType); 865 } 866 867 @Override 868 public boolean equals(Object obj) { 869 if (obj instanceof AccessorKey) { 870 AccessorKey that = (AccessorKey) obj; 871 return viewType.equals(that.valueType) && valueType.equals(that.valueType); 872 } else { 873 return false; 874 } 875 } 876 877 @Override 878 public String toString() { 879 return "AK(" + viewType + ", " + valueType + ")"; 880 } 881 } 882 883 private interface Intermediate extends Serializable { 884 Intermediate upgrade(); 885 } 886 887 private static class IntermediateV1 implements Serializable, Intermediate { 888 private static final long serialVersionUID = 1; 889 public final HashMap<String, HashMap<AccessorKey, MethodDescription>> adapterMethods = 890 new HashMap<String, HashMap<AccessorKey, MethodDescription>>(); 891 public final HashMap<String, HashMap<String, MethodDescription>> renamedMethods = 892 new HashMap<String, HashMap<String, MethodDescription>>(); 893 public final HashMap<String, HashMap<String, MethodDescription>> conversionMethods = 894 new HashMap<String, HashMap<String, MethodDescription>>(); 895 public final HashMap<String, String> untaggableTypes = new HashMap<String, String>(); 896 public final HashMap<MultiValueAdapterKey, MethodDescription> multiValueAdapters = 897 new HashMap<MultiValueAdapterKey, MethodDescription>(); 898 899 public IntermediateV1() { 900 } 901 902 @Override 903 public Intermediate upgrade() { 904 return this; 905 } 906 } 907 908 public static class DummySetter extends SetterCall { 909 private String mMethodName; 910 911 public DummySetter(String methodName) { 912 mMethodName = methodName; 913 } 914 915 @Override 916 public String toJavaInternal(String viewExpression, String valueExpression) { 917 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 918 } 919 920 @Override 921 public String toJavaInternal(String viewExpression, String oldValue, 922 String valueExpression) { 923 return viewExpression + "." + mMethodName + "(" + valueExpression + ")"; 924 } 925 926 @Override 927 public int getMinApi() { 928 return 1; 929 } 930 931 @Override 932 public boolean requiresOldValue() { 933 return false; 934 } 935 936 @Override 937 public ModelClass[] getParameterTypes() { 938 return new ModelClass[] { 939 ModelAnalyzer.getInstance().findClass(Object.class) 940 }; 941 } 942 } 943 944 public static class AdapterSetter extends SetterCall { 945 final MethodDescription mAdapter; 946 final ModelClass mParameterType; 947 948 public AdapterSetter(MethodDescription adapter, ModelClass parameterType) { 949 mAdapter = adapter; 950 mParameterType = parameterType; 951 } 952 953 @Override 954 public String toJavaInternal(String viewExpression, String valueExpression) { 955 return mAdapter.type + "." + mAdapter.method + "(" + viewExpression + ", " + 956 mCastString + valueExpression + ")"; 957 } 958 959 @Override 960 protected String toJavaInternal(String viewExpression, String oldValue, 961 String valueExpression) { 962 return mAdapter.type + "." + mAdapter.method + "(" + 963 mCastString + viewExpression + ", " + oldValue + ", " + 964 mCastString + valueExpression + ")"; 965 } 966 967 @Override 968 public int getMinApi() { 969 return 1; 970 } 971 972 @Override 973 public boolean requiresOldValue() { 974 return mAdapter.requiresOldValue; 975 } 976 977 @Override 978 public ModelClass[] getParameterTypes() { 979 return new ModelClass[] { mParameterType }; 980 } 981 } 982 983 public static class ModelMethodSetter extends SetterCall { 984 final ModelMethod mModelMethod; 985 986 public ModelMethodSetter(ModelMethod modelMethod) { 987 mModelMethod = modelMethod; 988 } 989 990 @Override 991 public String toJavaInternal(String viewExpression, String valueExpression) { 992 return viewExpression + "." + mModelMethod.getName() + "(" + mCastString + 993 valueExpression + ")"; 994 } 995 996 @Override 997 protected String toJavaInternal(String viewExpression, String oldValue, 998 String valueExpression) { 999 return viewExpression + "." + mModelMethod.getName() + "(" + 1000 mCastString + oldValue + ", " + mCastString + valueExpression + ")"; 1001 } 1002 1003 @Override 1004 public int getMinApi() { 1005 return mModelMethod.getMinApi(); 1006 } 1007 1008 @Override 1009 public boolean requiresOldValue() { 1010 return mModelMethod.getParameterTypes().length == 3; 1011 } 1012 1013 @Override 1014 public ModelClass[] getParameterTypes() { 1015 return new ModelClass[] { mModelMethod.getParameterTypes()[0] }; 1016 } 1017 } 1018 1019 public interface BindingSetterCall { 1020 String toJava(String viewExpression, String... valueExpressions); 1021 1022 int getMinApi(); 1023 1024 boolean requiresOldValue(); 1025 1026 ModelClass[] getParameterTypes(); 1027 } 1028 1029 public static abstract class SetterCall implements BindingSetterCall { 1030 private MethodDescription mConverter; 1031 protected String mCastString = ""; 1032 1033 public SetterCall() { 1034 } 1035 1036 public void setConverter(MethodDescription converter) { 1037 mConverter = converter; 1038 } 1039 1040 protected abstract String toJavaInternal(String viewExpression, String converted); 1041 1042 protected abstract String toJavaInternal(String viewExpression, String oldValue, 1043 String converted); 1044 1045 @Override 1046 public final String toJava(String viewExpression, String... valueExpression) { 1047 Preconditions.checkArgument(valueExpression.length == 2); 1048 if (requiresOldValue()) { 1049 return toJavaInternal(viewExpression, convertValue(valueExpression[0]), 1050 convertValue(valueExpression[1])); 1051 } else { 1052 return toJavaInternal(viewExpression, convertValue(valueExpression[1])); 1053 } 1054 } 1055 1056 protected String convertValue(String valueExpression) { 1057 return mConverter == null ? valueExpression : 1058 mConverter.type + "." + mConverter.method + "(" + valueExpression + ")"; 1059 } 1060 1061 abstract public int getMinApi(); 1062 1063 public void setCast(ModelClass castTo) { 1064 mCastString = "(" + castTo.toJavaCode() + ") "; 1065 } 1066 } 1067 1068 public static class MultiAttributeSetter implements BindingSetterCall { 1069 public final String[] attributes; 1070 private final MethodDescription mAdapter; 1071 private final MethodDescription[] mConverters; 1072 private final String[] mCasts; 1073 private final MultiValueAdapterKey mKey; 1074 1075 public MultiAttributeSetter(MultiValueAdapterKey key, String[] attributes, 1076 MethodDescription adapter, MethodDescription[] converters, String[] casts) { 1077 Preconditions.checkArgument(converters != null && 1078 converters.length == attributes.length && 1079 casts != null && casts.length == attributes.length); 1080 this.attributes = attributes; 1081 this.mAdapter = adapter; 1082 this.mConverters = converters; 1083 this.mCasts = casts; 1084 this.mKey = key; 1085 } 1086 1087 @Override 1088 public final String toJava(String viewExpression, String[] valueExpressions) { 1089 Preconditions.checkArgument(valueExpressions.length == attributes.length * 2, 1090 "MultiAttributeSetter needs %s items, received %s", 1091 Arrays.toString(attributes), Arrays.toString(valueExpressions)); 1092 StringBuilder sb = new StringBuilder(); 1093 sb.append(mAdapter.type) 1094 .append('.') 1095 .append(mAdapter.method) 1096 .append('(') 1097 .append(viewExpression); 1098 final int startIndex = mAdapter.requiresOldValue ? 0 : attributes.length; 1099 for (int i = startIndex; i < valueExpressions.length; i++) { 1100 sb.append(','); 1101 if (mConverters[i % attributes.length] != null) { 1102 final MethodDescription converter = mConverters[i % attributes.length]; 1103 sb.append(converter.type) 1104 .append('.') 1105 .append(converter.method) 1106 .append('(') 1107 .append(valueExpressions[i]) 1108 .append(')'); 1109 } else { 1110 if (mCasts[i % attributes.length] != null) { 1111 sb.append('(') 1112 .append(mCasts[i % attributes.length]) 1113 .append(')'); 1114 } 1115 sb.append(valueExpressions[i]); 1116 } 1117 } 1118 sb.append(')'); 1119 return sb.toString(); 1120 } 1121 1122 @Override 1123 public int getMinApi() { 1124 return 1; 1125 } 1126 1127 @Override 1128 public boolean requiresOldValue() { 1129 return mAdapter.requiresOldValue; 1130 } 1131 1132 @Override 1133 public ModelClass[] getParameterTypes() { 1134 ModelClass[] parameters = new ModelClass[attributes.length]; 1135 String[] paramTypeStrings = mKey.parameterTypes; 1136 ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance(); 1137 for (int i = 0; i < attributes.length; i++) { 1138 parameters[i] = modelAnalyzer.findClass(paramTypeStrings[i], null); 1139 } 1140 return parameters; 1141 } 1142 1143 @Override 1144 public String toString() { 1145 return "MultiAttributeSetter{" + 1146 "attributes=" + Arrays.toString(attributes) + 1147 ", mAdapter=" + mAdapter + 1148 ", mConverters=" + Arrays.toString(mConverters) + 1149 ", mCasts=" + Arrays.toString(mCasts) + 1150 ", mKey=" + mKey + 1151 '}'; 1152 } 1153 } 1154} 1155