1 /* 2 * Copyright (C) 2021 Square, Inc. 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.squareup.kotlinpoet.metadata.specs 17 18 import com.squareup.kotlinpoet.AnnotationSpec 19 import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.GET 20 import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.SET 21 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil 22 23 /** 24 * Represents relevant information on a property used for [ClassInspector]. Should only be 25 * associated with properties of a [ClassData]. 26 * 27 * @param annotations declared annotations on this property. 28 * @property fieldData associated [FieldData] with this property, if any. 29 * @property getterData associated getter (as [MethodData]) with this property, if any. 30 * @property setterData associated setter (as [MethodData]) with this property, if any. 31 * @property isJvmField indicates if this property should be treated as a jvm field. 32 */ 33 public data class PropertyData( 34 private val annotations: List<AnnotationSpec>, 35 val fieldData: FieldData?, 36 val getterData: MethodData?, 37 val setterData: MethodData?, 38 val isJvmField: Boolean, 39 ) { 40 /** Indicates if this property overrides another from a supertype. */ 41 val isOverride: Boolean = (getterData?.isOverride ?: false) || (setterData?.isOverride ?: false) 42 43 /** 44 * A collection of all annotations on this property including declared ones and any derived from 45 * [fieldData], [getterData], [setterData], and [isJvmField]. 46 */ <lambda>null47 val allAnnotations: Collection<AnnotationSpec> = ClassInspectorUtil.createAnnotations { 48 // Don't add annotations that are already defined on the parent 49 val higherScopedAnnotations = annotations.associateBy { it.typeName } 50 val fieldAnnotations = fieldData?.allAnnotations.orEmpty() 51 .filterNot { it.typeName in higherScopedAnnotations } 52 .associateByTo(LinkedHashMap()) { it.typeName } 53 val getterAnnotations = getterData?.allAnnotations(GET).orEmpty() 54 .filterNot { it.typeName in higherScopedAnnotations } 55 .associateByTo(LinkedHashMap()) { it.typeName } 56 57 val finalTopAnnotations = annotations.toMutableList() 58 59 // If this is a val, and annotation is on both getter and field, we can move it to just the 60 // regular annotations 61 if (setterData == null && !isJvmField) { 62 val sharedAnnotations = getterAnnotations.keys.intersect(fieldAnnotations.keys) 63 for (sharedAnnotation in sharedAnnotations) { 64 // Add it to the top-level annotations without a site-target 65 finalTopAnnotations += getterAnnotations.getValue(sharedAnnotation).toBuilder() 66 .useSiteTarget(null) 67 .build() 68 69 // Remove from field and getter 70 fieldAnnotations.remove(sharedAnnotation) 71 getterAnnotations.remove(sharedAnnotation) 72 } 73 } 74 75 addAll(finalTopAnnotations) 76 addAll(fieldAnnotations.values) 77 addAll(getterAnnotations.values) 78 addAll( 79 setterData?.allAnnotations(SET).orEmpty() 80 .filterNot { it.typeName in higherScopedAnnotations }, 81 ) 82 if (isJvmField) { 83 add(ClassInspectorUtil.JVM_FIELD_SPEC) 84 } 85 } 86 } 87