aboutsummaryrefslogtreecommitdiff
path: root/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java
blob: 76d28f0ce32628d372c5aa094102ab861057ca11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright (C) 2019 The Dagger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dagger.internal.codegen.kotlin;

import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
import static kotlinx.metadata.Flag.Class.IS_DATA;
import static kotlinx.metadata.Flag.Class.IS_OBJECT;
import static kotlinx.metadata.Flag.IS_PRIVATE;

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import dagger.internal.codegen.extension.DaggerCollectors;
import java.lang.annotation.Annotation;
import java.util.Optional;
import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import kotlin.Metadata;
import kotlin.jvm.JvmStatic;
import kotlinx.metadata.Flag;

/** Utility class for interacting with Kotlin Metadata. */
public final class KotlinMetadataUtil {

  private final KotlinMetadataFactory metadataFactory;

  @Inject
  KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
    this.metadataFactory = metadataFactory;
  }

  /**
   * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
   * an element that does.
   */
  public boolean hasMetadata(Element element) {
    return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class);
  }

  /**
   * Returns the synthetic annotations of a Kotlin property.
   *
   * <p>Note that this method only looks for additional annotations in the synthetic property
   * method, if any, of a Kotlin property and not for annotations in its backing field.
   */
  public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations(
      VariableElement fieldElement, Class<? extends Annotation> annotationType) {
    return metadataFactory
        .create(fieldElement)
        .getSyntheticAnnotationMethod(fieldElement)
        .map(methodElement -> getAnnotatedAnnotations(methodElement, annotationType).asList())
        .orElse(ImmutableList.of());
  }

  /**
   * Returns {@code true} if the synthetic method for annotations is missing. This can occur when
   * the Kotlin metadata of the property reports that it contains a synthetic method for annotations
   * but such method is not found since it is synthetic and ignored by the processor.
   */
  public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) {
    return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement);
  }

  /** Returns {@code true} if this type element is a Kotlin Object. */
  public boolean isObjectClass(TypeElement typeElement) {
    return hasMetadata(typeElement)
        && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT);
  }

  /** Returns {@code true} if this type element is a Kotlin data class. */
  public boolean isDataClass(TypeElement typeElement) {
    return hasMetadata(typeElement)
        && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA);
  }

  /* Returns {@code true} if this type element is a Kotlin Companion Object. */
  public boolean isCompanionObjectClass(TypeElement typeElement) {
    return hasMetadata(typeElement)
        && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT);
  }

  /* Returns {@code true} if this type element has a Kotlin Companion Object. */
  public boolean hasEnclosedCompanionObject(TypeElement typeElement) {
    return hasMetadata(typeElement)
        && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent();
  }

  /* Returns the Companion Object element enclosed by the given type element. */
  public TypeElement getEnclosedCompanionObject(TypeElement typeElement) {
    return metadataFactory
        .create(typeElement)
        .classMetadata()
        .companionObjectName()
        .map(
            companionObjectName ->
                ElementFilter.typesIn(typeElement.getEnclosedElements()).stream()
                    .filter(
                        innerType -> innerType.getSimpleName().contentEquals(companionObjectName))
                    .collect(DaggerCollectors.onlyElement()))
        .get();
  }

  /**
   * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin
   * source.
   */
  public boolean isVisibilityPrivate(TypeElement typeElement) {
    return hasMetadata(typeElement)
        && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE);
  }

  /**
   * Returns {@code true} if the given executable element was declared {@code internal} in its
   * Kotlin source.
   */
  public boolean isVisibilityInternal(ExecutableElement method) {
    return hasMetadata(method)
        && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL);
  }

  public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) {
    return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement);
  }

  public boolean containsConstructorWithDefaultParam(TypeElement typeElement) {
    return hasMetadata(typeElement)
        && metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
  }

  /**
   * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element.
   */
  public static boolean isJvmStaticPresent(ExecutableElement element) {
    return isAnnotationPresent(element, JvmStatic.class);
  }
}