1 /*
2  * Copyright (C) 2019 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 @file:JvmName("KotlinPoetMetadata")
17 
18 package com.squareup.kotlinpoet.metadata
19 
20 import javax.lang.model.element.TypeElement
21 import kotlin.metadata.KmClass
22 import kotlin.metadata.jvm.KotlinClassMetadata
23 import kotlin.metadata.jvm.Metadata
24 import kotlin.reflect.KClass
25 
26 /**
27  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
28  * @return a new [KmClass] representation of the Kotlin metadata for [this] class.
29  */
toKmClassnull30 internal fun KClass<*>.toKmClass(lenient: Boolean): KmClass = java.toKmClass(lenient)
31 
32 /**
33  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
34  * @return a new [KmClass] representation of the Kotlin metadata for [this] class.
35  */
36 internal fun Class<*>.toKmClass(lenient: Boolean): KmClass = readMetadata(::getAnnotation).toKmClass(lenient)
37 
38 /**
39  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
40  * @return a new [KmClass] representation of the Kotlin metadata for [this] type.
41  */
42 internal fun TypeElement.toKmClass(lenient: Boolean): KmClass = readMetadata(::getAnnotation).toKmClass(lenient)
43 
44 /**
45  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
46  */
47 internal fun Metadata.toKmClass(lenient: Boolean): KmClass {
48   return toKotlinClassMetadata<KotlinClassMetadata.Class>(lenient)
49     .kmClass
50 }
51 
52 /**
53  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
54  */
toKotlinClassMetadatanull55 internal inline fun <reified T : KotlinClassMetadata> Metadata.toKotlinClassMetadata(
56   lenient: Boolean,
57 ): T {
58   val expectedType = T::class
59   val metadata = readKotlinClassMetadata(lenient)
60   return when (expectedType) {
61     KotlinClassMetadata.Class::class -> {
62       check(metadata is KotlinClassMetadata.Class)
63       metadata as T
64     }
65     KotlinClassMetadata.FileFacade::class -> {
66       check(metadata is KotlinClassMetadata.FileFacade)
67       metadata as T
68     }
69     KotlinClassMetadata.SyntheticClass::class ->
70       throw UnsupportedOperationException("SyntheticClass isn't supported yet!")
71     KotlinClassMetadata.MultiFileClassFacade::class ->
72       throw UnsupportedOperationException("MultiFileClassFacade isn't supported yet!")
73     KotlinClassMetadata.MultiFileClassPart::class ->
74       throw UnsupportedOperationException("MultiFileClassPart isn't supported yet!")
75     KotlinClassMetadata.Unknown::class ->
76       throw RuntimeException("Recorded unknown metadata type! $metadata")
77     else -> TODO("Unrecognized KotlinClassMetadata type: $expectedType")
78   }
79 }
80 
81 /**
82  * Returns the [KotlinClassMetadata] this represents. In general you should only use this function
83  * when you don't know what the underlying [KotlinClassMetadata] subtype is, otherwise you should
84  * use one of the more direct functions like [toKmClass].
85  *
86  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
87  */
readKotlinClassMetadatanull88 internal fun Metadata.readKotlinClassMetadata(lenient: Boolean): KotlinClassMetadata {
89   return if (lenient) {
90     KotlinClassMetadata.readLenient(this)
91   } else {
92     KotlinClassMetadata.readStrict(this)
93   }
94 }
95 
readMetadatanull96 private inline fun readMetadata(lookup: ((Class<Metadata>) -> Metadata?)): Metadata {
97   return checkNotNull(lookup.invoke(Metadata::class.java)) {
98     "No Metadata annotation found! Must be Kotlin code built with the standard library on the classpath."
99   }
100 }
101