diff --git a/libraries/pom.xml b/libraries/pom.xml
index a10a511670e..63ae3880eec 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -89,6 +89,7 @@
tools/kotlin-js-library
+ tools/kotlin-annotation-processing
tools/kotlin-gradle-plugin
tools/kotlin-gradle-plugin-core
tools/kotlin-gradle-plugin-api
diff --git a/libraries/tools/kotlin-annotation-processing/pom.xml b/libraries/tools/kotlin-annotation-processing/pom.xml
new file mode 100644
index 00000000000..dc04ac48d4e
--- /dev/null
+++ b/libraries/tools/kotlin-annotation-processing/pom.xml
@@ -0,0 +1,80 @@
+
+
+
+ 4.0.0
+
+ 3.0.4
+
+
+
+ org.jetbrains.kotlin
+ kotlin-project
+ 0.1-SNAPSHOT
+ ../../pom.xml
+
+
+ kotlin-annotation-processing
+ jar
+
+ Annotation Processor wrapper for Kotlin
+
+
+
+ jetbrains-utils
+ http://repository.jetbrains.com/utils
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${project.version}
+
+
+ org.jetbrains.kotlin
+ gradle-api
+ 1.6
+ provided
+
+
+ com.android.tools.build
+ gradle
+ 1.1.0
+ provided
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+
+
+
+ kotlin-maven-plugin
+ org.jetbrains.kotlin
+ ${project.version}
+
+
+ ${basedir}/kotlinAnnotation
+
+
+
+
+
+ compile
+ compile
+ compile
+
+
+
+ test-compile
+ test-compile
+ test-compile
+
+
+
+
+
+
diff --git a/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/AnnotationProcessorWrapper.kt b/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/AnnotationProcessorWrapper.kt
new file mode 100644
index 00000000000..81534c0258a
--- /dev/null
+++ b/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/AnnotationProcessorWrapper.kt
@@ -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?, 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 {
+ 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 {
+ val supportedAnnotations = processor.getSupportedAnnotationTypes().toMutableSet()
+ supportedAnnotations.add("__gen.KotlinAptAnnotation")
+ return supportedAnnotations
+ }
+
+ override fun getSupportedSourceVersion(): SourceVersion? {
+ return processor.getSupportedSourceVersion()
+ }
+
+ override fun process(annotations: MutableSet?, 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 {
+ return processor.getSupportedOptions()
+ }
+
+ private fun ProcessingEnvironment.err(message: String) {
+ getMessager().printMessage(Diagnostic.Kind.ERROR, message)
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/KotlinAnnotationProvider.kt b/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/KotlinAnnotationProvider.kt
new file mode 100644
index 00000000000..7a3ce1d63c8
--- /dev/null
+++ b/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/KotlinAnnotationProvider.kt
@@ -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> by Delegates.lazy {
+ readAnnotations()
+ }
+
+ protected abstract val serializedAnnotations: String
+
+ private fun readAnnotations(): MutableMap> {
+ val shortenedAnnotationCache = hashMapOf()
+ val shortenedPackageNameCache = hashMapOf()
+
+ 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> = 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, lineParts: List) {
+ 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()
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/RoundEnvironmentWrapper.kt b/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/RoundEnvironmentWrapper.kt
new file mode 100644
index 00000000000..0e7daec152c
--- /dev/null
+++ b/libraries/tools/kotlin-annotation-processing/src/main/kotlin/org/jetbrains/kotlin/annotation/RoundEnvironmentWrapper.kt
@@ -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>
+) : RoundEnvironment {
+
+ override fun getRootElements(): MutableSet? {
+ return parent.getRootElements()
+ }
+
+ override fun getElementsAnnotatedWith(a: TypeElement): MutableSet? {
+ val elements = parent.getElementsAnnotatedWith(a).toHashSet()
+ elements.addAll(resolveKotlinElements(a.getQualifiedName().toString()))
+ return elements
+ }
+
+ override fun getElementsAnnotatedWith(a: Class): MutableSet? {
+ 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 {
+ 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 {
+ if (roundNumber > 1) return setOf()
+
+ val descriptors = annotatedElementDescriptors.get(annotationFqName) ?: setOf()
+ return descriptors.fold(hashSetOf()) { 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
+ }
+ }
+}