blob: 789fbfea23a42194bf7ae8d00ff597add1dee759 [file] [log] [blame]
/*
* Copyright (C) 2020 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.hilt.android.processor.internal.viewmodel
import com.google.auto.common.MoreElements
import com.squareup.javapoet.ClassName
import dagger.hilt.android.processor.internal.AndroidClassNames
import dagger.hilt.processor.internal.ClassNames
import dagger.hilt.processor.internal.ProcessorErrors
import dagger.hilt.processor.internal.Processors
import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.Modifier
import javax.lang.model.element.NestingKind
import javax.lang.model.element.TypeElement
import javax.lang.model.util.ElementFilter
/**
* Data class that represents a Hilt injected ViewModel
*/
internal class ViewModelMetadata private constructor(
val typeElement: TypeElement
) {
val className = ClassName.get(typeElement)
val modulesClassName = ClassName.get(
MoreElements.getPackage(typeElement).qualifiedName.toString(),
"${className.simpleNames().joinToString("_")}_HiltModules"
)
companion object {
internal fun create(
processingEnv: ProcessingEnvironment,
typeElement: TypeElement,
): ViewModelMetadata? {
val types = processingEnv.typeUtils
val elements = processingEnv.elementUtils
ProcessorErrors.checkState(
types.isSubtype(
typeElement.asType(),
elements.getTypeElement(AndroidClassNames.VIEW_MODEL.toString()).asType()
),
typeElement,
"@HiltViewModel is only supported on types that subclass %s.",
AndroidClassNames.VIEW_MODEL
)
ElementFilter.constructorsIn(typeElement.enclosedElements).filter { constructor ->
ProcessorErrors.checkState(
!Processors.hasAnnotation(constructor, ClassNames.ASSISTED_INJECT),
constructor,
"ViewModel constructor should be annotated with @Inject instead of @AssistedInject."
)
Processors.hasAnnotation(constructor, ClassNames.INJECT)
}.let { injectConstructors ->
ProcessorErrors.checkState(
injectConstructors.size == 1,
typeElement,
"@HiltViewModel annotated class should contain exactly one @Inject " +
"annotated constructor."
)
injectConstructors.forEach { constructor ->
ProcessorErrors.checkState(
!constructor.modifiers.contains(Modifier.PRIVATE),
constructor,
"@Inject annotated constructors must not be private."
)
}
}
ProcessorErrors.checkState(
typeElement.nestingKind != NestingKind.MEMBER ||
typeElement.modifiers.contains(Modifier.STATIC),
typeElement,
"@HiltViewModel may only be used on inner classes if they are static."
)
Processors.getScopeAnnotations(typeElement).let { scopeAnnotations ->
ProcessorErrors.checkState(
scopeAnnotations.isEmpty(),
typeElement,
"@HiltViewModel classes should not be scoped. Found: %s",
scopeAnnotations.joinToString()
)
}
return ViewModelMetadata(
typeElement
)
}
}
}