1 /* 2 * Copyright 2020 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 */ 16 // @exportToFramework:skipFile() 17 package androidx.appsearch.annotation; 18 19 import androidx.appsearch.app.AppSearchSchema; 20 import androidx.appsearch.app.EmbeddingVector; 21 import androidx.appsearch.app.ExperimentalAppSearchApi; 22 import androidx.appsearch.app.LongSerializer; 23 import androidx.appsearch.app.StringSerializer; 24 25 import org.jspecify.annotations.NonNull; 26 27 import java.lang.annotation.Documented; 28 import java.lang.annotation.ElementType; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.lang.annotation.Target; 32 33 /** 34 * Marks a class as an entity known to AppSearch containing a data record. 35 * 36 * <p>Each field annotated with one of the Property annotations will become an AppSearch searchable 37 * property. Fields annotated with other annotations included here (like {@link Id @Id}) will have 38 * the special behaviour described in that annotation. All other members (those which do not have 39 * any of these annotations) will be ignored by AppSearch and will not be persisted or set. 40 * 41 * <p>Each AppSearch annotated field must meet at least one the following conditions: 42 * <ol> 43 * <li>There must be a getter named get<Fieldname> in the class (with package-private 44 * visibility or greater), or 45 * <li>The field itself must have package-private visibility or greater. 46 * </ol> 47 * 48 * <p>The field must also meet at least one of the following conditions: 49 * <ol> 50 * <li>There must be a setter named {@code set<FieldName>(arg)} in the class (with 51 * package-private visibility or greater), or 52 * <li>There must be a setter named {@code fieldname(arg)} in the class (with package-private 53 * visibility or greater), or 54 * <li>The field itself must be mutable (non-final) and have package-private visibility or 55 * greater, or 56 * <li>There must be a constructor that accepts all fields not meeting condition 1. and 2. as 57 * parameters. That constructor must have package-private visibility or greater. It may 58 * also accept fields that do meet conditions 1 and 2, in which case the constructor will be 59 * used to populate those fields instead of methods 1 and 2. 60 * </ol> 61 * 62 * <p>Fields may be named according to any of the following conventions: 63 * <ul> 64 * <li>exampleName 65 * <li>mExampleName 66 * <li>_exampleName 67 * <li>exampleName_ 68 * </ul> 69 * 70 * <p>In all of the above cases, the default property name will be "exampleName", the allowed 71 * getters are {@code getExampleName()} or {@code exampleName()}, the allowed setters are {@code 72 * setExampleName(arg)} or {@code exampleName(arg)}, and the expected constructor parameter for 73 * the field is "exampleName". 74 * 75 * <p>The class must also have exactly one member annotated with {@link Id @Id}. 76 * 77 * <p>Properties contain the document's data. They may be indexed or non-indexed (the default). 78 * Only indexed properties can be searched for in queries. There is a limit of 79 * {@link androidx.appsearch.app.Features#getMaxIndexedProperties} indexed properties in one 80 * document. 81 */ 82 @Documented 83 @Retention(RetentionPolicy.CLASS) 84 @Target(ElementType.TYPE) 85 public @interface Document { 86 /** 87 * The schema name of this type. 88 * 89 * <p>This string is the key to which the complete schema definition is associated in the 90 * AppSearch database. It can be specified to replace an existing type with a new definition. 91 * 92 * <p>If not specified, it will be automatically set to the simple name of the annotated class. 93 */ name()94 String name() default ""; 95 96 /** 97 * The list of {@link Document} annotated classes that this type inherits from, in the context 98 * of AppSearch. 99 * 100 * <p>Please note that the type systems in AppSearch and Java are not necessarily equivalent. 101 * Specifically, if Foo and Bar are two classes, Bar can be a parent type of Foo in 102 * AppSearch, but the Foo class does not have to extend the Bar class in Java. The converse 103 * holds as well. However, the most common use case is to align the two type systems for 104 * single parent pattern, given that if Foo extends Bar in Java, Bar's properties will 105 * automatically be copied into Foo so that it is not necessary to redefine every property in 106 * Foo. 107 * 108 * @see AppSearchSchema.Builder#addParentType(String) 109 */ parent()110 Class<?>[] parent() default {}; 111 112 /** 113 * Marks a member field of a document as the document's unique identifier (ID). 114 * 115 * <p>Indexing a document with a particular ID replaces any existing documents with the same 116 * ID in that namespace. 117 * 118 * <p>A document must have exactly one such field, and it must be of type {@link String}. 119 * 120 * <p>See the class description of {@link Document} for other requirements (i.e. it 121 * must be visible, or have a visible getter and setter, or be exposed through a visible 122 * constructor). 123 */ 124 @Documented 125 @Retention(RetentionPolicy.CLASS) 126 @Target({ElementType.FIELD, ElementType.METHOD}) 127 @interface Id {} 128 129 /** 130 * Marks a member field of a document as the document's namespace. 131 * 132 * <p>The namespace is an arbitrary user-provided string that can be used to group documents 133 * during querying or deletion. Indexing a document with a particular ID replaces any existing 134 * documents with the same ID in that namespace. 135 * 136 * <p>A document must have exactly one such field, and it must be of type {@link String}. 137 * 138 * <p>See the class description of {@link Document} for other requirements (i.e. if 139 * present it must be visible, or have a visible getter and setter, or be exposed through a 140 * visible constructor). 141 */ 142 @Documented 143 @Retention(RetentionPolicy.CLASS) 144 @Target({ElementType.FIELD, ElementType.METHOD}) 145 @interface Namespace {} 146 147 /** 148 * Marks a member field of a document as the document's creation timestamp. 149 * 150 * <p>The creation timestamp is used for document expiry (see {@link TtlMillis}) and as one 151 * of the sort options for queries. 152 * 153 * <p>This field is not required. If not present or not set, the document will be assigned 154 * the current timestamp as its creation timestamp. 155 * 156 * <p>If present, the field must be of type {@code long} or {@link Long}. 157 * 158 * <p>See the class description of {@link Document} for other requirements (i.e. if 159 * present it must be visible, or have a visible getter and setter, or be exposed through a 160 * visible constructor). 161 */ 162 @Documented 163 @Retention(RetentionPolicy.CLASS) 164 @Target({ElementType.FIELD, ElementType.METHOD}) 165 @interface CreationTimestampMillis {} 166 167 /** 168 * Marks a member field of a document as the document's time-to-live (TTL). 169 * 170 * <p>The document will be automatically deleted {@link TtlMillis} milliseconds after 171 * {@link CreationTimestampMillis}. 172 * 173 * <p>This field is not required. If not present, not set, or set to {@code 0}, the document 174 * will never expire or be auto-deleted until the app is uninstalled or 175 * {@link androidx.appsearch.app.AppSearchSession#removeAsync} is called. 176 * 177 * <p>If present, the field must be of type {@code long} or {@link Long}. 178 * 179 * <p>See the class description of {@link Document} for other requirements (i.e. if 180 * present it must be visible, or have a visible getter and setter, or be exposed through a 181 * visible constructor). 182 */ 183 @Documented 184 @Retention(RetentionPolicy.CLASS) 185 @Target({ElementType.FIELD, ElementType.METHOD}) 186 @interface TtlMillis {} 187 188 /** 189 * Marks a member field of a document as the document's query-independent score. 190 * 191 * <p>The score is a query-independent measure of the document's quality, relative to other 192 * documents of the same type. It is one of the sort options for queries. 193 * 194 * <p>This field is not required. If not present or not set, the document will have a score 195 * of 0. 196 * 197 * <p>If present, the field must be of type {@code int} or {@link Integer}. 198 * 199 * <p>See the class description of {@link Document} for other requirements (i.e. if 200 * present it must be visible, or have a visible getter and setter, or be exposed through a 201 * visible constructor). 202 */ 203 @Documented 204 @Retention(RetentionPolicy.CLASS) 205 @Target({ElementType.FIELD, ElementType.METHOD}) 206 @interface Score {} 207 208 /** Configures a string member field of a class as a property known to AppSearch. */ 209 @Documented 210 @Retention(RetentionPolicy.CLASS) 211 @Target({ElementType.FIELD, ElementType.METHOD}) 212 @interface StringProperty { 213 /** 214 * The name of this property. This string is used to query against this property. 215 * 216 * <p>If not specified, the name of the field in the code will be used instead. 217 */ name()218 String name() default ""; 219 220 /** 221 * Configures how tokens should be extracted from this property. 222 * 223 * <p>If not specified, defaults to {@link 224 * AppSearchSchema.StringPropertyConfig#TOKENIZER_TYPE_PLAIN} (the field will be tokenized 225 * along word boundaries as plain text). 226 */ tokenizerType()227 @AppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType() 228 default AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN; 229 230 /** 231 * Configures how a property should be indexed so that it can be retrieved by queries. 232 * 233 * <p>If not specified, defaults to {@link 234 * AppSearchSchema.StringPropertyConfig#INDEXING_TYPE_NONE} (the field will not be indexed 235 * and cannot be queried). 236 */ indexingType()237 @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType() 238 default AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE; 239 240 /** 241 * Configures how a property should be processed so that the document can be joined. 242 * 243 * <p>Properties configured with 244 * {@link AppSearchSchema.StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID} enable 245 * the documents to be joined with other documents that have the same qualified ID as the 246 * value of this field. (A qualified ID is a compact representation of the tuple <package 247 * name, database name, namespace, document ID> that uniquely identifies a document 248 * indexed in the AppSearch storage backend.) This property name can be specified as the 249 * child property expression in {@link androidx.appsearch.app.JoinSpec.Builder(String)} for 250 * join operations. 251 * 252 * <p>This attribute doesn't apply to properties of a repeated type (e.g., a list). 253 * 254 * <p>If not specified, defaults to 255 * {@link AppSearchSchema.StringPropertyConfig#JOINABLE_VALUE_TYPE_NONE}, which means the 256 * property can not be used in a child property expression to configure a 257 * {@link androidx.appsearch.app.JoinSpec.Builder(String)}. 258 */ joinableValueType()259 @AppSearchSchema.StringPropertyConfig.JoinableValueType int joinableValueType() 260 default AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE; 261 262 /** 263 * Configures how a property should be converted to and from a {@link String}. 264 * 265 * <p>Useful for representing properties using rich types that boil down to simple string 266 * values in the database. 267 * 268 * <p>The referenced class must have a public zero params constructor. 269 * 270 * <p>For example: 271 * 272 * <pre> 273 * {@code 274 * @Document("Entity") 275 * public final class MyEntity { 276 * 277 * @Document.StringProperty(serializer = SomeRichTypeSerializer.class) 278 * public SomeRichType getMyProperty(); 279 * 280 * public final class SomeRichTypeSerializer implements StringSerializer<SomeRichType> { 281 * 282 * @Override 283 * @NonNull 284 * public String serialize(@NonNull SomeRichType instance) {...} 285 * 286 * @Override 287 * @Nullable 288 * public SomeRichType deserialize(@NonNull String string) {...} 289 * } 290 * } 291 * } 292 * </pre> 293 */ serializer()294 Class<? extends StringSerializer<?>> serializer() default DefaultSerializer.class; 295 296 /** 297 * Configures whether this property must be specified for the document to be valid. 298 * 299 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 300 * 301 * <p>Please make sure you understand the consequences of required fields on 302 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before setting 303 * this attribute to {@code true}. 304 */ required()305 boolean required() default false; 306 307 final class DefaultSerializer implements StringSerializer<String> { 308 @Override serialize(@onNull String instance)309 public @NonNull String serialize(@NonNull String instance) { 310 return instance; 311 } 312 313 @Override deserialize(@onNull String string)314 public @NonNull String deserialize(@NonNull String string) { 315 return string; 316 } 317 } 318 } 319 320 /** 321 * Configures a member field of a class as a property known to AppSearch. 322 * 323 * <p>Field's data class is required to be annotated with {@link Document}. 324 */ 325 @Documented 326 @Retention(RetentionPolicy.CLASS) 327 @Target({ElementType.FIELD, ElementType.METHOD}) 328 @interface DocumentProperty { 329 /** 330 * The name of this property. This string is used to query against this property. 331 * 332 * <p>If not specified, the name of the field in the code will be used instead. 333 */ name()334 String name() default ""; 335 336 /** 337 * Configures whether all fields in the nested document should be indexed. 338 * 339 * <p>If false, the nested document's properties are not indexed regardless of its own 340 * schema, unless {@link #indexableNestedPropertiesList()} is used to index a subset of 341 * properties from the nested document. 342 * 343 * <p>{@link IllegalArgumentException} will be thrown during setSchema if set to true and 344 * defining a non-empty list for {@link #indexableNestedPropertiesList()} 345 */ indexNestedProperties()346 boolean indexNestedProperties() default false; 347 348 /** 349 * The list of properties in the nested document to index. The property will be indexed 350 * according to its indexing configurations in the document's schema definition. 351 * 352 * <p>{@link #indexNestedProperties} is required to be false if this list is non-empty. 353 * {@link IllegalArgumentException} will be thrown during setSchema if this condition is 354 * not met. 355 * 356 * @see 357 * AppSearchSchema.DocumentPropertyConfig.Builder#addIndexableNestedProperties(Collection) 358 */ indexableNestedPropertiesList()359 String[] indexableNestedPropertiesList() default {}; 360 361 /** 362 * Configures whether to inherit the indexable nested properties list from the Document's 363 * superclass type definition. When set to true, the indexable property paths will be 364 * a union of the paths specified in {@link #indexableNestedPropertiesList()} and any 365 * path specified in the document class's superclass or inherited interfaces. 366 * Effectively, this is a no-op if none of the document superclasses specify a path for 367 * this document property. 368 * 369 * <p>Ex. Consider the following Document classes: 370 * <pre> 371 * {@code 372 * @Document 373 * class Person { 374 * @Document.DocumentProperty(indexableNestedPropertiesList = {"streetName", "zipcode"}) 375 * Address livesAt; 376 * } 377 * @Document 378 * class Artist extends Person { 379 * @Document.DocumentProperty( 380 * indexableNestedPropertiesList = {"country"}, 381 * inheritIndexableNestedPropertiesFromSuperclass = true 382 * ) 383 * Address livesAt; 384 * } 385 * } 386 * </pre> 387 * 388 * <p>By setting 'inheritIndexableNestedPropertiesFromSuperclass = true', Artist.livesAt 389 * inherits the indexable nested properties defined by its parent class's livesAt field 390 * (Person.livesAt) and indexes all three fields: {streetName, zipCode, country} 391 */ inheritIndexableNestedPropertiesFromSuperclass()392 boolean inheritIndexableNestedPropertiesFromSuperclass() default false; 393 394 /** 395 * Configures whether this property must be specified for the document to be valid. 396 * 397 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 398 * 399 * <p>Please make sure you understand the consequences of required fields on 400 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before setting 401 * this attribute to {@code true}. 402 */ required()403 boolean required() default false; 404 } 405 406 /** Configures a 64-bit integer field of a class as a property known to AppSearch. */ 407 @Documented 408 @Retention(RetentionPolicy.CLASS) 409 @Target({ElementType.FIELD, ElementType.METHOD}) 410 @interface LongProperty { 411 /** 412 * The name of this property. This string is used to query against this property. 413 * 414 * <p>If not specified, the name of the field in the code will be used instead. 415 */ name()416 String name() default ""; 417 418 /** 419 * Configures how a property should be indexed so that it can be retrieved by queries. 420 * 421 * <p>If not specified, defaults to {@link 422 * AppSearchSchema.LongPropertyConfig#INDEXING_TYPE_NONE} (the field will not be indexed 423 * and cannot be queried). 424 */ 425 // TODO(b/259744228): figure out backward compatibility handling. Before U, we would like 426 // to deal with Long INDEXING_TYPE_RANGE properly to make it un-indexed 427 // and avoid failing the whole document. indexingType()428 @AppSearchSchema.LongPropertyConfig.IndexingType int indexingType() 429 default AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE; 430 431 /** 432 * Configures how a property should be converted to and from a {@link Long}. 433 * 434 * <p>Useful for representing properties using rich types that boil down to simple 64-bit 435 * integer values in the database. 436 * 437 * <p>The referenced class must have a public zero params constructor. 438 * 439 * <p>See {@link StringProperty#serializer()} for an example of a serializer. 440 */ serializer()441 Class<? extends LongSerializer<?>> serializer() default DefaultSerializer.class; 442 443 /** 444 * Configures whether this property must be specified for the document to be valid. 445 * 446 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 447 * 448 * <p>Please make sure you understand the consequences of required fields on 449 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before setting 450 * this attribute to {@code true}. 451 */ required()452 boolean required() default false; 453 454 final class DefaultSerializer implements LongSerializer<Long> { 455 @Override serialize(@uppressWarnings"AutoBoxing") @onNull Long value)456 public long serialize(@SuppressWarnings("AutoBoxing") @NonNull Long value) { 457 return value; 458 } 459 460 @Override 461 @SuppressWarnings("AutoBoxing") deserialize(long value)462 public @NonNull Long deserialize(long value) { 463 return value; 464 } 465 } 466 } 467 468 /** 469 * Configures a double-precision decimal number field of a class as a property known to 470 * AppSearch. 471 */ 472 @Documented 473 @Retention(RetentionPolicy.CLASS) 474 @Target({ElementType.FIELD, ElementType.METHOD}) 475 @interface DoubleProperty { 476 /** 477 * The name of this property. This string is used to query against this property. 478 * 479 * <p>If not specified, the name of the field in the code will be used instead. 480 */ name()481 String name() default ""; 482 483 /** 484 * Configures whether this property must be specified for the document to be valid. 485 * 486 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 487 * 488 * <p>Please make sure you understand the consequences of required fields on 489 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before setting 490 * this attribute to {@code true}. 491 */ required()492 boolean required() default false; 493 } 494 495 /** Configures a boolean member field of a class as a property known to AppSearch. */ 496 @Documented 497 @Retention(RetentionPolicy.CLASS) 498 @Target({ElementType.FIELD, ElementType.METHOD}) 499 @interface BooleanProperty { 500 /** 501 * The name of this property. This string is used to query against this property. 502 * 503 * <p>If not specified, the name of the field in the code will be used instead. 504 */ name()505 String name() default ""; 506 507 /** 508 * Configures whether this property must be specified for the document to be valid. 509 * 510 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 511 * 512 * <p>Please make sure you understand the consequences of required fields on 513 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before setting 514 * this attribute to {@code true}. 515 */ required()516 boolean required() default false; 517 } 518 519 /** Configures a byte array member field of a class as a property known to AppSearch. */ 520 @Documented 521 @Retention(RetentionPolicy.CLASS) 522 @Target({ElementType.FIELD, ElementType.METHOD}) 523 @interface BytesProperty { 524 /** 525 * The name of this property. This string is used to query against this property. 526 * 527 * <p>If not specified, the name of the field in the code will be used instead. 528 */ name()529 String name() default ""; 530 531 /** 532 * Configures whether this property must be specified for the document to be valid. 533 * 534 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 535 * 536 * <p>Please make sure you understand the consequences of required fields on 537 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before setting 538 * this attribute to {@code true}. 539 */ required()540 boolean required() default false; 541 } 542 543 /** 544 * Configures an {@link EmbeddingVector} field of a class as a property known to AppSearch. 545 */ 546 @Documented 547 @Retention(RetentionPolicy.CLASS) 548 @Target({ElementType.FIELD, ElementType.METHOD}) 549 @interface EmbeddingProperty { 550 /** 551 * The name of this property. This string is used to query against this property. 552 * 553 * <p>If not specified, the name of the field in the code will be used instead. 554 */ name()555 String name() default ""; 556 557 /** 558 * Configures how a property should be indexed so that it can be retrieved by queries. 559 * 560 * <p>If not specified, defaults to 561 * {@link AppSearchSchema.EmbeddingPropertyConfig#INDEXING_TYPE_NONE} (the field will not be 562 * indexed and cannot be queried). 563 */ indexingType()564 @AppSearchSchema.EmbeddingPropertyConfig.IndexingType int indexingType() 565 default AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE; 566 567 /** 568 * Configures whether the embedding vectors in this property should be quantized. 569 * 570 * <p>If not specified, defaults to 571 * {@link AppSearchSchema.EmbeddingPropertyConfig#QUANTIZATION_TYPE_NONE} (contents in 572 * this property will not be quantized.). 573 */ 574 @ExperimentalAppSearchApi quantizationType()575 @AppSearchSchema.EmbeddingPropertyConfig.QuantizationType int quantizationType() 576 default AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_NONE; 577 578 /** 579 * Configures whether this property must be specified for the document to be valid. 580 * 581 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 582 * 583 * <p>Please make sure you understand the consequences of required fields on 584 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before 585 * setting this attribute to {@code true}. 586 */ required()587 boolean required() default false; 588 } 589 590 /** 591 * Configures an {@link androidx.appsearch.app.AppSearchBlobHandle} field of a class as a 592 * property known to AppSearch. 593 */ 594 @Documented 595 @Retention(RetentionPolicy.CLASS) 596 @Target({ElementType.FIELD, ElementType.METHOD}) 597 @ExperimentalAppSearchApi 598 @interface BlobHandleProperty { 599 /** 600 * The name of this property. This string is used to query against this property. 601 * 602 * <p>If not specified, the name of the field in the code will be used instead. 603 */ name()604 String name() default ""; 605 606 /** 607 * Configures whether this property must be specified for the document to be valid. 608 * 609 * <p>This attribute does not apply to properties of a repeated type (e.g. a list). 610 * 611 * <p>Please make sure you understand the consequences of required fields on 612 * {@link androidx.appsearch.app.AppSearchSession#setSchemaAsync schema migration} before 613 * setting this attribute to {@code true}. 614 */ required()615 boolean required() default false; 616 } 617 618 /** 619 * Marks a static method or a builder class directly as a builder producer. A builder class 620 * should contain a "build()" method to construct the AppSearch document object and setter 621 * methods to set field values. 622 * 623 * <p>When a static method is marked as a builder producer, the method should return a 624 * builder instance for AppSearch to construct the document object. When a builder class is 625 * marked as a builder producer directly, AppSearch will use the constructor of the builder 626 * class to create a builder instance. 627 * 628 * <p>The annotated static method or the constructor of the annotated builder class is allowed 629 * to accept parameters to set a part of field values. In this case, AppSearch will only use 630 * setters to set values for the remaining fields. 631 * 632 * <p>Once a builder producer is specified, AppSearch will be forced to use the builder 633 * pattern to construct the document object. 634 */ 635 @Documented 636 @Retention(RetentionPolicy.CLASS) 637 @Target({ElementType.METHOD, ElementType.TYPE}) 638 @interface BuilderProducer {} 639 } 640