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&lt;Fieldname&gt; 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