Add annotation processor wrapper as a Maven artifact
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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 javax.annotation.processing.*
|
||||
import javax.lang.model.SourceVersion
|
||||
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.tools.Diagnostic
|
||||
import javax.tools.StandardLocation
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public class AnnotationProcessorStub : AbstractProcessor() {
|
||||
override fun process(annotations: Set<TypeElement>?, roundEnv: RoundEnvironment?) = true
|
||||
}
|
||||
|
||||
abstract class AnnotatedElementDescriptor(public val classFqName: String)
|
||||
|
||||
data class AnnotatedClassDescriptor(classFqName: String) : AnnotatedElementDescriptor(classFqName)
|
||||
data class AnnotatedMethodDescriptor(classFqName: String, public val methodName: String) : AnnotatedElementDescriptor(classFqName)
|
||||
data class AnnotatedFieldDescriptor(classFqName: String, public val fieldName: String) : AnnotatedElementDescriptor(classFqName)
|
||||
|
||||
public abstract class AnnotationProcessorWrapper(private val processorFqName: String) : Processor {
|
||||
|
||||
private val processor: Processor by Delegates.lazy {
|
||||
try {
|
||||
val instance = Class.forName(processorFqName).newInstance() as? Processor
|
||||
instance ?: throw IllegalArgumentException("Instance has a wrong type")
|
||||
}
|
||||
catch (e: Exception) {
|
||||
AnnotationProcessorStub()
|
||||
}
|
||||
}
|
||||
|
||||
private var processingEnv: ProcessingEnvironment by Delegates.notNull()
|
||||
|
||||
private var kotlinAnnotationsProvider: KotlinAnnotationProvider by Delegates.notNull()
|
||||
|
||||
private var roundCounter = 0
|
||||
|
||||
override fun getCompletions(
|
||||
element: Element?,
|
||||
annotation: AnnotationMirror?,
|
||||
member: ExecutableElement?,
|
||||
userText: String?
|
||||
): MutableIterable<Completion> {
|
||||
return processor.getCompletions(element, annotation, member, userText)
|
||||
}
|
||||
|
||||
override fun init(processingEnv: ProcessingEnvironment) {
|
||||
this.processingEnv = processingEnv
|
||||
|
||||
if (processor is AnnotationProcessorStub) {
|
||||
processingEnv.err("Can't instantiate processor $processorFqName")
|
||||
return
|
||||
}
|
||||
|
||||
val annotationsTxt = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", "0apt/annotations.txt")
|
||||
kotlinAnnotationsProvider = FileObjectKotlinAnnotationProvider(annotationsTxt)
|
||||
|
||||
processor.init(processingEnv)
|
||||
}
|
||||
|
||||
override fun getSupportedAnnotationTypes(): MutableSet<String> {
|
||||
val supportedAnnotations = processor.getSupportedAnnotationTypes().toMutableSet()
|
||||
supportedAnnotations.add("__gen.KotlinAptAnnotation")
|
||||
return supportedAnnotations
|
||||
}
|
||||
|
||||
override fun getSupportedSourceVersion(): SourceVersion? {
|
||||
return processor.getSupportedSourceVersion()
|
||||
}
|
||||
|
||||
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean {
|
||||
roundCounter = roundCounter + 1
|
||||
|
||||
val annotatedKotlinElements = kotlinAnnotationsProvider.annotatedKotlinElements
|
||||
val roundEnvironmentWrapper = RoundEnvironmentWrapper(processingEnv, roundEnv, roundCounter, annotatedKotlinElements)
|
||||
return processor.process(annotations, roundEnvironmentWrapper)
|
||||
}
|
||||
|
||||
override fun getSupportedOptions(): MutableSet<String> {
|
||||
return processor.getSupportedOptions()
|
||||
}
|
||||
|
||||
private fun ProcessingEnvironment.err(message: String) {
|
||||
getMessager().printMessage(Diagnostic.Kind.ERROR, message)
|
||||
}
|
||||
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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 javax.tools.FileObject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public abstract class KotlinAnnotationProvider {
|
||||
|
||||
private companion object {
|
||||
val ANNOTATED_CLASS = "c"
|
||||
val ANNOTATED_METHOD = "m"
|
||||
val ANNOTATED_FIELD = "f"
|
||||
|
||||
val SHORTENED_ANNOTATION = "a"
|
||||
val SHORTENED_PACKAGE_NAME = "p"
|
||||
}
|
||||
|
||||
public val annotatedKotlinElements: MutableMap<String, MutableSet<AnnotatedElementDescriptor>> by Delegates.lazy {
|
||||
readAnnotations()
|
||||
}
|
||||
|
||||
protected abstract val serializedAnnotations: String
|
||||
|
||||
private fun readAnnotations(): MutableMap<String, MutableSet<AnnotatedElementDescriptor>> {
|
||||
val shortenedAnnotationCache = hashMapOf<String, String>()
|
||||
val shortenedPackageNameCache = hashMapOf<String, String>()
|
||||
|
||||
fun expandAnnotation(s: String) = shortenedAnnotationCache.getOrElse(s) { s }
|
||||
|
||||
fun expandClassName(s: String): String {
|
||||
val id = s.substringBefore('/', "")
|
||||
if (id.isEmpty()) return s
|
||||
val shortenedValue = shortenedPackageNameCache.get(id) ?:
|
||||
throw RuntimeException("Value for $id couldn't be found in shrink cache")
|
||||
|
||||
return shortenedValue + '.' + s.substring(id.length() + 1)
|
||||
}
|
||||
|
||||
val annotatedKotlinElements: MutableMap<String, MutableSet<AnnotatedElementDescriptor>> = hashMapOf()
|
||||
|
||||
for (line in serializedAnnotations.split('\n')) {
|
||||
if (line.isEmpty()) continue
|
||||
val lineParts = line.split(' ')
|
||||
|
||||
val type = lineParts[0]
|
||||
when (type) {
|
||||
SHORTENED_ANNOTATION -> handleShortenedName(shortenedAnnotationCache, lineParts)
|
||||
SHORTENED_PACKAGE_NAME -> handleShortenedName(shortenedPackageNameCache, lineParts)
|
||||
|
||||
ANNOTATED_CLASS, ANNOTATED_FIELD, ANNOTATED_METHOD -> {
|
||||
val annotationName = expandAnnotation(lineParts[1])
|
||||
val classFqName = expandClassName(lineParts[2])
|
||||
val elementName = if (lineParts.size() == 4) lineParts[3] else null
|
||||
|
||||
val set = annotatedKotlinElements.getOrPut(annotationName) { hashSetOf() }
|
||||
set.add(when (type) {
|
||||
ANNOTATED_CLASS -> AnnotatedClassDescriptor(classFqName)
|
||||
ANNOTATED_FIELD -> AnnotatedFieldDescriptor(classFqName, elementName!!)
|
||||
ANNOTATED_METHOD -> AnnotatedMethodDescriptor(classFqName, elementName!!)
|
||||
else -> throw AssertionError("Should not occur")
|
||||
})
|
||||
|
||||
}
|
||||
else -> throw RuntimeException("Unknown type: $type")
|
||||
}
|
||||
}
|
||||
|
||||
return annotatedKotlinElements
|
||||
}
|
||||
|
||||
private fun handleShortenedName(cache: MutableMap<String, String>, lineParts: List<String>) {
|
||||
val name = lineParts[1]
|
||||
val id = lineParts[2]
|
||||
cache.put(id, name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class FileObjectKotlinAnnotationProvider(val annotationsFileObject: FileObject): KotlinAnnotationProvider() {
|
||||
override val serializedAnnotations: String
|
||||
get() = annotationsFileObject.getCharContent(false).toString()
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package org.jetbrains.kotlin.annotation
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.element.Element
|
||||
import javax.lang.model.element.ElementKind
|
||||
import javax.lang.model.element.TypeElement
|
||||
|
||||
private class RoundEnvironmentWrapper(
|
||||
val processingEnv: ProcessingEnvironment,
|
||||
val parent: RoundEnvironment,
|
||||
val roundNumber: Int,
|
||||
val annotatedElementDescriptors: Map<String, Set<AnnotatedElementDescriptor>>
|
||||
) : RoundEnvironment {
|
||||
|
||||
override fun getRootElements(): MutableSet<out Element>? {
|
||||
return parent.getRootElements()
|
||||
}
|
||||
|
||||
override fun getElementsAnnotatedWith(a: TypeElement): MutableSet<out Element>? {
|
||||
val elements = parent.getElementsAnnotatedWith(a).toHashSet()
|
||||
elements.addAll(resolveKotlinElements(a.getQualifiedName().toString()))
|
||||
return elements
|
||||
}
|
||||
|
||||
override fun getElementsAnnotatedWith(a: Class<out Annotation>): MutableSet<out Element>? {
|
||||
val elements = parent.getElementsAnnotatedWith(a).toHashSet()
|
||||
elements.addAll(resolveKotlinElements(a.getName()))
|
||||
return elements
|
||||
}
|
||||
|
||||
override fun processingOver() = parent.processingOver()
|
||||
|
||||
override fun errorRaised() = parent.errorRaised()
|
||||
|
||||
private fun TypeElement.filterEnclosedElements(kind: ElementKind, name: String): List<Element> {
|
||||
return getEnclosedElements().filter { it.getKind() == kind && it.getSimpleName().toString() == name }
|
||||
}
|
||||
|
||||
private fun Element.hasAnnotation(annotationFqName: String): Boolean {
|
||||
return getAnnotationMirrors().any { annotationFqName == it.getAnnotationType().asElement().toString() }
|
||||
}
|
||||
|
||||
private fun resolveKotlinElements(annotationFqName: String): Set<Element> {
|
||||
if (roundNumber > 1) return setOf()
|
||||
|
||||
val descriptors = annotatedElementDescriptors.get(annotationFqName) ?: setOf()
|
||||
return descriptors.fold(hashSetOf<Element>()) { set, descriptor ->
|
||||
val clazz = processingEnv.getElementUtils().getTypeElement(descriptor.classFqName) ?: return@fold set
|
||||
when (descriptor) {
|
||||
is AnnotatedClassDescriptor -> set.add(clazz)
|
||||
is AnnotatedFieldDescriptor -> {
|
||||
set.addAll(clazz.filterEnclosedElements(ElementKind.FIELD, descriptor.fieldName)
|
||||
.filter { it.hasAnnotation(annotationFqName) })
|
||||
}
|
||||
is AnnotatedMethodDescriptor -> {
|
||||
set.addAll(clazz.filterEnclosedElements(ElementKind.METHOD, descriptor.methodName)
|
||||
.filter { it.hasAnnotation(annotationFqName) })
|
||||
}
|
||||
}
|
||||
set
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user