1 /*
<lambda>null2  * Copyright (C) 2020 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.hilt.android.processor.internal.viewmodel
18 
19 import com.google.auto.common.MoreElements
20 import com.squareup.javapoet.ClassName
21 import dagger.hilt.android.processor.internal.AndroidClassNames
22 import dagger.hilt.processor.internal.ClassNames
23 import dagger.hilt.processor.internal.ProcessorErrors
24 import dagger.hilt.processor.internal.Processors
25 import javax.annotation.processing.ProcessingEnvironment
26 import javax.lang.model.element.Modifier
27 import javax.lang.model.element.NestingKind
28 import javax.lang.model.element.TypeElement
29 import javax.lang.model.util.ElementFilter
30 
31 /**
32  * Data class that represents a Hilt injected ViewModel
33  */
34 internal class ViewModelMetadata private constructor(
35   val typeElement: TypeElement
36 ) {
37   val className = ClassName.get(typeElement)
38 
39   val modulesClassName = ClassName.get(
40     MoreElements.getPackage(typeElement).qualifiedName.toString(),
41     "${className.simpleNames().joinToString("_")}_HiltModules"
42   )
43 
44   companion object {
45     internal fun create(
46       processingEnv: ProcessingEnvironment,
47       typeElement: TypeElement,
48     ): ViewModelMetadata? {
49       val types = processingEnv.typeUtils
50       val elements = processingEnv.elementUtils
51 
52       ProcessorErrors.checkState(
53         types.isSubtype(
54           typeElement.asType(),
55           elements.getTypeElement(AndroidClassNames.VIEW_MODEL.toString()).asType()
56         ),
57         typeElement,
58         "@HiltViewModel is only supported on types that subclass %s.",
59         AndroidClassNames.VIEW_MODEL
60       )
61 
62       ElementFilter.constructorsIn(typeElement.enclosedElements).filter { constructor ->
63         ProcessorErrors.checkState(
64           !Processors.hasAnnotation(constructor, ClassNames.ASSISTED_INJECT),
65           constructor,
66           "ViewModel constructor should be annotated with @Inject instead of @AssistedInject."
67         )
68         Processors.hasAnnotation(constructor, ClassNames.INJECT)
69       }.let { injectConstructors ->
70         ProcessorErrors.checkState(
71           injectConstructors.size == 1,
72           typeElement,
73           "@HiltViewModel annotated class should contain exactly one @Inject " +
74             "annotated constructor."
75         )
76 
77         injectConstructors.forEach { constructor ->
78           ProcessorErrors.checkState(
79             !constructor.modifiers.contains(Modifier.PRIVATE),
80             constructor,
81             "@Inject annotated constructors must not be private."
82           )
83         }
84       }
85 
86       ProcessorErrors.checkState(
87         typeElement.nestingKind != NestingKind.MEMBER ||
88           typeElement.modifiers.contains(Modifier.STATIC),
89         typeElement,
90         "@HiltViewModel may only be used on inner classes if they are static."
91       )
92 
93       Processors.getScopeAnnotations(typeElement).let { scopeAnnotations ->
94         ProcessorErrors.checkState(
95           scopeAnnotations.isEmpty(),
96           typeElement,
97           "@HiltViewModel classes should not be scoped. Found: %s",
98           scopeAnnotations.joinToString()
99         )
100       }
101 
102       return ViewModelMetadata(
103         typeElement
104       )
105     }
106   }
107 }
108