Serialize KotlinAnnotationProvider

This commit is contained in:
Alexey Tsvetkov
2016-04-15 16:14:00 +03:00
parent 33dc792bf9
commit c21693b0d0
7 changed files with 185 additions and 25 deletions
@@ -23,4 +23,21 @@ sealed class AnnotatedElementDescriptor(val classFqName: String) {
override fun hashCode() = 31 * classFqName.hashCode() + fieldName.hashCode()
}
}
fun AnnotationWriter.writeAnnotatedElement(annotation: String, element: AnnotatedElementDescriptor) {
when (element) {
is AnnotatedElementDescriptor.Class -> {
writeAnnotatedClass(annotation, element.classFqName)
}
is AnnotatedElementDescriptor.Constructor -> {
writeAnnotatedMethod(annotation, element.classFqName, AnnotatedElementDescriptor.Constructor.METHOD_NAME)
}
is AnnotatedElementDescriptor.Method -> {
writeAnnotatedMethod(annotation, element.classFqName, element.methodName)
}
is AnnotatedElementDescriptor.Field -> {
writeAnnotatedField(annotation, element.classFqName, element.fieldName)
}
}
}
@@ -0,0 +1,28 @@
package org.jetbrains.kotlin.annotation
import java.io.Writer
import org.jetbrains.kotlin.annotation.CompactNotationType as Notation
abstract class AnnotationWriter(private val writer: Writer) {
companion object {
private val LINE_SEP = System.getProperty("line.separator")
}
abstract fun writeClassDeclaration(classFqName: String)
abstract fun writeAnnotatedClass(annotationName: String, classFqName: String)
abstract fun writeAnnotatedMethod(annotationName: String, classFqName: String, methodName: String)
abstract fun writeAnnotatedField(annotationName: String, classFqName: String, fieldName: String)
protected fun writeLine(vararg parts: String?) {
var i = 0
for (part in parts) {
if (part == null) continue
if (i > 0) {
writer.write(" ")
}
writer.write(part)
i++
}
writer.write(LINE_SEP)
}
}
@@ -0,0 +1,50 @@
package org.jetbrains.kotlin.annotation
import java.io.Writer
import org.jetbrains.kotlin.annotation.CompactNotationType as Notation
class CompactAnnotationWriter(writer: Writer) : AnnotationWriter(writer) {
private val annotationsShortener = NameShortener(Notation.SHORTENED_ANNOTATION)
private val packageShortener = NameShortener(Notation.SHORTENED_PACKAGE_NAME)
override fun writeClassDeclaration(classFqName: String) {
writeLine(Notation.CLASS_DECLARATION, getShortenedClassName(classFqName))
}
override fun writeAnnotatedClass(annotationName: String, classFqName: String) {
writeAnnotated(Notation.ANNOTATED_CLASS, annotationName, classFqName)
}
override fun writeAnnotatedMethod(annotationName: String, classFqName: String, methodName: String) {
writeAnnotated(Notation.ANNOTATED_METHOD, annotationName, classFqName, methodName)
}
override fun writeAnnotatedField(annotationName: String, classFqName: String, fieldName: String) {
writeAnnotated(Notation.ANNOTATED_FIELD, annotationName, classFqName, fieldName)
}
private fun writeAnnotated(type: String, annotationName: String, className: String, memberName: String? = null) {
writeLine(type, annotationsShortener[annotationName].toString(), getShortenedClassName(className), memberName)
}
private fun getShortenedClassName(fqName: String): String {
val lastDotIndex = fqName.lastIndexOf('.')
if (lastDotIndex == -1) return fqName
val packageName = fqName.substring(0, lastDotIndex)
val simpleName = fqName.substring(lastDotIndex + 1)
return "${packageShortener[packageName]}/$simpleName"
}
private inner class NameShortener(private val type: String) {
private val names = hashMapOf<String, Int>()
operator fun get(name: String): Int =
names.getOrPut(name) {
val id = names.size
writeLine(type, name, "$id")
id
}
}
}
@@ -0,0 +1,12 @@
package org.jetbrains.kotlin.annotation
object CompactNotationType {
const val ANNOTATED_CLASS = "c"
const val ANNOTATED_METHOD = "m"
const val ANNOTATED_FIELD = "f"
const val SHORTENED_ANNOTATION = "a"
const val SHORTENED_PACKAGE_NAME = "p"
const val CLASS_DECLARATION = "d"
}
@@ -19,20 +19,10 @@ package org.jetbrains.kotlin.annotation
import java.io.File
import java.io.Reader
import java.io.StringReader
import java.util.*
import org.jetbrains.kotlin.annotation.CompactNotationType as Notation
class KotlinAnnotationProvider(annotationsReader: Reader) {
private companion object {
const val ANNOTATED_CLASS = "c"
const val ANNOTATED_METHOD = "m"
const val ANNOTATED_FIELD = "f"
const val SHORTENED_ANNOTATION = "a"
const val SHORTENED_PACKAGE_NAME = "p"
const val CLASS_DECLARATION = "d"
}
constructor(annotationsFile: File) : this(annotationsFile.reader().buffered())
constructor() : this(StringReader(""))
@@ -80,26 +70,26 @@ class KotlinAnnotationProvider(annotationsReader: Reader) {
val type = lineParts[0]
when (type) {
SHORTENED_ANNOTATION -> handleShortenedName(shortenedAnnotationCache, lineParts)
SHORTENED_PACKAGE_NAME -> handleShortenedName(shortenedPackageNameCache, lineParts)
CLASS_DECLARATION -> {
Notation.SHORTENED_ANNOTATION -> handleShortenedName(shortenedAnnotationCache, lineParts)
Notation.SHORTENED_PACKAGE_NAME -> handleShortenedName(shortenedPackageNameCache, lineParts)
Notation.CLASS_DECLARATION -> {
val classFqName = expandClassName(lineParts[1]).replace('$', '.')
kotlinClassesInternal.add(classFqName)
}
ANNOTATED_CLASS, ANNOTATED_FIELD, ANNOTATED_METHOD -> {
Notation.ANNOTATED_CLASS, Notation.ANNOTATED_FIELD, Notation.ANNOTATED_METHOD -> {
val annotationName = expandAnnotation(lineParts[1])
val classFqName = expandClassName(lineParts[2]).replace('$', '.')
val elementName = if (lineParts.size == 4) lineParts[3] else null
val set = annotatedKotlinElementsInternal.getOrPut(annotationName) { hashSetOf() }
val set = annotatedKotlinElementsInternal.getOrPut(annotationName) { HashSet() }
set.add(when (type) {
ANNOTATED_CLASS -> AnnotatedElementDescriptor.Class(classFqName)
ANNOTATED_FIELD -> {
Notation.ANNOTATED_CLASS -> AnnotatedElementDescriptor.Class(classFqName)
Notation.ANNOTATED_FIELD -> {
val name = elementName ?: throw AssertionError("Name for field must be provided")
AnnotatedElementDescriptor.Field(classFqName, name)
}
ANNOTATED_METHOD -> {
Notation.ANNOTATED_METHOD -> {
val name = elementName ?: throw AssertionError("Name for method must be provided")
if (AnnotatedElementDescriptor.Constructor.METHOD_NAME == name)
@@ -116,4 +106,16 @@ class KotlinAnnotationProvider(annotationsReader: Reader) {
}
}
}
fun writeAnnotations(writer: AnnotationWriter) {
for ((annotation, elements) in annotatedKotlinElements) {
for (element in elements) {
writer.writeAnnotatedElement(annotation, element)
}
}
for (className in kotlinClasses) {
writer.writeClassDeclaration(className)
}
}
}
@@ -1,12 +1,16 @@
package org.jetbrains.kotlin.annotation
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File
import org.junit.Assert.*
import java.io.IOException
public class AnnotationListParseTest {
open class AnnotationListParseTest {
companion object {
const val ANNOTATIONS_FILE_NAME = "annotations.txt"
const val PARSED_FILE_NAME = "parsed.txt"
}
@Test
fun testAnnotatedGettersSetters() = doTest("annotatedGettersSetters")
@@ -48,8 +52,12 @@ public class AnnotationListParseTest {
private val resourcesRootFile = File("src/test/resources/parse")
private fun doTest(testName: String) {
val annotationsFile = File(resourcesRootFile, "$testName/annotations.txt")
val expectedFile = File(resourcesRootFile, "$testName/parsed.txt")
doTest(testDir = File(resourcesRootFile, testName))
}
protected open fun doTest(testDir: File) {
val annotationsFile = File(testDir, ANNOTATIONS_FILE_NAME)
val expectedFile = File(testDir, PARSED_FILE_NAME)
assertTrue(annotationsFile.absolutePath + " does not exist.", annotationsFile.exists())
@@ -0,0 +1,43 @@
/*
* 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.google.common.io.Files
import java.io.File
class AnnotationSerializationTest : AnnotationListParseTest() {
// after reading and serializing annotations file should not change the result of AnnotationListParseTest
override fun doTest(testDir: File) {
val workingDir = Files.createTempDir()
testDir.copyRecursively(workingDir)
val annotationsFile = File(workingDir, ANNOTATIONS_FILE_NAME)
val annotationProvider = KotlinAnnotationProvider(annotationsFile)
annotationsFile.delete()
val writer = annotationsFile.bufferedWriter()
try {
val annotationWriter = CompactAnnotationWriter(writer)
annotationProvider.writeAnnotations(annotationWriter)
}
finally {
writer.close()
}
super.doTest(workingDir)
}
}