• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 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 
17 package com.android.tools.metalava.model.turbine
18 
19 import com.android.tools.metalava.model.AnnotationItem
20 import com.android.tools.metalava.model.BoundsTypeItem
21 import com.android.tools.metalava.model.CallableItem
22 import com.android.tools.metalava.model.ClassItem
23 import com.android.tools.metalava.model.ClassKind
24 import com.android.tools.metalava.model.ClassOrigin
25 import com.android.tools.metalava.model.DefaultTypeParameterList
26 import com.android.tools.metalava.model.ExceptionTypeItem
27 import com.android.tools.metalava.model.FixedFieldValue
28 import com.android.tools.metalava.model.ItemDocumentation.Companion.toItemDocumentationFactory
29 import com.android.tools.metalava.model.ItemDocumentationFactory
30 import com.android.tools.metalava.model.ModifierFlags.Companion.ABSTRACT
31 import com.android.tools.metalava.model.ModifierFlags.Companion.DEFAULT
32 import com.android.tools.metalava.model.ModifierFlags.Companion.FINAL
33 import com.android.tools.metalava.model.ModifierFlags.Companion.NATIVE
34 import com.android.tools.metalava.model.ModifierFlags.Companion.PRIVATE
35 import com.android.tools.metalava.model.ModifierFlags.Companion.PROTECTED
36 import com.android.tools.metalava.model.ModifierFlags.Companion.PUBLIC
37 import com.android.tools.metalava.model.ModifierFlags.Companion.SEALED
38 import com.android.tools.metalava.model.ModifierFlags.Companion.STATIC
39 import com.android.tools.metalava.model.ModifierFlags.Companion.STRICT_FP
40 import com.android.tools.metalava.model.ModifierFlags.Companion.SYNCHRONIZED
41 import com.android.tools.metalava.model.ModifierFlags.Companion.TRANSIENT
42 import com.android.tools.metalava.model.ModifierFlags.Companion.VARARG
43 import com.android.tools.metalava.model.ModifierFlags.Companion.VOLATILE
44 import com.android.tools.metalava.model.MutableModifierList
45 import com.android.tools.metalava.model.ParameterItem
46 import com.android.tools.metalava.model.TypeParameterList
47 import com.android.tools.metalava.model.TypeParameterListAndFactory
48 import com.android.tools.metalava.model.VisibilityLevel
49 import com.android.tools.metalava.model.addDefaultRetentionPolicyAnnotation
50 import com.android.tools.metalava.model.createMutableModifiers
51 import com.android.tools.metalava.model.hasAnnotation
52 import com.android.tools.metalava.model.item.DefaultClassItem
53 import com.android.tools.metalava.model.item.DefaultTypeParameterItem
54 import com.android.tools.metalava.model.item.FieldValue
55 import com.android.tools.metalava.model.item.ParameterDefaultValue
56 import com.android.tools.metalava.model.type.MethodFingerprint
57 import com.android.tools.metalava.reporter.FileLocation
58 import com.google.common.collect.ImmutableList
59 import com.google.common.collect.ImmutableMap
60 import com.google.turbine.binder.bound.SourceTypeBoundClass
61 import com.google.turbine.binder.bound.TypeBoundClass
62 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo
63 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo
64 import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo
65 import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo
66 import com.google.turbine.binder.sym.ClassSymbol
67 import com.google.turbine.binder.sym.TyVarSymbol
68 import com.google.turbine.model.TurbineFlag
69 import com.google.turbine.model.TurbineTyKind
70 import com.google.turbine.tree.Tree
71 import com.google.turbine.tree.Tree.Anno
72 import com.google.turbine.tree.Tree.AnnoExpr
73 import com.google.turbine.tree.Tree.Expression
74 import com.google.turbine.tree.Tree.Literal
75 import com.google.turbine.tree.Tree.MethDecl
76 import com.google.turbine.tree.Tree.TyDecl
77 import com.google.turbine.tree.Tree.VarDecl
78 import com.google.turbine.type.AnnoInfo
79 import com.google.turbine.type.Type
80 
81 /**
82  * Responsible for creating [ClassItem]s from either source or binary [ClassSymbol] and
83  * [TypeBoundClass] pairs.
84  *
85  * @param globalContext provides access to various pieces of data that apply across all classes.
86  * @param classSymbol the unique identifier for the [TypeBoundClass].
87  * @param typeBoundClass the definition of the class as recorded by Turbine.
88  */
89 internal class TurbineClassBuilder(
90     private val globalContext: TurbineGlobalContext,
91     private val classSymbol: ClassSymbol,
92     private val typeBoundClass: TypeBoundClass,
93 ) : TurbineGlobalContext by globalContext {
94     /** The [SourceTypeBoundClass] if this is a source class. */
95     private val sourceTypeBoundClass = typeBoundClass as? SourceTypeBoundClass
96 
97     /**
98      * The [TurbineFieldResolver] used for resolving [Tree.ConstVarName] to
99      * [TypeBoundClass.FieldInfo].
100      */
101     private var fieldResolver: TurbineFieldResolver?
102 
103     init {
104         if (sourceTypeBoundClass == null) {
105             // Only source classes need to resolve fields.
106             fieldResolver = null
107         } else {
108             // Source files need
109             fieldResolver = createFieldResolver(classSymbol, sourceTypeBoundClass)
110         }
111     }
112 
113     /**
114      * Create a [ClassItem] for the [classSymbol]/[typeBoundClass] pair.
115      *
116      * @param containingClassItem the containing [DefaultClassItem] to which the created [ClassItem]
117      *   will belong, if any.
118      * @param enclosingClassTypeItemFactory the [TurbineTypeItemFactory] that is used to create
119      *   [TypeItem]s and tracks the in scope type parameters.
120      */
121     internal fun createClass(
122         containingClassItem: DefaultClassItem?,
123         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
124         origin: ClassOrigin,
125     ): ClassItem {
126         val decl = sourceTypeBoundClass?.decl()
127 
128         // Get the package item
129         val pkgName = classSymbol.dotSeparatedPackageName
130         val pkgItem = codebase.findOrCreatePackage(pkgName)
131 
132         // Create the sourcefile
133         val sourceFile =
134             if (sourceTypeBoundClass != null) {
135                 sourceFileCache.turbineSourceFile(sourceTypeBoundClass.source())
136             } else null
137         val fileLocation =
138             when {
139                 sourceFile != null -> TurbineFileLocation.forTree(sourceFile, decl)
140                 containingClassItem != null ->
141                     TurbineFileLocation.forTree(containingClassItem, decl)
142                 else -> FileLocation.UNKNOWN
143             }
144 
145         // Create class
146         val qualifiedName = classSymbol.qualifiedName
147         val documentation = javadoc(decl)
148         val modifierItem =
149             createModifiers(
150                 typeBoundClass.access(),
151                 typeBoundClass.annotations(),
152             )
153         val (typeParameters, classTypeItemFactory) =
154             createTypeParameters(
155                 typeBoundClass.typeParameterTypes(),
156                 enclosingClassTypeItemFactory,
157                 "class $qualifiedName",
158             )
159         val classKind = getClassKind(typeBoundClass.kind())
160 
161         modifierItem.setSynchronized(false) // A class can not be synchronized in java
162 
163         if (classKind == ClassKind.ANNOTATION_TYPE) {
164             if (!modifierItem.hasAnnotation(AnnotationItem::isRetention)) {
165                 modifierItem.addDefaultRetentionPolicyAnnotation(codebase, isKotlin = false)
166             }
167         }
168 
169         // Set up the SuperClass
170         val superClassType =
171             when (classKind) {
172                 // Normal classes and enums have a non-null super class type.
173                 ClassKind.CLASS,
174                 ClassKind.ENUM ->
175                     typeBoundClass.superClassType()?.let {
176                         classTypeItemFactory.getSuperClassType(it)
177                     }
178                 // Interfaces and annotations (which are a form of interface) do not.
179                 ClassKind.INTERFACE,
180                 ClassKind.ANNOTATION_TYPE -> null
181             }
182 
183         // Set interface types
184         val interfaceTypes =
185             typeBoundClass.interfaceTypes().map { classTypeItemFactory.getInterfaceType(it) }
186 
187         val classItem =
188             itemFactory.createClassItem(
189                 fileLocation = fileLocation,
190                 modifiers = modifierItem,
191                 documentationFactory = getCommentedDoc(documentation),
192                 source = sourceFile,
193                 classKind = classKind,
194                 containingClass = containingClassItem,
195                 containingPackage = pkgItem,
196                 qualifiedName = qualifiedName,
197                 typeParameterList = typeParameters,
198                 origin = origin,
199                 superClassType = superClassType,
200                 interfaceTypes = interfaceTypes,
201             )
202 
203         // Create fields
204         createFields(classItem, typeBoundClass.fields(), classTypeItemFactory)
205 
206         // Create methods
207         createMethods(classItem, typeBoundClass.methods(), classTypeItemFactory)
208 
209         // Create constructors
210         createConstructors(classItem, typeBoundClass.methods(), classTypeItemFactory)
211 
212         // Create InnerClasses.
213         val children = typeBoundClass.children()
214         createNestedClasses(classItem, children.values.asList(), classTypeItemFactory)
215 
216         return classItem
217     }
218 
219     private fun createModifiers(flag: Int, annoInfos: List<AnnoInfo>): MutableModifierList {
220         val annotations = annotationFactory.createAnnotations(annoInfos)
221         val modifierItem =
222             when (flag) {
223                 0 -> { // No Modifier. Default modifier is PACKAGE_PRIVATE in such case
224                     createMutableModifiers(
225                         visibility = VisibilityLevel.PACKAGE_PRIVATE,
226                         annotations = annotations,
227                     )
228                 }
229                 else -> {
230                     createMutableModifiers(computeFlag(flag), annotations)
231                 }
232             }
233         modifierItem.setDeprecated(isDeprecated(annotations))
234         return modifierItem
235     }
236 
237     /**
238      * Given flag value corresponding to Turbine modifiers compute the equivalent flag in Metalava.
239      */
240     private fun computeFlag(flag: Int): Int {
241         // If no visibility flag is provided, result remains 0, implying a 'package-private' default
242         // state.
243         var result = 0
244 
245         if (flag and TurbineFlag.ACC_STATIC != 0) {
246             result = result or STATIC
247         }
248         if (flag and TurbineFlag.ACC_ABSTRACT != 0) {
249             result = result or ABSTRACT
250         }
251         if (flag and TurbineFlag.ACC_FINAL != 0) {
252             result = result or FINAL
253         }
254         if (flag and TurbineFlag.ACC_NATIVE != 0) {
255             result = result or NATIVE
256         }
257         if (flag and TurbineFlag.ACC_SYNCHRONIZED != 0) {
258             result = result or SYNCHRONIZED
259         }
260         if (flag and TurbineFlag.ACC_STRICT != 0) {
261             result = result or STRICT_FP
262         }
263         if (flag and TurbineFlag.ACC_TRANSIENT != 0) {
264             result = result or TRANSIENT
265         }
266         if (flag and TurbineFlag.ACC_VOLATILE != 0) {
267             result = result or VOLATILE
268         }
269         if (flag and TurbineFlag.ACC_DEFAULT != 0) {
270             result = result or DEFAULT
271         }
272         if (flag and TurbineFlag.ACC_SEALED != 0) {
273             result = result or SEALED
274         }
275         if (flag and TurbineFlag.ACC_VARARGS != 0) {
276             result = result or VARARG
277         }
278 
279         // Visibility Modifiers
280         if (flag and TurbineFlag.ACC_PUBLIC != 0) {
281             result = result or PUBLIC
282         }
283         if (flag and TurbineFlag.ACC_PRIVATE != 0) {
284             result = result or PRIVATE
285         }
286         if (flag and TurbineFlag.ACC_PROTECTED != 0) {
287             result = result or PROTECTED
288         }
289 
290         return result
291     }
292 
293     private fun isDeprecated(annotations: List<AnnotationItem>?): Boolean {
294         return annotations?.any { it.qualifiedName == "java.lang.Deprecated" } ?: false
295     }
296 
297     private fun getClassKind(type: TurbineTyKind): ClassKind {
298         return when (type) {
299             TurbineTyKind.INTERFACE -> ClassKind.INTERFACE
300             TurbineTyKind.ENUM -> ClassKind.ENUM
301             TurbineTyKind.ANNOTATION -> ClassKind.ANNOTATION_TYPE
302             else -> ClassKind.CLASS
303         }
304     }
305 
306     private fun createTypeParameters(
307         tyParams: ImmutableMap<TyVarSymbol, TyVarInfo>,
308         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
309         description: String,
310     ): TypeParameterListAndFactory<TurbineTypeItemFactory> {
311 
312         if (tyParams.isEmpty())
313             return TypeParameterListAndFactory(
314                 TypeParameterList.NONE,
315                 enclosingClassTypeItemFactory
316             )
317 
318         // Create a list of [TypeParameterItem]s from turbine specific classes.
319         return DefaultTypeParameterList.createTypeParameterItemsAndFactory(
320             enclosingClassTypeItemFactory,
321             description,
322             tyParams.toList(),
323             { (sym, tyParam) -> createTypeParameter(sym, tyParam) },
324             { typeItemFactory, (_, tParam) -> createTypeParameterBounds(tParam, typeItemFactory) },
325         )
326     }
327 
328     /**
329      * Create the [DefaultTypeParameterItem] without any bounds and register it so that any uses of
330      * it within the type bounds, e.g. `<E extends Enum<E>>`, or from other type parameters within
331      * the same [TypeParameterList] can be resolved.
332      */
333     private fun createTypeParameter(sym: TyVarSymbol, param: TyVarInfo): DefaultTypeParameterItem {
334         val modifiers = createModifiers(0, param.annotations())
335         val typeParamItem =
336             itemFactory.createTypeParameterItem(
337                 modifiers,
338                 name = sym.name(),
339                 // Java does not supports reified generics
340                 isReified = false,
341             )
342         return typeParamItem
343     }
344 
345     /** Create the bounds of a [DefaultTypeParameterItem]. */
346     private fun createTypeParameterBounds(
347         param: TyVarInfo,
348         typeItemFactory: TurbineTypeItemFactory,
349     ): List<BoundsTypeItem> {
350         val typeBounds = mutableListOf<BoundsTypeItem>()
351         val upperBounds = param.upperBound()
352 
353         upperBounds.bounds().mapTo(typeBounds) { typeItemFactory.getBoundsType(it) }
354         param.lowerBound()?.let { typeBounds.add(typeItemFactory.getBoundsType(it)) }
355 
356         return typeBounds.toList()
357     }
358 
359     /** This method sets up the nested class hierarchy. */
360     private fun createNestedClasses(
361         classItem: DefaultClassItem,
362         nestedClasses: ImmutableList<ClassSymbol>,
363         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
364     ) {
365         for (nestedClassSymbol in nestedClasses) {
366             val nestedTypeBoundClass = typeBoundClassForSymbol(nestedClassSymbol)
367             val nestedClassBuilder =
368                 TurbineClassBuilder(
369                     globalContext = globalContext,
370                     classSymbol = nestedClassSymbol,
371                     typeBoundClass = nestedTypeBoundClass,
372                 )
373             nestedClassBuilder.createClass(
374                 containingClassItem = classItem,
375                 enclosingClassTypeItemFactory = enclosingClassTypeItemFactory,
376                 origin = classItem.origin,
377             )
378         }
379     }
380 
381     /** This method creates and sets the fields of a class */
382     private fun createFields(
383         classItem: DefaultClassItem,
384         fields: ImmutableList<FieldInfo>,
385         typeItemFactory: TurbineTypeItemFactory,
386     ) {
387         for (field in fields) {
388             val flags = field.access()
389             val decl = field.decl()
390             val fieldModifierItem =
391                 createModifiers(
392                     flags,
393                     field.annotations(),
394                 )
395             val isEnumConstant = (flags and TurbineFlag.ACC_ENUM) != 0
396             val fieldValue = createInitialValue(field)
397             val type =
398                 typeItemFactory.getFieldType(
399                     underlyingType = field.type(),
400                     itemAnnotations = fieldModifierItem.annotations(),
401                     isEnumConstant = isEnumConstant,
402                     isFinal = fieldModifierItem.isFinal(),
403                     isInitialValueNonNull = {
404                         // The initial value is non-null if the value is a literal which is not
405                         // null.
406                         fieldValue.initialValue(false) != null
407                     }
408                 )
409 
410             val documentation = javadoc(decl)
411             val fieldItem =
412                 itemFactory.createFieldItem(
413                     fileLocation = TurbineFileLocation.forTree(classItem, decl),
414                     modifiers = fieldModifierItem,
415                     documentationFactory = getCommentedDoc(documentation),
416                     name = field.name(),
417                     containingClass = classItem,
418                     type = type,
419                     isEnumConstant = isEnumConstant,
420                     fieldValue = fieldValue,
421                 )
422 
423             classItem.addField(fieldItem)
424         }
425     }
426 
427     private fun createMethods(
428         classItem: DefaultClassItem,
429         methods: List<MethodInfo>,
430         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
431     ) {
432         for (method in methods) {
433             // Ignore constructors.
434             if (method.sym().name() == "<init>") continue
435 
436             val decl: MethDecl? = method.decl()
437             val methodModifierItem =
438                 createModifiers(
439                     method.access(),
440                     method.annotations(),
441                 )
442             val name = method.name()
443             val (typeParams, methodTypeItemFactory) =
444                 createTypeParameters(
445                     method.tyParams(),
446                     enclosingClassTypeItemFactory,
447                     name,
448                 )
449             val documentation = javadoc(decl)
450             val defaultValueExpr = getAnnotationDefaultExpression(method)
451             val defaultValue =
452                 method.defaultValue()?.let { defaultConst ->
453                     TurbineValue(defaultConst, defaultValueExpr, fieldResolver)
454                         .getSourceForMethodDefault()
455                 }
456                     ?: ""
457 
458             val parameters = method.parameters()
459             val fingerprint = MethodFingerprint(name, parameters.size)
460             val isAnnotationElement = classItem.isAnnotationType() && !methodModifierItem.isStatic()
461             val returnType =
462                 methodTypeItemFactory.getMethodReturnType(
463                     underlyingReturnType = method.returnType(),
464                     itemAnnotations = methodModifierItem.annotations(),
465                     fingerprint = fingerprint,
466                     isAnnotationElement = isAnnotationElement,
467                 )
468 
469             val methodItem =
470                 itemFactory.createMethodItem(
471                     fileLocation = TurbineFileLocation.forTree(classItem, decl),
472                     modifiers = methodModifierItem,
473                     documentationFactory = getCommentedDoc(documentation),
474                     name = name,
475                     containingClass = classItem,
476                     typeParameterList = typeParams,
477                     returnType = returnType,
478                     parameterItemsFactory = { containingCallable ->
479                         createParameters(
480                             containingCallable,
481                             decl?.params(),
482                             parameters,
483                             methodTypeItemFactory,
484                         )
485                     },
486                     throwsTypes = getThrowsList(method.exceptions(), methodTypeItemFactory),
487                     annotationDefault = defaultValue,
488                 )
489 
490             // Ignore enum synthetic methods.
491             if (methodItem.isEnumSyntheticMethod()) continue
492 
493             classItem.addMethod(methodItem)
494         }
495     }
496 
497     private fun createParameters(
498         containingCallable: CallableItem,
499         parameterDecls: List<VarDecl>?,
500         parameters: List<ParamInfo>,
501         typeItemFactory: TurbineTypeItemFactory,
502     ): List<ParameterItem> {
503         val fingerprint = MethodFingerprint(containingCallable.name(), parameters.size)
504         // Some parameters in [parameters] are implicit parameters that do not have a corresponding
505         // entry in the [parameterDecls] list. The number of implicit parameters is the total
506         // number of [parameters] minus the number of declared parameters [parameterDecls]. The
507         // implicit parameters are always at the beginning so the offset from the declared parameter
508         // in [parameterDecls] to the corresponding parameter in [parameters] is simply the number
509         // of the implicit parameters.
510         val declaredParameterOffset = parameters.size - (parameterDecls?.size ?: 0)
511         return parameters.mapIndexed { idx, parameter ->
512             val parameterModifierItem =
513                 createModifiers(parameter.access(), parameter.annotations()).toImmutable()
514             val type =
515                 typeItemFactory.getMethodParameterType(
516                     underlyingParameterType = parameter.type(),
517                     itemAnnotations = parameterModifierItem.annotations(),
518                     fingerprint = fingerprint,
519                     parameterIndex = idx,
520                     isVarArg = parameterModifierItem.isVarArg(),
521                 )
522             // Get the [Tree.VarDecl] corresponding to the [ParamInfo], if available.
523             val decl =
524                 if (parameterDecls != null && idx >= declaredParameterOffset)
525                     parameterDecls.get(idx - declaredParameterOffset)
526                 else null
527 
528             val fileLocation =
529                 TurbineFileLocation.forTree(containingCallable.containingClass(), decl)
530             val parameterItem =
531                 itemFactory.createParameterItem(
532                     fileLocation = fileLocation,
533                     modifiers = parameterModifierItem,
534                     name = parameter.name(),
535                     publicNameProvider = { null },
536                     containingCallable = containingCallable,
537                     parameterIndex = idx,
538                     type = type,
539                     defaultValueFactory = { ParameterDefaultValue.NONE },
540                 )
541             parameterItem
542         }
543     }
544 
545     private fun createConstructors(
546         classItem: DefaultClassItem,
547         methods: List<MethodInfo>,
548         enclosingClassTypeItemFactory: TurbineTypeItemFactory,
549     ) {
550         for (constructor in methods) {
551             // Skip real methods.
552             if (constructor.sym().name() != "<init>") continue
553 
554             val decl: MethDecl? = constructor.decl()
555             val constructorModifierItem =
556                 createModifiers(
557                     constructor.access(),
558                     constructor.annotations(),
559                 )
560             val (typeParams, constructorTypeItemFactory) =
561                 createTypeParameters(
562                     constructor.tyParams(),
563                     enclosingClassTypeItemFactory,
564                     constructor.name(),
565                 )
566             val isImplicitDefaultConstructor =
567                 (constructor.access() and TurbineFlag.ACC_SYNTH_CTOR) != 0
568             val name = classItem.simpleName()
569             val documentation = javadoc(decl)
570             val constructorItem =
571                 itemFactory.createConstructorItem(
572                     fileLocation = TurbineFileLocation.forTree(classItem, decl),
573                     modifiers = constructorModifierItem,
574                     documentationFactory = getCommentedDoc(documentation),
575                     // Turbine's Binder gives return type of constructors as void but the
576                     // model expects it to the type of object being created. So, use the
577                     // containing [ClassItem]'s type as the constructor return type.
578                     name = name,
579                     containingClass = classItem,
580                     typeParameterList = typeParams,
581                     returnType = classItem.type(),
582                     parameterItemsFactory = { constructorItem ->
583                         createParameters(
584                             constructorItem,
585                             decl?.params(),
586                             constructor.parameters(),
587                             constructorTypeItemFactory,
588                         )
589                     },
590                     throwsTypes =
591                         getThrowsList(constructor.exceptions(), constructorTypeItemFactory),
592                     implicitConstructor = isImplicitDefaultConstructor,
593                 )
594 
595             classItem.addConstructor(constructorItem)
596         }
597     }
598 
599     private fun javadoc(item: TyDecl?): String {
600         if (!allowReadingComments) return ""
601         return item?.javadoc() ?: ""
602     }
603 
604     private fun javadoc(item: VarDecl?): String {
605         if (!allowReadingComments) return ""
606         return item?.javadoc() ?: ""
607     }
608 
609     private fun javadoc(item: MethDecl?): String {
610         if (!allowReadingComments) return ""
611         return item?.javadoc() ?: ""
612     }
613 
614     private fun getThrowsList(
615         throwsTypes: List<Type>,
616         enclosingTypeItemFactory: TurbineTypeItemFactory
617     ): List<ExceptionTypeItem> {
618         return throwsTypes.map { type -> enclosingTypeItemFactory.getExceptionType(type) }
619     }
620 
621     private fun getCommentedDoc(doc: String): ItemDocumentationFactory {
622         return buildString {
623                 if (doc != "") {
624                     append("/**")
625                     append(doc)
626                     append("*/")
627                 }
628             }
629             .toItemDocumentationFactory()
630     }
631 
632     private fun createInitialValue(field: FieldInfo): FieldValue {
633         val optExpr = field.decl()?.init()
634         val expr = if (optExpr != null && optExpr.isPresent()) optExpr.get() else null
635         val constantValue = field.value()?.getValue()
636 
637         val initialValueWithoutRequiredConstant =
638             when {
639                 constantValue != null -> constantValue
640                 expr == null -> null
641                 else ->
642                     when (expr.kind()) {
643                         Tree.Kind.LITERAL -> {
644                             (expr as Literal).value().underlyingValue
645                         }
646                         // Class Type
647                         Tree.Kind.CLASS_LITERAL -> {
648                             expr
649                         }
650                         else -> {
651                             null
652                         }
653                     }
654             }
655 
656         return FixedFieldValue(constantValue, initialValueWithoutRequiredConstant)
657     }
658 
659     /**
660      * Extracts the expression corresponding to the default value of a given annotation method. If
661      * the method does not have a default value, returns null.
662      */
663     private fun getAnnotationDefaultExpression(method: MethodInfo) =
664         method.decl()?.defaultValue()?.orElse(null)?.let { defaultTree ->
665 
666             // Turbine stores the default value as a Tree not an Expression so that it can use an
667             // Anno class (which is not an Expression). It could wrap the Anno in an AnnoExpr but
668             // does not, presumably as an optimization. However, this does wrap it in an AnnoExpr
669             // as it allows for more consistent handling.
670             when (defaultTree) {
671                 is Expression -> defaultTree
672                 is Anno -> AnnoExpr(defaultTree.position(), defaultTree)
673                 else -> error("unknown default value type (${defaultTree.javaClass}: $defaultTree")
674             }
675         }
676 }
677