Kapt: Implement Annotation Processing plugin in Kotlin (KT-13499)
(cherry picked from commit 32c461a)
This commit is contained in:
committed by
Yan Zhulanow
parent
cc12a6c228
commit
0181dd000d
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing
|
||||
|
||||
class AnnotationProcessingConfigurationService(val aptClassLoader: ClassLoader)
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.annotation.processing.impl.*
|
||||
import org.jetbrains.kotlin.asJava.findFacadeClass
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.java.model.elements.JeTypeElement
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.*
|
||||
import javax.annotation.processing.Processor
|
||||
|
||||
class AnnotationProcessingExtension(
|
||||
val generatedOutputDir: File,
|
||||
val annotationProcessingClasspath: List<String>
|
||||
) : AnalysisCompletedHandlerExtension {
|
||||
private var annotationProcessingComplete = false
|
||||
|
||||
override fun analysisCompleted(
|
||||
project: Project,
|
||||
module: ModuleDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
files: Collection<KtFile>
|
||||
): AnalysisResult? {
|
||||
if (annotationProcessingComplete) {
|
||||
return null
|
||||
}
|
||||
|
||||
val processors = loadAnnotationProcessors(annotationProcessingClasspath)
|
||||
if (processors.isEmpty()) return null
|
||||
|
||||
val analysisContext = AnalysisContext(hashMapOf())
|
||||
analysisContext.analyzeFiles(files)
|
||||
|
||||
val options = emptyMap<String, String>()
|
||||
|
||||
val javaPsiFacade = JavaPsiFacade.getInstance(project)
|
||||
val projectScope = GlobalSearchScope.projectScope(project)
|
||||
|
||||
val filer = KotlinFiler(generatedOutputDir)
|
||||
val messages = KotlinMessager()
|
||||
val types = KotlinTypes(javaPsiFacade, projectScope)
|
||||
val elements = KotlinElements(javaPsiFacade, projectScope)
|
||||
|
||||
val processingEnvironment = KotlinProcessingEnvironment(elements, types, messages, options, filer)
|
||||
processors.forEach { it.init(processingEnvironment) }
|
||||
|
||||
|
||||
// Round 1
|
||||
val round1Environment = KotlinRoundEnvironment(analysisContext)
|
||||
for (processor in processors) {
|
||||
val supportedAnnotationNames = processor.supportedAnnotationTypes.filter { it in analysisContext.annotationsMap }
|
||||
if (supportedAnnotationNames.isEmpty()) continue
|
||||
|
||||
val supportedAnnotations = supportedAnnotationNames
|
||||
.map { javaPsiFacade.findClass(it, projectScope)?.let { JeTypeElement(it) } }
|
||||
.filterNotNullTo(hashSetOf())
|
||||
|
||||
processor.process(supportedAnnotations, round1Environment)
|
||||
}
|
||||
|
||||
// Round 2
|
||||
val round2Environment = KotlinRoundEnvironment(AnalysisContext(hashMapOf()), isProcessingOver = true)
|
||||
for (processor in processors) {
|
||||
processor.process(emptySet(), round2Environment)
|
||||
}
|
||||
|
||||
annotationProcessingComplete = true
|
||||
return AnalysisResult.RetryWithAdditionalJavaRoots(bindingContext, module, listOf(generatedOutputDir))
|
||||
}
|
||||
|
||||
private fun loadAnnotationProcessors(classpath: List<String>): List<Processor> {
|
||||
val classLoader = URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray())
|
||||
return ServiceLoader.load(Processor::class.java, classLoader).toList()
|
||||
}
|
||||
}
|
||||
|
||||
internal class AnalysisContext(val annotationsMap: MutableMap<String, MutableList<PsiModifierListOwner>>)
|
||||
|
||||
private fun AnalysisContext.analyzeFiles(files: Collection<KtFile>) {
|
||||
for (file in files) {
|
||||
analyzeFile(file)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AnalysisContext.analyzeFile(file: KtFile) {
|
||||
val lightClass = file.findFacadeClass()
|
||||
|
||||
if (lightClass != null) {
|
||||
analyzeDeclaration(lightClass)
|
||||
}
|
||||
|
||||
for (declaration in file.declarations) {
|
||||
if (declaration !is KtClassOrObject) continue
|
||||
val clazz = declaration.toLightClass() ?: continue
|
||||
analyzeDeclaration(clazz)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AnalysisContext.analyzeDeclaration(declaration: PsiElement) {
|
||||
if (declaration !is PsiModifierListOwner) return
|
||||
|
||||
val annotations = declaration.modifierList?.annotations
|
||||
if (annotations != null) {
|
||||
for (annotation in annotations) {
|
||||
val fqName = annotation.qualifiedName ?: continue
|
||||
annotationsMap.getOrPut(fqName, { mutableListOf() }).add(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is PsiClass) {
|
||||
declaration.methods.forEach { analyzeDeclaration(it) }
|
||||
declaration.fields.forEach { analyzeDeclaration(it) }
|
||||
declaration.innerClasses.forEach { analyzeDeclaration(it) }
|
||||
}
|
||||
|
||||
when (declaration) {
|
||||
is KtClassOrObject -> declaration.declarations.forEach { analyzeDeclaration(it) }
|
||||
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import org.jetbrains.kotlin.annotation.AnnotationProcessingExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
||||
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
|
||||
import java.io.File
|
||||
|
||||
object AnnotationProcessingConfigurationKeys {
|
||||
val GENERATED_OUTPUT_DIR: CompilerConfigurationKey<String> =
|
||||
CompilerConfigurationKey.create<String>("generated files output directory")
|
||||
|
||||
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
|
||||
CompilerConfigurationKey.create<List<String>>("annotation processor classpath")
|
||||
}
|
||||
|
||||
class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
|
||||
companion object {
|
||||
val ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt2"
|
||||
|
||||
val GENERATED_OUTPUT_DIR_OPTION: CliOption =
|
||||
CliOption("generated", "<path>", "Output path for generated files", required = false)
|
||||
|
||||
val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption =
|
||||
CliOption("apclasspath", "<classpath>", "Annotation processor classpath",
|
||||
required = false, allowMultipleOccurrences = true)
|
||||
}
|
||||
|
||||
override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID
|
||||
|
||||
override val pluginOptions: Collection<CliOption> =
|
||||
listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION)
|
||||
|
||||
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
|
||||
when (option) {
|
||||
ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> {
|
||||
val paths = configuration.getList(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH).toMutableList()
|
||||
paths.add(value)
|
||||
configuration.put(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH, paths)
|
||||
}
|
||||
GENERATED_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR, value)
|
||||
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnnotationProcessingComponentRegistrar : ComponentRegistrar {
|
||||
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val generatedOutputDir = configuration.get(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR) ?: return
|
||||
val classpath = configuration.get(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH) ?: return
|
||||
|
||||
val generatedOutputDirFile = File(generatedOutputDir)
|
||||
generatedOutputDirFile.mkdirs()
|
||||
|
||||
val annotationProcessingExtension = AnnotationProcessingExtension(generatedOutputDirFile, classpath)
|
||||
AnalysisCompletedHandlerExtension.registerExtension(project, annotationProcessingExtension)
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiSuperMethodUtil
|
||||
import com.intellij.psi.util.PsiTypesUtil
|
||||
import org.jetbrains.kotlin.java.model.JeAnnotationOwner
|
||||
import org.jetbrains.kotlin.java.model.JeElement
|
||||
import org.jetbrains.kotlin.java.model.JeName
|
||||
import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror
|
||||
import org.jetbrains.kotlin.java.model.elements.JeMethodExecutableElement
|
||||
import org.jetbrains.kotlin.java.model.elements.JePackageElement
|
||||
import org.jetbrains.kotlin.java.model.elements.JeTypeElement
|
||||
import java.io.PrintWriter
|
||||
import java.io.Writer
|
||||
import javax.lang.model.element.*
|
||||
import javax.lang.model.util.Elements
|
||||
|
||||
class KotlinElements(val javaPsiFacade: JavaPsiFacade, val scope: GlobalSearchScope) : Elements {
|
||||
override fun hides(hider: Element, hidden: Element): Boolean {
|
||||
val hiderMethod = (hider as? JeMethodExecutableElement)?.psi ?: return false
|
||||
val hiddenMethod = (hidden as? JeMethodExecutableElement)?.psi ?: return false
|
||||
|
||||
if (hiderMethod.name != hiddenMethod.name) return false
|
||||
if (hiderMethod.parameterList.parametersCount != hiddenMethod.parameterList.parametersCount) return false
|
||||
|
||||
val hiderMethodClass = hiderMethod.containingClass ?: return false
|
||||
val hiddenMethodClass = hiddenMethod.containingClass ?: return false
|
||||
|
||||
if (PsiTypesUtil.getClassType(hiddenMethodClass) !in hiderMethodClass.superTypes) return false
|
||||
|
||||
if (hiderMethod.returnType != hiddenMethod.returnType) return false
|
||||
for (i in 0..hiderMethod.parameterList.parametersCount - 1) {
|
||||
if (hiderMethod.parameterList.parameters[i].type != hiddenMethod.parameterList.parameters[i].type) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun overrides(overrider: ExecutableElement, overridden: ExecutableElement, type: TypeElement): Boolean {
|
||||
overrider as? JeMethodExecutableElement ?: return false
|
||||
overridden as? JeMethodExecutableElement ?: return false
|
||||
|
||||
return PsiSuperMethodUtil.isSuperMethod(overrider.psi, overridden.psi)
|
||||
}
|
||||
|
||||
override fun getName(cs: CharSequence?) = JeName(cs?.toString())
|
||||
|
||||
override fun getElementValuesWithDefaults(a: AnnotationMirror): Map<out ExecutableElement, AnnotationValue> {
|
||||
a as? JeAnnotationMirror ?: return emptyMap()
|
||||
return a.getAllElementValues()
|
||||
}
|
||||
|
||||
override fun getBinaryName(type: TypeElement) = JeName((type as JeTypeElement).psi.qualifiedName)
|
||||
|
||||
override fun getDocComment(e: Element?) = ""
|
||||
|
||||
override fun isDeprecated(e: Element?): Boolean {
|
||||
return (e as? JeAnnotationOwner)?.annotationOwner?.findAnnotation("java.lang.Deprecated") != null
|
||||
}
|
||||
|
||||
override fun getAllMembers(type: TypeElement) = (type as? JeTypeElement)?.getAllMembers() ?: emptyList()
|
||||
|
||||
override fun printElements(w: Writer, vararg elements: Element) {
|
||||
val printWriter = PrintWriter(w)
|
||||
for (element in elements) {
|
||||
printWriter.println(element.simpleName.toString() + " (" + element.javaClass.name + ")")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPackageElement(name: CharSequence): PackageElement? {
|
||||
val psiPackage = javaPsiFacade.findPackage(name.toString()) ?: return null
|
||||
return JePackageElement(psiPackage)
|
||||
}
|
||||
|
||||
override fun getTypeElement(name: CharSequence): TypeElement? {
|
||||
val psiClass = javaPsiFacade.findClass(name.toString(), scope) ?: return null
|
||||
return JeTypeElement(psiClass)
|
||||
}
|
||||
|
||||
override fun getConstantExpression(value: Any?) = Constants.format(value)
|
||||
|
||||
override tailrec fun getPackageOf(element: Element): PackageElement? {
|
||||
if (element is PackageElement) return element
|
||||
val parent = element.enclosingElement ?: return null
|
||||
return getPackageOf(parent)
|
||||
}
|
||||
|
||||
override fun getAllAnnotationMirrors(e: Element): List<AnnotationMirror> {
|
||||
val annotations = (e as? JeElement)?.annotationMirrors?.toMutableList() ?: return emptyList()
|
||||
|
||||
if (e is JeTypeElement) {
|
||||
var parent = e.psi.superClass
|
||||
while (parent != null) {
|
||||
val parentAnnotations = parent.modifierList?.annotations
|
||||
if (parentAnnotations == null) {
|
||||
parent = parent.superClass
|
||||
continue
|
||||
}
|
||||
|
||||
for (parentAnnotation in parentAnnotations) {
|
||||
val annotationClass = parentAnnotation.nameReferenceElement?.resolve() as? PsiClass ?: continue
|
||||
annotationClass.modifierList?.findAnnotation("java.lang.annotation.Inherited") ?: continue
|
||||
annotations += JeAnnotationMirror(parentAnnotation)
|
||||
}
|
||||
|
||||
parent = parent.superClass
|
||||
}
|
||||
}
|
||||
|
||||
return annotations
|
||||
}
|
||||
|
||||
override fun isFunctionalInterface(type: TypeElement): Boolean {
|
||||
val jeTypeElement = type as? JeTypeElement ?: return false
|
||||
if (!jeTypeElement.psi.isInterface) return false
|
||||
if (jeTypeElement.psi.allMethods.size != 1) return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
object Constants {
|
||||
fun format(value: Any?): String {
|
||||
return when (value) {
|
||||
is Byte -> formatByte((value as Byte?)!!)
|
||||
is Short -> formatShort((value as Short?)!!)
|
||||
is Long -> formatLong((value as Long?)!!)
|
||||
is Float -> formatFloat((value as Float?)!!)
|
||||
is Double -> formatDouble((value as Double?)!!)
|
||||
is Char -> formatChar(value)
|
||||
is String -> formatString(value)
|
||||
is Int, is Boolean -> value.toString()
|
||||
else -> throw IllegalArgumentException(
|
||||
"Argument is not a primitive type or a string; it " +
|
||||
(if (value == null) "is a null value." else "has class " + value.javaClass.name) + ".")
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatByte(b: Byte) = String.format("(byte)0x%02x", b)
|
||||
private fun formatShort(s: Short) = String.format("(short)%d", s)
|
||||
private fun formatLong(lng: Long) = "${lng}L"
|
||||
private fun formatChar(c: Char) = '\'' + quote(c) + '\''
|
||||
private fun formatString(s: String) = '"' + quote(s) + '"'
|
||||
|
||||
private fun formatFloat(f: Float): String {
|
||||
if (java.lang.Float.isNaN(f))
|
||||
return "0.0f/0.0f"
|
||||
else if (java.lang.Float.isInfinite(f))
|
||||
return if (f < 0) "-1.0f/0.0f" else "1.0f/0.0f"
|
||||
else
|
||||
return "${f}f"
|
||||
}
|
||||
|
||||
private fun formatDouble(d: Double): String {
|
||||
if (java.lang.Double.isNaN(d))
|
||||
return "0.0/0.0"
|
||||
else if (java.lang.Double.isInfinite(d))
|
||||
return if (d < 0) "-1.0/0.0" else "1.0/0.0"
|
||||
else
|
||||
return d.toString()
|
||||
}
|
||||
|
||||
fun quote(ch: Char): String {
|
||||
return when (ch) {
|
||||
'\b' -> "\\b"
|
||||
'\n' -> "\\n"
|
||||
'\r' -> "\\r"
|
||||
'\t' -> "\\t"
|
||||
'\'' -> "\\'"
|
||||
'\"' -> "\\\""
|
||||
'\\' -> "\\\\"
|
||||
else -> if (isPrintableAscii(ch)) ch.toString() else String.format("\\u%04x", ch.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
fun quote(s: String): String {
|
||||
val buf = StringBuilder()
|
||||
for (i in 0..s.length - 1) {
|
||||
buf.append(quote(s[i]))
|
||||
}
|
||||
return buf.toString()
|
||||
}
|
||||
|
||||
private fun isPrintableAscii(ch: Char) = ch >= ' ' && ch <= '~'
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import java.io.File
|
||||
import javax.annotation.processing.Filer
|
||||
import javax.lang.model.element.Element
|
||||
import javax.tools.FileObject
|
||||
import javax.tools.JavaFileManager
|
||||
import javax.tools.JavaFileObject
|
||||
|
||||
class KotlinFiler(val generatedDir: File) : Filer {
|
||||
private companion object {
|
||||
val PACKAGE_INFO_SUFFIX = ".packageInfo"
|
||||
}
|
||||
|
||||
private var dotGeneratedGenerated = false
|
||||
|
||||
private fun createDotGeneratedIfNeeded() {
|
||||
if (!dotGeneratedGenerated) {
|
||||
dotGeneratedGenerated = true
|
||||
File(generatedDir, ".generated").writeText("Generated by kapt2")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGeneratedFile(nameCharSequence: CharSequence, extension: String): File {
|
||||
createDotGeneratedIfNeeded()
|
||||
|
||||
val name = nameCharSequence.toString()
|
||||
val isPackageInfo = name.endsWith(PACKAGE_INFO_SUFFIX)
|
||||
val fqName = if (isPackageInfo) name.substring(0, PACKAGE_INFO_SUFFIX.length) else name
|
||||
|
||||
val packageName = fqName.substringBeforeLast('.')
|
||||
|
||||
val packageDir = File(generatedDir, packageName.replace('.', '/'))
|
||||
packageDir.mkdirs()
|
||||
|
||||
val fileName = fqName.substringAfterLast('.') + (if (isPackageInfo) PACKAGE_INFO_SUFFIX else extension)
|
||||
return File(packageDir, fileName)
|
||||
}
|
||||
|
||||
override fun createSourceFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
|
||||
return KotlinJavaFileObject(getGeneratedFile(name, ".java"))
|
||||
}
|
||||
|
||||
override fun getResource(location: JavaFileManager.Location, pkg: CharSequence, relativeName: CharSequence): FileObject? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun createResource(
|
||||
location: JavaFileManager.Location?,
|
||||
pkg: CharSequence,
|
||||
relativeName: CharSequence,
|
||||
vararg originatingElements: Element?
|
||||
): FileObject? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun createClassFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject {
|
||||
return KotlinJavaFileObject(getGeneratedFile(name, ".class"))
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import java.io.File
|
||||
import javax.tools.JavaFileObject
|
||||
|
||||
class KotlinJavaFileObject(val file: File) : JavaFileObject {
|
||||
override fun openOutputStream() = file.outputStream()
|
||||
|
||||
override fun getName() = file.name
|
||||
|
||||
override fun openWriter() = file.writer()
|
||||
|
||||
override fun openInputStream() = file.inputStream()
|
||||
|
||||
override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence? {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun getLastModified() = file.lastModified()
|
||||
|
||||
override fun toUri() = file.toURI()
|
||||
|
||||
override fun openReader(ignoreEncodingErrors: Boolean) = file.reader()
|
||||
|
||||
override fun delete() = file.delete()
|
||||
|
||||
//TODO
|
||||
override fun isNameCompatible(simpleName: String, kind: JavaFileObject.Kind) = true
|
||||
|
||||
override fun getKind() = when (file.extension) {
|
||||
"class" -> JavaFileObject.Kind.CLASS
|
||||
"java" -> JavaFileObject.Kind.SOURCE
|
||||
"html" -> JavaFileObject.Kind.HTML
|
||||
else -> JavaFileObject.Kind.OTHER
|
||||
}
|
||||
|
||||
//TODO
|
||||
override fun getAccessLevel() = null
|
||||
|
||||
//TODO
|
||||
override fun getNestingKind() = null
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import javax.annotation.processing.Messager
|
||||
import javax.lang.model.element.AnnotationMirror
|
||||
import javax.lang.model.element.AnnotationValue
|
||||
import javax.lang.model.element.Element
|
||||
import javax.tools.Diagnostic
|
||||
|
||||
class KotlinMessager : Messager {
|
||||
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence) = printMessage(kind, msg, null)
|
||||
|
||||
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?) = printMessage(kind, msg, e, null)
|
||||
|
||||
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?, a: AnnotationMirror?) {
|
||||
printMessage(kind, msg, e, a, null)
|
||||
}
|
||||
|
||||
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?, a: AnnotationMirror?, v: AnnotationValue?) {
|
||||
println(msg)
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import java.util.*
|
||||
import javax.annotation.processing.ProcessingEnvironment
|
||||
import javax.lang.model.SourceVersion
|
||||
|
||||
class KotlinProcessingEnvironment(
|
||||
private val elements: KotlinElements,
|
||||
private val types: KotlinTypes,
|
||||
private val messager: KotlinMessager,
|
||||
options: Map<String, String>,
|
||||
private val filer: KotlinFiler
|
||||
) : ProcessingEnvironment {
|
||||
private val options = Collections.unmodifiableMap(options)
|
||||
|
||||
override fun getElementUtils() = elements
|
||||
override fun getTypeUtils() = types
|
||||
override fun getMessager() = messager
|
||||
override fun getLocale() = Locale.getDefault()
|
||||
override fun getSourceVersion() = SourceVersion.RELEASE_6
|
||||
override fun getOptions() = options
|
||||
override fun getFiler() = filer
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import org.jetbrains.kotlin.annotation.AnalysisContext
|
||||
import org.jetbrains.kotlin.java.model.JeConverter
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.element.Element
|
||||
import javax.lang.model.element.TypeElement
|
||||
|
||||
internal class KotlinRoundEnvironment(
|
||||
private val context: AnalysisContext,
|
||||
private val isProcessingOver: Boolean = false) : RoundEnvironment {
|
||||
private var isError = false
|
||||
|
||||
override fun getRootElements() = emptySet<Element>()
|
||||
|
||||
override fun processingOver() = isProcessingOver
|
||||
|
||||
private fun getElementsAnnotatedWith(fqName: String): Set<Element> {
|
||||
val declarations = context.annotationsMap[fqName] ?: return emptySet()
|
||||
return hashSetOf<Element>().apply {
|
||||
for (declaration in declarations) {
|
||||
JeConverter.convert(declaration)?.let { add(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getElementsAnnotatedWith(a: TypeElement) = getElementsAnnotatedWith(a.qualifiedName.toString())
|
||||
override fun getElementsAnnotatedWith(a: Class<out Annotation>) = getElementsAnnotatedWith(a.canonicalName)
|
||||
|
||||
override fun errorRaised() = isError
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.annotation.processing.impl
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.PsiSubstitutorImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTypesUtil
|
||||
import com.intellij.psi.util.TypeConversionUtil
|
||||
import org.jetbrains.kotlin.java.model.elements.JeClassInitializerExecutableElement
|
||||
import org.jetbrains.kotlin.java.model.elements.JeMethodExecutableElement
|
||||
import org.jetbrains.kotlin.java.model.elements.JeTypeElement
|
||||
import org.jetbrains.kotlin.java.model.elements.JeVariableElement
|
||||
import org.jetbrains.kotlin.java.model.types.*
|
||||
import javax.lang.model.element.Element
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.type.*
|
||||
import javax.lang.model.util.Types
|
||||
|
||||
class KotlinTypes(val javaPsiFacade: JavaPsiFacade, val scope: GlobalSearchScope) : Types {
|
||||
override fun contains(t1: TypeMirror, t2: TypeMirror): Boolean {
|
||||
t1 as? JeAbstractType ?: return false
|
||||
t2 as? JeAbstractType ?: return false
|
||||
|
||||
val classType = t1.psiType as? PsiClassType ?: return false
|
||||
return t2.psiType in classType.parameters
|
||||
}
|
||||
|
||||
override fun getArrayType(componentType: TypeMirror): ArrayType {
|
||||
val psiType = (componentType as? JeAbstractType)?.psiType
|
||||
?: throw IllegalArgumentException("Invalid component type: $componentType")
|
||||
return JeArrayType(PsiArrayType(psiType))
|
||||
}
|
||||
|
||||
override fun isAssignable(t1: TypeMirror, t2: TypeMirror): Boolean {
|
||||
fun error(t: TypeMirror?): Nothing = throw IllegalArgumentException("Invalid type: $t")
|
||||
if (t1 is ExecutableType || t1 is NoType) error(t1)
|
||||
if (t2 is ExecutableType || t2 is NoType) error(t2)
|
||||
|
||||
t1 as? JeAbstractType ?: return false
|
||||
t2 as? JeAbstractType ?: return false
|
||||
return t1.psiType.isAssignableFrom(t2.psiType)
|
||||
}
|
||||
|
||||
override fun getNullType() = JeNullType
|
||||
|
||||
override fun getWildcardType(extendsBound: TypeMirror, superBound: TypeMirror): JeWildcardTypeWithBounds {
|
||||
return JeWildcardTypeWithBounds(extendsBound, superBound)
|
||||
}
|
||||
|
||||
override fun unboxedType(t: TypeMirror): PrimitiveType? {
|
||||
fun error(): Nothing = throw IllegalArgumentException("This type could not be unboxed: $t")
|
||||
t as? JeAbstractType ?: error()
|
||||
val unboxedType = PsiPrimitiveType.getUnboxedType(t.psiType) ?: error()
|
||||
return unboxedType.toJePrimitiveType()
|
||||
}
|
||||
|
||||
override fun getPrimitiveType(kind: TypeKind) = kind.toJePrimitiveType()
|
||||
|
||||
override fun erasure(t: TypeMirror): TypeMirror {
|
||||
if (t is NoType) throw IllegalArgumentException("Invalid type: $t")
|
||||
t as? JeAbstractType ?: return t
|
||||
return TypeConversionUtil.erasure(t.psiType).toJeType()
|
||||
}
|
||||
|
||||
override fun directSupertypes(t: TypeMirror): List<TypeMirror> {
|
||||
if (t is NoType) throw IllegalArgumentException("Invalid type: $t")
|
||||
val psiType = (t as? JeAbstractType)?.psiType as? PsiClassType ?: return emptyList()
|
||||
return psiType.superTypes.map { it.toJeType() }
|
||||
}
|
||||
|
||||
override fun boxedClass(p: PrimitiveType): TypeElement? {
|
||||
p as? JePrimitiveType ?: throw IllegalArgumentException("Unknown type: $p")
|
||||
val boxedTypeName = p.psiType.boxedTypeName
|
||||
val boxedClass = javaPsiFacade.findClass(boxedTypeName, scope)
|
||||
?: throw IllegalStateException("Can't find boxed class $boxedTypeName")
|
||||
return JeTypeElement(boxedClass)
|
||||
}
|
||||
|
||||
override fun asElement(t: TypeMirror): Element? {
|
||||
if (t is JeDeclaredType) {
|
||||
return t.asElement()
|
||||
} else if (t is JeTypeVariableType) {
|
||||
return t.asElement()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun isSubtype(t1: TypeMirror, t2: TypeMirror): Boolean {
|
||||
val psiType1 = (t1 as? JeAbstractType)?.psiType ?: return false
|
||||
val psiType2 = (t2 as? JeAbstractType)?.psiType ?: return false
|
||||
|
||||
return TypeConversionUtil.isAssignable(psiType2, psiType1, false)
|
||||
}
|
||||
|
||||
override fun isSameType(t1: TypeMirror, t2: TypeMirror) = t1 == t2
|
||||
|
||||
override fun getNoType(kind: TypeKind) = CustomJeNoneType(kind)
|
||||
|
||||
override fun getDeclaredType(typeElem: TypeElement, vararg typeArgMirrors: TypeMirror): DeclaredType {
|
||||
val psiClass = (typeElem as JeTypeElement).psi
|
||||
if (!psiClass.hasTypeParameters() && typeArgMirrors.size > 0) {
|
||||
throw IllegalArgumentException("$typeElem has not type parameters")
|
||||
}
|
||||
|
||||
// Raw type
|
||||
if (psiClass.hasTypeParameters() && typeArgMirrors.isEmpty()) {
|
||||
return JeDeclaredType(PsiTypesUtil.getClassType(psiClass).rawType(), psiClass)
|
||||
}
|
||||
|
||||
val typeParameters = psiClass.typeParameters
|
||||
if (typeArgMirrors.size != typeParameters.size) {
|
||||
throw IllegalArgumentException("$typeElem type parameters count: " +
|
||||
"${typeParameters.size}, received ${typeArgMirrors.size} args")
|
||||
}
|
||||
|
||||
val typeArgs = typeArgMirrors.mapIndexed {
|
||||
i, t -> (t as? JeAbstractType)?.psiType ?: throw IllegalArgumentException("Invalid type argument #$i: $t")
|
||||
}
|
||||
|
||||
return JeCompoundDeclaredType(psiClass, typeElem, null, typeArgs, typeArgMirrors.toList())
|
||||
}
|
||||
|
||||
override fun getDeclaredType(
|
||||
containing: DeclaredType?,
|
||||
typeElem: TypeElement,
|
||||
vararg typeArgMirrors: TypeMirror
|
||||
): DeclaredType? {
|
||||
if (containing == null) {
|
||||
return getDeclaredType(typeElem, *typeArgMirrors)
|
||||
}
|
||||
|
||||
val psiClass = (typeElem as JeTypeElement).psi
|
||||
if (psiClass.containingClass == null) {
|
||||
throw IllegalArgumentException("$typeElem is a top-level class element")
|
||||
}
|
||||
|
||||
val containingType = (containing as? JeAbstractType)?.psiType as? PsiClassType
|
||||
?: throw IllegalArgumentException("Illegal containing type: $containing")
|
||||
val containingClass = containingType.resolve()
|
||||
?: throw IllegalArgumentException("Class type can't be resolved: $containing")
|
||||
|
||||
if (psiClass.containingClass != containingClass) {
|
||||
throw IllegalArgumentException("$containing is not an enclosing element for $typeElem")
|
||||
}
|
||||
|
||||
val typeArgs = typeArgMirrors.mapIndexed {
|
||||
i, t -> (t as? JeAbstractType)?.psiType ?: throw IllegalArgumentException("Invalid type argument #$i: $t")
|
||||
}
|
||||
|
||||
return JeCompoundDeclaredType(psiClass, typeElem, containing, typeArgs, typeArgMirrors.toList())
|
||||
}
|
||||
|
||||
override fun asMemberOf(containing: DeclaredType, element: Element): TypeMirror {
|
||||
val substitutor = when (containing) {
|
||||
is JeDeclaredType -> {
|
||||
val result = containing.psiType.resolveGenerics()
|
||||
if (result.isValidResult) result.substitutor else PsiSubstitutor.EMPTY
|
||||
}
|
||||
is JeCompoundDeclaredType -> {
|
||||
val mapping = containing.psiClass.typeParameters.zip(containing.typeArgs).toMap()
|
||||
PsiSubstitutorImpl.createSubstitutor(mapping)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid containing type: $containing")
|
||||
}
|
||||
|
||||
return when (element) {
|
||||
is JeMethodExecutableElement -> {
|
||||
val method = element.psi
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC) || !method.hasTypeParameters()) {
|
||||
JeMethodExecutableTypeMirror(method)
|
||||
} else {
|
||||
val signature = method.getSignature(substitutor)
|
||||
val returnType = substitutor.substitute(element.psi.returnType)
|
||||
JeMethodExecutableTypeMirror(method, signature, returnType)
|
||||
}
|
||||
}
|
||||
is JeClassInitializerExecutableElement -> element.asType()
|
||||
is JeVariableElement -> substitutor.substitute(element.psi.type).toJeType()
|
||||
else -> throw IllegalArgumentException("Invalid element type: $element")
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSubsignature(t1: ExecutableType, t2: ExecutableType): Boolean {
|
||||
val m1 = (t1 as JeClassInitializerExecutableTypeMirror).initializer
|
||||
val m2 = (t2 as JeClassInitializerExecutableTypeMirror).initializer
|
||||
|
||||
if (m1 !is PsiMethod || m2 !is PsiMethod) return false
|
||||
if (m1.parameterList.parametersCount != m2.parameterList.parametersCount) return false
|
||||
|
||||
for (i in 0..(m1.parameterList.parametersCount - 1)) {
|
||||
val p1 = m1.parameterList.parameters[i]
|
||||
val p2 = m2.parameterList.parameters[i]
|
||||
|
||||
if (!TypeConversionUtil.isAssignable(p2.type, p1.type, false)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun capture(t: TypeMirror): TypeMirror? {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user