1 /* 2 * Copyright 2021 Google LLC 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 * https://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 */ 16 package com.google.android.enterprise.connectedapps.processor; 17 18 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.hasCrossProfileCallbackAnnotation; 19 20 import com.google.android.enterprise.connectedapps.processor.containers.Context; 21 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileCallbackInterfaceInfo; 22 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileMethodInfo; 23 import com.google.android.enterprise.connectedapps.processor.containers.FutureWrapper; 24 import com.google.android.enterprise.connectedapps.processor.containers.ParcelableWrapper; 25 import com.google.android.enterprise.connectedapps.processor.containers.Type; 26 import com.google.auto.value.AutoValue; 27 import com.google.common.collect.ImmutableCollection; 28 import com.google.common.collect.ImmutableMap; 29 import com.squareup.javapoet.ClassName; 30 import com.squareup.javapoet.CodeBlock; 31 import java.util.Collection; 32 import java.util.LinkedHashMap; 33 import java.util.Map; 34 import java.util.Objects; 35 import java.util.Optional; 36 import javax.lang.model.element.ExecutableElement; 37 import javax.lang.model.element.TypeElement; 38 import javax.lang.model.element.VariableElement; 39 import javax.lang.model.type.TypeKind; 40 import javax.lang.model.type.TypeMirror; 41 import javax.lang.model.util.Elements; 42 import javax.lang.model.util.Types; 43 import org.checkerframework.checker.nullness.qual.Nullable; 44 45 /** 46 * Utility methods for generating code related to valid types for use with the Connected Apps SDK. 47 */ 48 public final class SupportedTypes { 49 50 @Override toString()51 public String toString() { 52 return "SupportedTypes{" + 53 "usableTypes=" + usableTypes + 54 '}'; 55 } 56 57 @Override equals(Object o)58 public boolean equals(Object o) { 59 if (this == o) { 60 return true; 61 } 62 if (o == null || getClass() != o.getClass()) { 63 return false; 64 } 65 SupportedTypes that = (SupportedTypes) o; 66 return usableTypes.equals(that.usableTypes); 67 } 68 69 @Override hashCode()70 public int hashCode() { 71 return Objects.hash(usableTypes); 72 } 73 74 /** Record of the current context for type checking. */ 75 @AutoValue 76 public abstract static class TypeCheckContext { 77 78 /** True if we are checking inside a generic type or an array. */ isWrapped()79 public abstract boolean isWrapped(); 80 isOnCrossProfileCallbackInterface()81 public abstract boolean isOnCrossProfileCallbackInterface(); 82 toBuilder()83 public abstract Builder toBuilder(); 84 create()85 public static TypeCheckContext create() { 86 return new AutoValue_SupportedTypes_TypeCheckContext.Builder() 87 .setWrapped(false) 88 .setOnCrossProfileCallbackInterface(false) 89 .build(); 90 } 91 createForCrossProfileCallbackInterface()92 public static TypeCheckContext createForCrossProfileCallbackInterface() { 93 return new AutoValue_SupportedTypes_TypeCheckContext.Builder() 94 .setWrapped(false) 95 .setOnCrossProfileCallbackInterface(true) 96 .build(); 97 } 98 99 @AutoValue.Builder 100 abstract static class Builder { setWrapped(boolean wrapped)101 abstract Builder setWrapped(boolean wrapped); 102 setOnCrossProfileCallbackInterface(boolean onCrossProfileCallbackInterface)103 abstract Builder setOnCrossProfileCallbackInterface(boolean onCrossProfileCallbackInterface); 104 build()105 abstract TypeCheckContext build(); 106 } 107 } 108 109 private final ImmutableMap<String, Type> usableTypes; 110 isFuture(TypeMirror type)111 public boolean isFuture(TypeMirror type) { 112 Type supportedType = get(type); 113 return supportedType != null && supportedType.isFuture(); 114 } 115 isValidReturnType(TypeMirror type)116 boolean isValidReturnType(TypeMirror type) { 117 // default behaviour is to check generics 118 return isValidReturnType(type, TypeCheckContext.create(), /* checkGenerics= */ true); 119 } 120 isValidReturnType(TypeMirror type, TypeCheckContext context)121 boolean isValidReturnType(TypeMirror type, TypeCheckContext context) { 122 // default behaviour is to check generics 123 return isValidReturnType(type, context, /* checkGenerics= */ true); 124 } 125 isValidReturnType(TypeMirror type, boolean checkGenerics)126 boolean isValidReturnType(TypeMirror type, boolean checkGenerics) { 127 return isValidReturnType(type, TypeCheckContext.create(), checkGenerics); 128 } 129 isValidReturnType( TypeMirror type, TypeCheckContext context, boolean checkGenerics)130 private boolean isValidReturnType( 131 TypeMirror type, TypeCheckContext context, boolean checkGenerics) { 132 133 if (TypeUtils.isArray(type)) { 134 TypeMirror wrappedType = TypeUtils.extractTypeFromArray(type); 135 if (TypeUtils.isGeneric(wrappedType)) { 136 return false; // We don't support generic arrays 137 } 138 if (TypeUtils.isArray(wrappedType)) { 139 // We don't support non-primitive multidimensional arrays 140 return TypeUtils.isPrimitiveArray(wrappedType); 141 } 142 return isValidReturnType(wrappedType, context, checkGenerics); 143 } 144 145 return (TypeUtils.isGeneric(type) && checkGenerics) 146 ? isValidGenericReturnType(type, context) 147 : isValidReturnType(get(type), context); 148 } 149 isValidReturnType(@ullable Type supportedType, TypeCheckContext context)150 private static boolean isValidReturnType(@Nullable Type supportedType, TypeCheckContext context) { 151 if (supportedType == null) { 152 return false; 153 } 154 155 if (context.isWrapped() && !supportedType.isSupportedInsideWrapper()) { 156 return false; 157 } 158 159 return supportedType.isAcceptableReturnType(); 160 } 161 isValidGenericReturnType(TypeMirror type, TypeCheckContext context)162 private boolean isValidGenericReturnType(TypeMirror type, TypeCheckContext context) { 163 TypeMirror genericType = TypeUtils.removeTypeArguments(type); 164 Type supportedType = get(genericType); 165 166 if (supportedType == null) { 167 return false; 168 } 169 170 if (!supportedType.isSupportedWithAnyGenericType()) { 171 // We need to recursively check all type arguments 172 for (TypeMirror typeArgument : TypeUtils.extractTypeArguments(type)) { 173 if (!isValidReturnType(typeArgument, context.toBuilder().setWrapped(true).build())) { 174 return false; 175 } 176 } 177 } 178 179 return isValidReturnType(supportedType, context); 180 } 181 182 /** 183 * Returns true if this type is automatically resolved. 184 * 185 * <p>An automatically resolved type does not need to have a value provided by the developer at 186 * runtime, and should instead use the value of 187 * {@link #getAutomaticallyResolvedReplacement(TypeMirror)}. 188 */ isAutomaticallyResolved(TypeMirror type)189 public boolean isAutomaticallyResolved(TypeMirror type) { 190 Type supportedType = get(type); 191 return supportedType != null && supportedType.getAutomaticallyResolvedReplacement().isPresent(); 192 } 193 getAutomaticallyResolvedReplacement(TypeMirror type)194 public String getAutomaticallyResolvedReplacement(TypeMirror type) { 195 Type supportedType = get(type); 196 return supportedType.getAutomaticallyResolvedReplacement().get(); 197 } 198 isValidParameterType(TypeMirror type)199 boolean isValidParameterType(TypeMirror type) { 200 // default behaviour is to check generics 201 return isValidParameterType(type, TypeCheckContext.create(), /* checkGenerics= */ true); 202 } 203 isValidParameterType(TypeMirror type, TypeCheckContext context)204 boolean isValidParameterType(TypeMirror type, TypeCheckContext context) { 205 // default behaviour is to check generics 206 return isValidParameterType(type, context, /* checkGenerics= */ true); 207 } 208 isValidParameterType(TypeMirror type, boolean checkGenerics)209 boolean isValidParameterType(TypeMirror type, boolean checkGenerics) { 210 return isValidParameterType(type, TypeCheckContext.create(), checkGenerics); 211 } 212 isValidParameterType(TypeMirror type, TypeCheckContext context, boolean checkGenerics)213 boolean isValidParameterType(TypeMirror type, TypeCheckContext context, boolean checkGenerics) { 214 if (TypeUtils.isArray(type)) { 215 TypeMirror wrappedType = TypeUtils.extractTypeFromArray(type); 216 if (TypeUtils.isGeneric(wrappedType)) { 217 return false; // We don't support generic arrays 218 } 219 if (TypeUtils.isArray(wrappedType)) { 220 // We don't support non-primitive multidimensional arrays 221 return TypeUtils.isPrimitiveArray(wrappedType); 222 } 223 return isValidParameterType( 224 wrappedType, context.toBuilder().setWrapped(true).build(), checkGenerics); 225 } 226 227 Type supportedType = get(TypeUtils.removeTypeArguments(type)); 228 if (context.isOnCrossProfileCallbackInterface()) { 229 if (supportedType != null && !supportedType.isSupportedInsideCrossProfileCallback()) { 230 return false; 231 } 232 } 233 234 if (context.isWrapped()) { 235 if (supportedType == null || !supportedType.isSupportedInsideWrapper()) { 236 return false; 237 } 238 } 239 240 return (TypeUtils.isGeneric(type) && checkGenerics) 241 ? isValidGenericParameterType(type, context) 242 : isValidParameterType(get(type)); 243 } 244 isValidParameterType(Type supportedType)245 private static boolean isValidParameterType(Type supportedType) { 246 return supportedType != null && supportedType.isAcceptableParameterType(); 247 } 248 isValidGenericParameterType(TypeMirror type, TypeCheckContext context)249 private boolean isValidGenericParameterType(TypeMirror type, TypeCheckContext context) { 250 TypeMirror genericType = TypeUtils.removeTypeArguments(type); 251 Type supportedType = get(genericType); 252 253 if (supportedType == null) { 254 return false; 255 } 256 257 if (!supportedType.isSupportedWithAnyGenericType()) { 258 // We need to recursively check all type arguments 259 for (TypeMirror typeArgument : TypeUtils.extractTypeArguments(type)) { 260 if (!isValidParameterType(typeArgument, context.toBuilder().setWrapped(true).build())) { 261 return false; 262 } 263 } 264 } 265 266 return isValidParameterType(supportedType); 267 } 268 usableTypes()269 ImmutableCollection<Type> usableTypes() { 270 return usableTypes.values(); 271 } 272 get(TypeMirror type)273 private Type get(TypeMirror type) { 274 return usableTypes.getOrDefault(type.toString(), null); 275 } 276 generatePutIntoBundleCode( String bundleName, Type type, String keyCode, String valueCode)277 CodeBlock generatePutIntoBundleCode( 278 String bundleName, Type type, String keyCode, String valueCode) { 279 280 if (type.getPutIntoBundleCode().isPresent()) { 281 return CodeBlock.of(type.getPutIntoBundleCode().get(), bundleName, keyCode, valueCode); 282 } 283 284 throw new IllegalArgumentException( 285 String.format("%s can not put into bundle", type.getQualifiedName())); 286 } 287 generateGetFromBundleCode(String bundleName, Type type, String keyCode)288 CodeBlock generateGetFromBundleCode(String bundleName, Type type, String keyCode) { 289 if (type.getGetFromBundleCode().isPresent()) { 290 return CodeBlock.of(type.getGetFromBundleCode().get(), bundleName, keyCode); 291 } 292 293 throw new IllegalArgumentException( 294 String.format("%s can not get from bundle", type.getQualifiedName())); 295 } 296 generateWriteToParcelCode(String parcelName, Type type, String valueCode)297 CodeBlock generateWriteToParcelCode(String parcelName, Type type, String valueCode) { 298 if (type.getWriteToParcelCode().isPresent()) { 299 return CodeBlock.of(type.getWriteToParcelCode().get(), parcelName, valueCode); 300 } 301 302 throw new IllegalArgumentException( 303 String.format("%s can not write to parcel", type.getQualifiedName())); 304 } 305 generateReadFromParcelCode(String parcelName, Type type)306 CodeBlock generateReadFromParcelCode(String parcelName, Type type) { 307 if (type.getReadFromParcelCode().isPresent()) { 308 return CodeBlock.of(type.getReadFromParcelCode().get(), parcelName); 309 } 310 311 throw new IllegalArgumentException( 312 String.format("%s can not read from parcel", type.getQualifiedName())); 313 } 314 getType(TypeMirror type)315 public Type getType(TypeMirror type) { 316 String typeName = type.toString(); 317 if (!usableTypes.containsKey(typeName)) { 318 throw new IllegalArgumentException(String.format("%s type not loaded", type)); 319 } 320 321 return get(type); 322 } 323 SupportedTypes(Map<String, Type> usableTypes)324 private SupportedTypes(Map<String, Type> usableTypes) { 325 this.usableTypes = ImmutableMap.copyOf(usableTypes); 326 } 327 createFromMethods( Context context, Collection<ParcelableWrapper> parcelableWrappers, Collection<FutureWrapper> futureWrappers, Collection<ExecutableElement> methods)328 public static SupportedTypes createFromMethods( 329 Context context, 330 Collection<ParcelableWrapper> parcelableWrappers, 331 Collection<FutureWrapper> futureWrappers, 332 Collection<ExecutableElement> methods) { 333 Map<String, Type> usableTypes = new LinkedHashMap<>(); 334 335 addDefaultTypes(context, usableTypes); 336 addParcelableWrapperTypes(usableTypes, parcelableWrappers); 337 addFutureWrapperTypes(usableTypes, futureWrappers); 338 addSupportForUsedTypes(context, usableTypes, methods); 339 340 return new SupportedTypes(usableTypes); 341 } 342 addSupportForUsedTypes( Context context, Map<String, Type> usableTypes, Collection<ExecutableElement> methods)343 private static void addSupportForUsedTypes( 344 Context context, Map<String, Type> usableTypes, Collection<ExecutableElement> methods) { 345 for (ExecutableElement method : methods) { 346 addSupportForUsedType(context, usableTypes, method.getReturnType()); 347 348 for (VariableElement parameter : method.getParameters()) { 349 addSupportForUsedType(context, usableTypes, parameter.asType()); 350 } 351 } 352 } 353 addSupportForUsedType( Context context, Map<String, Type> usableTypes, TypeMirror type)354 private static void addSupportForUsedType( 355 Context context, Map<String, Type> usableTypes, TypeMirror type) { 356 if (TypeUtils.isArray(type)) { 357 if (!TypeUtils.isPrimitiveArray(type)) { 358 addSupportForUsedType(context, usableTypes, TypeUtils.extractTypeFromArray(type)); 359 type = 360 context 361 .types() 362 .getArrayType(context.elements().getTypeElement("java.lang.Object").asType()); 363 } 364 } 365 366 if (TypeUtils.isGeneric(type)) { 367 addSupportForGenericUsedType(context, usableTypes, type); 368 return; 369 } 370 Optional<Type> optionalSupportedType = getSupportedType(context, usableTypes, type); 371 if (!optionalSupportedType.isPresent()) { 372 // The type isn't supported 373 return; 374 } 375 376 Type supportedType = optionalSupportedType.get(); 377 378 // We don't support generic callbacks so any callback interfaces can be picked up here 379 if (supportedType.isCrossProfileCallbackInterface()) { 380 for (TypeMirror typeMirror : 381 supportedType.getCrossProfileCallbackInterface().get().argumentTypes()) { 382 addSupportForUsedType(context, usableTypes, typeMirror); 383 } 384 } 385 386 addUsableType(usableTypes, supportedType); 387 } 388 addSupportForGenericUsedType( Context context, Map<String, Type> usableTypes, TypeMirror type)389 private static void addSupportForGenericUsedType( 390 Context context, Map<String, Type> usableTypes, TypeMirror type) { 391 TypeMirror genericType = TypeUtils.removeTypeArguments(type); 392 393 Optional<Type> optionalSupportedType = getSupportedType(context, usableTypes, genericType); 394 if (!optionalSupportedType.isPresent()) { 395 // The base type isn't supported 396 return; 397 } 398 399 Type supportedType = optionalSupportedType.get(); 400 401 addUsableType(usableTypes, supportedType); 402 403 if (!supportedType.isSupportedWithAnyGenericType()) { 404 for (TypeMirror typeArgument : TypeUtils.extractTypeArguments(type)) { 405 addSupportForUsedType(context, usableTypes, typeArgument); 406 } 407 } 408 } 409 getSupportedType( Context context, Map<String, Type> usableTypes, TypeMirror type)410 private static Optional<Type> getSupportedType( 411 Context context, Map<String, Type> usableTypes, TypeMirror type) { 412 Elements elements = context.elements(); 413 Types types = context.types(); 414 if (usableTypes.containsKey(type.toString())) { 415 return Optional.of(usableTypes.get(type.toString())); 416 } 417 418 TypeMirror parcelable = elements.getTypeElement("android.os.Parcelable").asType(); 419 if (types.isAssignable(type, parcelable)) { 420 return Optional.of(createParcelableType(type)); 421 } 422 423 TypeMirror serializable = elements.getTypeElement("java.io.Serializable").asType(); 424 if (types.isAssignable(type, serializable)) { 425 return Optional.of(createSerializableType(type)); 426 } 427 428 TypeElement element = elements.getTypeElement(type.toString()); 429 430 if (element != null && hasCrossProfileCallbackAnnotation(element)) { 431 return Optional.of(createCrossProfileCallbackType(element)); 432 } 433 434 // We don't support this type - it will error in a later stage 435 return Optional.empty(); 436 } 437 createCrossProfileCallbackType(TypeElement type)438 private static Type createCrossProfileCallbackType(TypeElement type) { 439 return Type.builder() 440 .setTypeMirror(type.asType()) 441 .setAcceptableReturnType(false) 442 .setAcceptableParameterType(true) 443 .setSupportedInsideWrapper(false) 444 .setSupportedInsideCrossProfileCallback(false) 445 .setCrossProfileCallbackInterface(CrossProfileCallbackInterfaceInfo.create(type)) 446 .build(); 447 } 448 createParcelableType(TypeMirror typeMirror)449 private static Type createParcelableType(TypeMirror typeMirror) { 450 return Type.builder() 451 .setTypeMirror(typeMirror) 452 .setAcceptableReturnType(true) 453 .setAcceptableParameterType(true) 454 .setPutIntoBundleCode("$L.putParcelable($L, $L)") 455 .setGetFromBundleCode("$L.getParcelable($L)") 456 .setWriteToParcelCode("$L.writeParcelable($L, flags)") 457 .setReadFromParcelCode("$L.readParcelable(Bundler.class.getClassLoader())") 458 // Parcelables must take care of their own generic types 459 .setSupportedWithAnyGenericType(true) 460 .build(); 461 } 462 createSerializableType(TypeMirror typeMirror)463 private static Type createSerializableType(TypeMirror typeMirror) { 464 return Type.builder() 465 .setTypeMirror(typeMirror) 466 .setAcceptableReturnType(true) 467 .setAcceptableParameterType(true) 468 .setPutIntoBundleCode("$L.putSerializable($L, $L)") 469 .setGetFromBundleCode("$L.getSerializable($L)") 470 .setWriteToParcelCode("$L.writeSerializable($L)") 471 .setReadFromParcelCode("$L.readSerializable()") 472 // Serializables must take care of their own generic types 473 .setSupportedWithAnyGenericType(true) 474 .build(); 475 } 476 477 /** Create a {@link Builder} to create a new {@link SupportedTypes} with modified entries. */ asBuilder()478 public Builder asBuilder() { 479 return new Builder(usableTypes); 480 } 481 addDefaultTypes(Context context, Map<String, Type> usableTypes)482 private static void addDefaultTypes(Context context, Map<String, Type> usableTypes) { 483 Elements elements = context.elements(); 484 Types types = context.types(); 485 addUsableType( 486 usableTypes, 487 Type.builder() 488 .setTypeMirror(types.getNoType(TypeKind.VOID)) 489 .setAcceptableReturnType(true) 490 .setGetFromBundleCode("null") 491 .setReadFromParcelCode("null") 492 .build()); 493 addUsableType( 494 usableTypes, 495 Type.builder() 496 .setTypeMirror(elements.getTypeElement("java.lang.Void").asType()) 497 .setAcceptableReturnType(true) 498 .setGetFromBundleCode("null") 499 .setReadFromParcelCode("null") 500 .build()); 501 addUsableType( 502 usableTypes, 503 Type.builder() 504 .setTypeMirror(elements.getTypeElement("java.lang.String").asType()) 505 .setAcceptableReturnType(true) 506 .setAcceptableParameterType(true) 507 .setPutIntoBundleCode("$L.putString($L, $L)") 508 .setGetFromBundleCode("$L.getString($L)") 509 .setWriteToParcelCode("$L.writeString($L)") 510 .setReadFromParcelCode("$L.readString()") 511 .build()); 512 addUsableType( 513 usableTypes, 514 Type.builder() 515 .setTypeMirror(elements.getTypeElement("java.lang.CharSequence").asType()) 516 .setAcceptableReturnType(true) 517 .setAcceptableParameterType(true) 518 .setPutIntoBundleCode("$1L.putString($2L, $3L == null ? null : String.valueOf($3L))") 519 .setGetFromBundleCode("$L.getString($L)") 520 .setWriteToParcelCode("$L.writeString(String.valueOf($L))") 521 .setReadFromParcelCode("$L.readString()") 522 .build()); 523 addUsableType( 524 usableTypes, 525 Type.builder() 526 .setTypeMirror(types.getPrimitiveType(TypeKind.BYTE)) 527 .setAcceptableReturnType(true) 528 .setAcceptableParameterType(true) 529 .setPutIntoBundleCode("$L.putByte($L, $L)") 530 .setGetFromBundleCode("$L.getByte($L)") 531 .setWriteToParcelCode("$L.writeByte($L)") 532 .setReadFromParcelCode("$L.readByte()") 533 .build()); 534 addUsableType( 535 usableTypes, 536 Type.builder() 537 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.BYTE)).asType()) 538 .setAcceptableReturnType(true) 539 .setAcceptableParameterType(true) 540 .setPutIntoBundleCode("$L.putByte($L, $L)") 541 .setGetFromBundleCode("$L.getByte($L)") 542 .setWriteToParcelCode("$L.writeByte($L)") 543 .setReadFromParcelCode("$L.readByte()") 544 .build()); 545 addUsableType( 546 usableTypes, 547 Type.builder() 548 .setTypeMirror(types.getPrimitiveType(TypeKind.SHORT)) 549 .setAcceptableReturnType(true) 550 .setAcceptableParameterType(true) 551 .setPutIntoBundleCode("$L.putShort($L, $L)") 552 .setGetFromBundleCode("$L.getShort($L)") 553 .setWriteToParcelCode("$L.writeInt($L)") 554 .setReadFromParcelCode("(short)$L.readInt()") 555 .build()); 556 addUsableType( 557 usableTypes, 558 Type.builder() 559 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.SHORT)).asType()) 560 .setAcceptableReturnType(true) 561 .setAcceptableParameterType(true) 562 .setPutIntoBundleCode("$L.putShort($L, $L)") 563 .setGetFromBundleCode("$L.getShort($L)") 564 .setWriteToParcelCode("$L.writeInt($L)") 565 .setReadFromParcelCode("(short)$L.readInt()") 566 .build()); 567 addUsableType( 568 usableTypes, 569 Type.builder() 570 .setTypeMirror(types.getPrimitiveType(TypeKind.INT)) 571 .setAcceptableReturnType(true) 572 .setAcceptableParameterType(true) 573 .setPutIntoBundleCode("$L.putInt($L, $L)") 574 .setGetFromBundleCode("$L.getInt($L)") 575 .setWriteToParcelCode("$L.writeInt($L)") 576 .setReadFromParcelCode("$L.readInt()") 577 .build()); 578 addUsableType( 579 usableTypes, 580 Type.builder() 581 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.INT)).asType()) 582 .setAcceptableReturnType(true) 583 .setAcceptableParameterType(true) 584 .setPutIntoBundleCode("$L.putInt($L, $L)") 585 .setGetFromBundleCode("$L.getInt($L)") 586 .setWriteToParcelCode("$L.writeInt($L)") 587 .setReadFromParcelCode("$L.readInt()") 588 .build()); 589 addUsableType( 590 usableTypes, 591 Type.builder() 592 .setTypeMirror(types.getPrimitiveType(TypeKind.LONG)) 593 .setAcceptableReturnType(true) 594 .setAcceptableParameterType(true) 595 .setPutIntoBundleCode("$L.putLong($L, $L)") 596 .setGetFromBundleCode("$L.getLong($L)") 597 .setWriteToParcelCode("$L.writeLong($L)") 598 .setReadFromParcelCode("$L.readLong()") 599 .build()); 600 addUsableType( 601 usableTypes, 602 Type.builder() 603 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.LONG)).asType()) 604 .setAcceptableReturnType(true) 605 .setAcceptableParameterType(true) 606 .setPutIntoBundleCode("$L.putLong($L, $L)") 607 .setGetFromBundleCode("$L.getLong($L)") 608 .setWriteToParcelCode("$L.writeLong($L)") 609 .setReadFromParcelCode("$L.readLong()") 610 .build()); 611 addUsableType( 612 usableTypes, 613 Type.builder() 614 .setTypeMirror(types.getPrimitiveType(TypeKind.FLOAT)) 615 .setAcceptableReturnType(true) 616 .setAcceptableParameterType(true) 617 .setPutIntoBundleCode("$L.putFloat($L, $L)") 618 .setGetFromBundleCode("$L.getFloat($L)") 619 .setWriteToParcelCode("$L.writeFloat($L)") 620 .setReadFromParcelCode("$L.readFloat()") 621 .build()); 622 addUsableType( 623 usableTypes, 624 Type.builder() 625 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.FLOAT)).asType()) 626 .setAcceptableReturnType(true) 627 .setAcceptableParameterType(true) 628 .setPutIntoBundleCode("$L.putFloat($L, $L)") 629 .setGetFromBundleCode("$L.getFloat($L)") 630 .setWriteToParcelCode("$L.writeFloat($L)") 631 .setReadFromParcelCode("$L.readFloat()") 632 .build()); 633 addUsableType( 634 usableTypes, 635 Type.builder() 636 .setTypeMirror(types.getPrimitiveType(TypeKind.DOUBLE)) 637 .setAcceptableReturnType(true) 638 .setAcceptableParameterType(true) 639 .setPutIntoBundleCode("$L.putDouble($L, $L)") 640 .setGetFromBundleCode("$L.getDouble($L)") 641 .setWriteToParcelCode("$L.writeDouble($L)") 642 .setReadFromParcelCode("$L.readDouble()") 643 .build()); 644 addUsableType( 645 usableTypes, 646 Type.builder() 647 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.DOUBLE)).asType()) 648 .setAcceptableReturnType(true) 649 .setAcceptableParameterType(true) 650 .setPutIntoBundleCode("$L.putDouble($L, $L)") 651 .setGetFromBundleCode("$L.getDouble($L)") 652 .setWriteToParcelCode("$L.writeDouble($L)") 653 .setReadFromParcelCode("$L.readDouble()") 654 .build()); 655 addUsableType( 656 usableTypes, 657 Type.builder() 658 .setTypeMirror(types.getPrimitiveType(TypeKind.CHAR)) 659 .setAcceptableReturnType(true) 660 .setAcceptableParameterType(true) 661 .setPutIntoBundleCode("$L.putChar($L, $L)") 662 .setGetFromBundleCode("$L.getChar($L)") 663 .setWriteToParcelCode("$L.writeInt($L)") 664 .setReadFromParcelCode("(char)$L.readInt()") 665 .build()); 666 addUsableType( 667 usableTypes, 668 Type.builder() 669 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.CHAR)).asType()) 670 .setAcceptableReturnType(true) 671 .setAcceptableParameterType(true) 672 .setPutIntoBundleCode("$L.putChar($L, $L)") 673 .setGetFromBundleCode("$L.getChar($L)") 674 .setWriteToParcelCode("$L.writeInt($L)") 675 .setReadFromParcelCode("(char)$L.readInt()") 676 .build()); 677 addUsableType( 678 usableTypes, 679 Type.builder() 680 .setTypeMirror(types.getPrimitiveType(TypeKind.BOOLEAN)) 681 .setAcceptableReturnType(true) 682 .setAcceptableParameterType(true) 683 .setPutIntoBundleCode("$L.putBoolean($L, $L)") 684 .setGetFromBundleCode("$L.getBoolean($L)") 685 .setWriteToParcelCode("$L.writeInt($L ? 1 : 0)") 686 .setReadFromParcelCode("($L.readInt() == 1)") 687 .build()); 688 addUsableType( 689 usableTypes, 690 Type.builder() 691 .setTypeMirror(types.boxedClass(types.getPrimitiveType(TypeKind.BOOLEAN)).asType()) 692 .setAcceptableReturnType(true) 693 .setAcceptableParameterType(true) 694 .setPutIntoBundleCode("$L.putBoolean($L, $L)") 695 .setGetFromBundleCode("$L.getBoolean($L)") 696 .setWriteToParcelCode("$L.writeInt($L ? 1 : 0)") 697 .setReadFromParcelCode("($L.readInt() == 1)") 698 .build()); 699 addUsableType( 700 usableTypes, 701 Type.builder() 702 .setTypeMirror(elements.getTypeElement("android.content.Context").asType()) 703 .setAcceptableParameterType(true) 704 .setAutomaticallyResolvedReplacement("context") 705 .setAcceptableReturnType(false) 706 .setSupportedInsideWrapper(false) 707 .setSupportedInsideCrossProfileCallback(false) 708 .build()); 709 710 addUsableType( 711 usableTypes, 712 Type.builder() 713 .setTypeMirror(elements.getTypeElement("android.os.Parcelable").asType()) 714 .setAcceptableReturnType(true) 715 .setAcceptableParameterType(true) 716 .setPutIntoBundleCode("$L.putParcelable($L, $L)") 717 .setGetFromBundleCode("$L.getParcelable($L)") 718 .setWriteToParcelCode("$L.writeParcelable($L, flags)") 719 .setReadFromParcelCode("$L.readParcelable(Bundler.class.getClassLoader())") 720 .build()); 721 722 //region **** Primitive Array Types **** 723 addUsableType( 724 usableTypes, 725 Type.builder() 726 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.BOOLEAN))) 727 .setAcceptableParameterType(true) 728 .setAcceptableReturnType(true) 729 .setPutIntoBundleCode("$L.putBooleanArray($L, $L)") 730 .setGetFromBundleCode("$L.getBooleanArray($L)") 731 .setWriteToParcelCode("$L.writeBooleanArray($L)") 732 .setReadFromParcelCode("$L.createBooleanArray()") 733 .build()); 734 addUsableType( 735 usableTypes, 736 Type.builder() 737 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.BYTE))) 738 .setAcceptableParameterType(true) 739 .setAcceptableReturnType(true) 740 .setPutIntoBundleCode("$L.putByteArray($L, $L)") 741 .setGetFromBundleCode("$L.getByteArray($L)") 742 .setWriteToParcelCode("$L.writeByteArray($L)") 743 .setReadFromParcelCode("$L.createByteArray()") 744 .build()); 745 addUsableType( 746 usableTypes, 747 Type.builder() 748 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.CHAR))) 749 .setAcceptableParameterType(true) 750 .setAcceptableReturnType(true) 751 .setPutIntoBundleCode("$L.putCharArray($L, $L)") 752 .setGetFromBundleCode("$L.getCharArray($L)") 753 .setWriteToParcelCode("$L.writeCharArray($L)") 754 .setReadFromParcelCode("$L.createCharArray()") 755 .build()); 756 addUsableType( 757 usableTypes, 758 Type.builder() 759 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.DOUBLE))) 760 .setAcceptableParameterType(true) 761 .setAcceptableReturnType(true) 762 .setPutIntoBundleCode("$L.putDoubleArray($L, $L)") 763 .setGetFromBundleCode("$L.getDoubleArray($L)") 764 .setWriteToParcelCode("$L.writeDoubleArray($L)") 765 .setReadFromParcelCode("$L.createDoubleArray()") 766 .build()); 767 addUsableType( 768 usableTypes, 769 Type.builder() 770 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.FLOAT))) 771 .setAcceptableParameterType(true) 772 .setAcceptableReturnType(true) 773 .setPutIntoBundleCode("$L.putFloatArray($L, $L)") 774 .setGetFromBundleCode("$L.getFloatArray($L)") 775 .setWriteToParcelCode("$L.writeFloatArray($L)") 776 .setReadFromParcelCode("$L.createFloatArray()") 777 .build()); 778 addUsableType( 779 usableTypes, 780 Type.builder() 781 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.INT))) 782 .setAcceptableParameterType(true) 783 .setAcceptableReturnType(true) 784 .setPutIntoBundleCode("$L.putIntArray($L, $L)") 785 .setGetFromBundleCode("$L.getIntArray($L)") 786 .setWriteToParcelCode("$L.writeIntArray($L)") 787 .setReadFromParcelCode("$L.createIntArray()") 788 .build()); 789 addUsableType( 790 usableTypes, 791 Type.builder() 792 .setTypeMirror(types.getArrayType(types.getPrimitiveType(TypeKind.LONG))) 793 .setAcceptableParameterType(true) 794 .setAcceptableReturnType(true) 795 .setPutIntoBundleCode("$L.putLongArray($L, $L)") 796 .setGetFromBundleCode("$L.getLongArray($L)") 797 .setWriteToParcelCode("$L.writeLongArray($L)") 798 .setReadFromParcelCode("$L.createLongArray()") 799 .build()); 800 //endregion **** Primitive Array Types **** 801 802 } 803 addUsableType(Map<String, Type> usableTypes, Type type)804 private static void addUsableType(Map<String, Type> usableTypes, Type type) { 805 usableTypes.put(type.getQualifiedName(), type); 806 } 807 addParcelableWrapperTypes( Map<String, Type> usableTypes, Collection<ParcelableWrapper> parcelableWrappers)808 private static void addParcelableWrapperTypes( 809 Map<String, Type> usableTypes, Collection<ParcelableWrapper> parcelableWrappers) { 810 for (ParcelableWrapper parcelableWrapper : parcelableWrappers) { 811 addParcelableWrapperType(usableTypes, parcelableWrapper); 812 } 813 } 814 addParcelableWrapperType( Map<String, Type> usableTypes, ParcelableWrapper parcelableWrapper)815 private static void addParcelableWrapperType( 816 Map<String, Type> usableTypes, ParcelableWrapper parcelableWrapper) { 817 String createParcelableCode = parcelableWrapper.wrapperClassName() + ".of(this, valueType, $L)"; 818 // "this" will be a Bundler as this code is only run within a Bundler 819 820 addUsableType( 821 usableTypes, 822 Type.builder() 823 .setTypeMirror(parcelableWrapper.wrappedType()) 824 .setAcceptableReturnType(true) 825 .setAcceptableParameterType(true) 826 .setPutIntoBundleCode("$L.putParcelable($L, " + createParcelableCode + ")") 827 .setGetFromBundleCode( 828 "((" + parcelableWrapper.wrapperClassName() + ") $L.getParcelable($L)).get()") 829 .setWriteToParcelCode("$L.writeParcelable(" + createParcelableCode + ", flags)") 830 .setReadFromParcelCode( 831 "((" 832 + parcelableWrapper.wrapperClassName() 833 + ") $L.readParcelable(Bundler.class.getClassLoader())).get()") 834 .setParcelableWrapper(parcelableWrapper) 835 .build()); 836 } 837 addFutureWrapperTypes( Map<String, Type> usableTypes, Collection<FutureWrapper> futureWrappers)838 private static void addFutureWrapperTypes( 839 Map<String, Type> usableTypes, Collection<FutureWrapper> futureWrappers) { 840 for (FutureWrapper futureWrapper : futureWrappers) { 841 addFutureWrapperType(usableTypes, futureWrapper); 842 } 843 } 844 addFutureWrapperType( Map<String, Type> usableTypes, FutureWrapper futureWrapper)845 private static void addFutureWrapperType( 846 Map<String, Type> usableTypes, FutureWrapper futureWrapper) { 847 addUsableType( 848 usableTypes, 849 Type.builder() 850 .setTypeMirror(futureWrapper.wrappedType()) 851 .setAcceptableReturnType(true) 852 .setSupportedInsideWrapper(false) 853 .setFutureWrapper(futureWrapper) 854 .build()); 855 } 856 857 public static final class Builder { 858 859 private Map<String, Type> usableTypes; 860 Builder(Map<String, Type> usableTypes)861 private Builder(Map<String, Type> usableTypes) { 862 this.usableTypes = usableTypes; 863 } 864 865 /** Filtering to only include used types and additional used types */ filterUsed( Context context, Collection<CrossProfileMethodInfo> methods, Collection<TypeElement> additionalUsedTypes)866 public Builder filterUsed( 867 Context context, 868 Collection<CrossProfileMethodInfo> methods, 869 Collection<TypeElement> additionalUsedTypes) { 870 871 Map<String, Type> usedTypes = new LinkedHashMap<>(); 872 873 for (CrossProfileMethodInfo method : methods) { 874 copySupportedTypesForMethod(context, usedTypes, method); 875 } 876 877 for (TypeElement type : additionalUsedTypes) { 878 // We do not want to recurse generics in additional types because generics will not be a 879 // supported type. 880 copySupportedType(context, usedTypes, type.asType(), /* recurseGenerics= */ false); 881 } 882 883 this.usableTypes = usedTypes; 884 885 return this; 886 } 887 copySupportedTypesForMethod( Context context, Map<String, Type> usedTypes, CrossProfileMethodInfo method)888 private void copySupportedTypesForMethod( 889 Context context, Map<String, Type> usedTypes, CrossProfileMethodInfo method) { 890 copySupportedType(context, usedTypes, method.returnType()); 891 for (TypeMirror argumentType : method.parameterTypes()) { 892 copySupportedType(context, usedTypes, argumentType); 893 } 894 } 895 896 // default behaviour is to recurse generics copySupportedType(Context context, Map<String, Type> usedTypes, TypeMirror type)897 private void copySupportedType(Context context, Map<String, Type> usedTypes, TypeMirror type) { 898 copySupportedType(context, usedTypes, type, true); 899 } 900 copySupportedType( Context context, Map<String, Type> usedTypes, TypeMirror type, boolean recurseGenerics)901 private void copySupportedType( 902 Context context, Map<String, Type> usedTypes, TypeMirror type, boolean recurseGenerics) { 903 if (TypeUtils.isGeneric(type) && recurseGenerics) { 904 copySupportedGenericType(context, usedTypes, type); 905 return; 906 } 907 908 if (TypeUtils.isArray(type)) { 909 if (!TypeUtils.isPrimitiveArray(type)) { 910 // Primitive arrays aren't resolved recursively 911 copySupportedType(context, usedTypes, TypeUtils.extractTypeFromArray(type)); 912 type = 913 context 914 .types() 915 .getArrayType(context.elements().getTypeElement("java.lang.Object").asType()); 916 } 917 } 918 919 // The type must have been seen in when constructing the original so this should not 920 // be null 921 Type supportedType = usableTypes.get(type.toString()); 922 923 // We don't support generic callbacks so any callback interfaces can be picked up here 924 if (supportedType.isCrossProfileCallbackInterface()) { 925 for (TypeMirror typeMirror : 926 supportedType.getCrossProfileCallbackInterface().get().argumentTypes()) { 927 copySupportedType(context, usedTypes, typeMirror); 928 } 929 } 930 931 copySupportedType(usedTypes, supportedType); 932 } 933 copySupportedType(Map<String, Type> usedTypes, Type supportedType)934 private void copySupportedType(Map<String, Type> usedTypes, Type supportedType) { 935 addUsableType(usedTypes, supportedType); 936 } 937 copySupportedGenericType( Context context, Map<String, Type> usedTypes, TypeMirror type)938 private void copySupportedGenericType( 939 Context context, Map<String, Type> usedTypes, TypeMirror type) { 940 TypeMirror genericType = TypeUtils.removeTypeArguments(type); 941 942 // The type must have been seen in when constructing the oldSupportedTypes so this should not 943 // be null 944 Type supportedType = usableTypes.get(genericType.toString()); 945 946 if (!supportedType.isSupportedWithAnyGenericType()) { 947 // We need to recursively copy all type arguments 948 for (TypeMirror typeArgument : TypeUtils.extractTypeArguments(type)) { 949 copySupportedType(context, usedTypes, typeArgument); 950 } 951 } 952 953 copySupportedType(usedTypes, supportedType); 954 } 955 956 /** Add additional parcelable wrappers. */ addParcelableWrappers(Collection<ParcelableWrapper> parcelableWrappers)957 public Builder addParcelableWrappers(Collection<ParcelableWrapper> parcelableWrappers) { 958 Map<String, Type> newUsableTypes = new LinkedHashMap<>(usableTypes); 959 960 addParcelableWrapperTypes(newUsableTypes, parcelableWrappers); 961 962 usableTypes = newUsableTypes; 963 964 return this; 965 } 966 967 /** Add additional future wrappers. */ addFutureWrappers(Collection<FutureWrapper> futureWrappers)968 public Builder addFutureWrappers(Collection<FutureWrapper> futureWrappers) { 969 Map<String, Type> newUsableTypes = new LinkedHashMap<>(usableTypes); 970 971 addFutureWrapperTypes(newUsableTypes, futureWrappers); 972 973 usableTypes = newUsableTypes; 974 975 return this; 976 } 977 replaceWrapperPrefix(ClassName prefix)978 public Builder replaceWrapperPrefix(ClassName prefix) { 979 Map<String, Type> newUsableTypes = new LinkedHashMap<>(); 980 981 for (Type usableType : usableTypes.values()) { 982 if (usableType.getParcelableWrapper().isPresent()) { 983 replaceParcelableWrapperPrefix(newUsableTypes, prefix, usableType); 984 } else if (usableType.getFutureWrapper().isPresent()) { 985 replaceFutureWrapperPrefix(newUsableTypes, prefix, usableType); 986 } else { 987 addUsableType(newUsableTypes, usableType); 988 } 989 } 990 991 usableTypes = newUsableTypes; 992 993 return this; 994 } 995 replaceParcelableWrapperPrefix( Map<String, Type> newUsableTypes, ClassName prefix, Type usableType)996 private void replaceParcelableWrapperPrefix( 997 Map<String, Type> newUsableTypes, ClassName prefix, Type usableType) { 998 ParcelableWrapper parcelableWrapper = usableType.getParcelableWrapper().get(); 999 1000 if (parcelableWrapper.wrapperType().equals(ParcelableWrapper.WrapperType.CUSTOM)) { 1001 // Custom types never get prefixed 1002 addUsableType(newUsableTypes, usableType); 1003 return; 1004 } 1005 1006 addParcelableWrapperType( 1007 newUsableTypes, 1008 ParcelableWrapper.create( 1009 parcelableWrapper.wrappedType(), 1010 parcelableWrapper.defaultWrapperClassName(), 1011 prefix(prefix, parcelableWrapper.wrapperClassName()), 1012 parcelableWrapper.wrapperType())); 1013 } 1014 replaceFutureWrapperPrefix( Map<String, Type> newUsableTypes, ClassName prefix, Type usableType)1015 private void replaceFutureWrapperPrefix( 1016 Map<String, Type> newUsableTypes, ClassName prefix, Type usableType) { 1017 FutureWrapper futureWrapper = usableType.getFutureWrapper().get(); 1018 1019 if (futureWrapper.wrapperType().equals(FutureWrapper.WrapperType.CUSTOM)) { 1020 // Custom types never get prefixed 1021 addUsableType(newUsableTypes, usableType); 1022 return; 1023 } 1024 1025 addFutureWrapperType( 1026 newUsableTypes, 1027 FutureWrapper.create( 1028 futureWrapper.wrappedType(), 1029 futureWrapper.defaultWrapperClassName(), 1030 prefix(prefix, futureWrapper.wrapperClassName()), 1031 futureWrapper.wrapperType())); 1032 } 1033 prefix(ClassName prefix, ClassName finalName)1034 private ClassName prefix(ClassName prefix, ClassName finalName) { 1035 return ClassName.get( 1036 prefix.packageName(), prefix.simpleName() + "_" + finalName.simpleName()); 1037 } 1038 1039 /** Build a new {@link SupportedTypes}. */ build()1040 public SupportedTypes build() { 1041 return new SupportedTypes(usableTypes); 1042 } 1043 } 1044 } 1045