• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Dagger Authors.
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 dagger.internal.codegen.kotlin;
18 
19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
20 import static com.google.auto.common.MoreElements.isAnnotationPresent;
21 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
22 import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
23 import static kotlinx.metadata.Flag.Class.IS_DATA;
24 import static kotlinx.metadata.Flag.Class.IS_OBJECT;
25 import static kotlinx.metadata.Flag.IS_PRIVATE;
26 
27 import com.google.common.collect.ImmutableCollection;
28 import com.google.common.collect.ImmutableList;
29 import dagger.internal.codegen.extension.DaggerCollectors;
30 import java.lang.annotation.Annotation;
31 import java.util.Optional;
32 import javax.inject.Inject;
33 import javax.lang.model.element.AnnotationMirror;
34 import javax.lang.model.element.Element;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.element.TypeElement;
37 import javax.lang.model.element.VariableElement;
38 import javax.lang.model.util.ElementFilter;
39 import kotlin.Metadata;
40 import kotlin.jvm.JvmStatic;
41 import kotlinx.metadata.Flag;
42 
43 /** Utility class for interacting with Kotlin Metadata. */
44 public final class KotlinMetadataUtil {
45 
46   private final KotlinMetadataFactory metadataFactory;
47 
48   @Inject
KotlinMetadataUtil(KotlinMetadataFactory metadataFactory)49   KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
50     this.metadataFactory = metadataFactory;
51   }
52 
53   /**
54    * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
55    * an element that does.
56    */
hasMetadata(Element element)57   public boolean hasMetadata(Element element) {
58     return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class);
59   }
60 
61   /**
62    * Returns the synthetic annotations of a Kotlin property.
63    *
64    * <p>Note that this method only looks for additional annotations in the synthetic property
65    * method, if any, of a Kotlin property and not for annotations in its backing field.
66    */
getSyntheticPropertyAnnotations( VariableElement fieldElement, Class<? extends Annotation> annotationType)67   public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations(
68       VariableElement fieldElement, Class<? extends Annotation> annotationType) {
69     return metadataFactory
70         .create(fieldElement)
71         .getSyntheticAnnotationMethod(fieldElement)
72         .map(methodElement -> getAnnotatedAnnotations(methodElement, annotationType).asList())
73         .orElse(ImmutableList.of());
74   }
75 
76   /**
77    * Returns {@code true} if the synthetic method for annotations is missing. This can occur when
78    * the Kotlin metadata of the property reports that it contains a synthetic method for annotations
79    * but such method is not found since it is synthetic and ignored by the processor.
80    */
isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement)81   public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) {
82     return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement);
83   }
84 
85   /** Returns {@code true} if this type element is a Kotlin Object. */
isObjectClass(TypeElement typeElement)86   public boolean isObjectClass(TypeElement typeElement) {
87     return hasMetadata(typeElement)
88         && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT);
89   }
90 
91   /** Returns {@code true} if this type element is a Kotlin data class. */
isDataClass(TypeElement typeElement)92   public boolean isDataClass(TypeElement typeElement) {
93     return hasMetadata(typeElement)
94         && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA);
95   }
96 
97   /* Returns {@code true} if this type element is a Kotlin Companion Object. */
isCompanionObjectClass(TypeElement typeElement)98   public boolean isCompanionObjectClass(TypeElement typeElement) {
99     return hasMetadata(typeElement)
100         && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT);
101   }
102 
103   /** Returns {@code true} if this type element is a Kotlin object or companion object. */
isObjectOrCompanionObjectClass(TypeElement typeElement)104   public boolean isObjectOrCompanionObjectClass(TypeElement typeElement) {
105     return isObjectClass(typeElement) || isCompanionObjectClass(typeElement);
106   }
107 
108   /* Returns {@code true} if this type element has a Kotlin Companion Object. */
hasEnclosedCompanionObject(TypeElement typeElement)109   public boolean hasEnclosedCompanionObject(TypeElement typeElement) {
110     return hasMetadata(typeElement)
111         && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent();
112   }
113 
114   /* Returns the Companion Object element enclosed by the given type element. */
getEnclosedCompanionObject(TypeElement typeElement)115   public TypeElement getEnclosedCompanionObject(TypeElement typeElement) {
116     return metadataFactory
117         .create(typeElement)
118         .classMetadata()
119         .companionObjectName()
120         .map(
121             companionObjectName ->
122                 ElementFilter.typesIn(typeElement.getEnclosedElements()).stream()
123                     .filter(
124                         innerType -> innerType.getSimpleName().contentEquals(companionObjectName))
125                     .collect(DaggerCollectors.onlyElement()))
126         .get();
127   }
128 
129   /**
130    * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin
131    * source.
132    */
isVisibilityPrivate(TypeElement typeElement)133   public boolean isVisibilityPrivate(TypeElement typeElement) {
134     return hasMetadata(typeElement)
135         && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE);
136   }
137 
138   /**
139    * Returns {@code true} if the given type element was declared {@code internal} in its Kotlin
140    * source.
141    */
isVisibilityInternal(TypeElement type)142   public boolean isVisibilityInternal(TypeElement type) {
143     return hasMetadata(type)
144         && metadataFactory.create(type).classMetadata().flags(Flag.IS_INTERNAL);
145   }
146 
147   /**
148    * Returns {@code true} if the given executable element was declared {@code internal} in its
149    * Kotlin source.
150    */
isVisibilityInternal(ExecutableElement method)151   public boolean isVisibilityInternal(ExecutableElement method) {
152     return hasMetadata(method)
153         && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL);
154   }
155 
getPropertyGetter(VariableElement fieldElement)156   public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) {
157     return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement);
158   }
159 
containsConstructorWithDefaultParam(TypeElement typeElement)160   public boolean containsConstructorWithDefaultParam(TypeElement typeElement) {
161     return hasMetadata(typeElement)
162         && metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
163   }
164 
165   /**
166    * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element.
167    */
isJvmStaticPresent(ExecutableElement element)168   public static boolean isJvmStaticPresent(ExecutableElement element) {
169     return isAnnotationPresent(element, JvmStatic.class);
170   }
171 }
172