Skip to content

Commit

Permalink
Merge pull request #259 from kategory/rr-include-kategory-annotations
Browse files Browse the repository at this point in the history
Bring `kategory-annotations` to the `kategory` repo
  • Loading branch information
raulraja committed Sep 8, 2017
2 parents 751f25a + 640c9f9 commit 539de80
Show file tree
Hide file tree
Showing 169 changed files with 1,539 additions and 227 deletions.
16 changes: 14 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ buildscript {
gradleVersionsPluginVersion = '0.15.0'
javaVersion = JavaVersion.VERSION_1_7
kotlinTestVersion = '2.0.5'
kotlinVersion = '1.1.4'
kotlinVersion = '1.1.4-3'
kotlinxCoroutinesVersion = '0.18'
kategoryAnnotationsVersion = '0.3.7'
kotlinxCollectionsImmutableVersion = '0.1'
}

Expand Down Expand Up @@ -98,6 +97,19 @@ subprojects { project ->
vcsUrl = 'https://github.com/kategory/kategory.git'
}
}

compileKotlin.kotlinOptions.freeCompilerArgs += ["-Xskip-runtime-version-check"]

test {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
}
}

build.dependsOn ':detekt'

sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}

task wrapper(type: Wrapper) {
Expand Down
13 changes: 10 additions & 3 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ potential-bugs:
active: false
UnsafeCast:
active: false
UnsafeCallOnNullableType:
active: false

exceptions:
active: true
Expand Down Expand Up @@ -78,7 +80,7 @@ formatting:
active: true
autoCorrect: true
OptionalUnit:
active: true
active: false
autoCorrect: false
ExpressionBodySyntax:
active: true
Expand All @@ -87,6 +89,11 @@ formatting:
active: false
autoCorrect: false

performance:
SpreadOperator:
active: false
autoCorrect: false

style:
active: true
WildcardImport:
Expand All @@ -100,9 +107,9 @@ style:
comments:
active: true
CommentOverPrivateMethod:
active: true
active: false
CommentOverPrivateProperty:
active: true
active: false
UndocumentedPublicClass:
active: false
UndocumentedPublicFunction:
Expand Down
1 change: 1 addition & 0 deletions kategory-annotations-processor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
20 changes: 20 additions & 0 deletions kategory-annotations-processor/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import org.gradle.internal.jvm.Jvm

apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlinVersion"
compile project(':kategory-annotations')
compile 'com.squareup:kotlinpoet:0.4.0'
compile 'me.eugeniomarletti:kotlin-metadata:1.1.0'
compileOnly 'com.google.auto.service:auto-service:1.0-rc3'
kapt 'com.google.auto.service:auto-service:1.0-rc3'

testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "com.google.testing.compile:compile-testing:0.6"
testCompile fileTree(dir: './src/test/libs', includes: ['*.jar'])
testCompile files(Jvm.current().getToolsJar())
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
4 changes: 4 additions & 0 deletions kategory-annotations-processor/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Maven publishing configuration
POM_NAME=Kategory Annotations Compile Time
POM_ARTIFACT_ID=kategory-annotations-processor
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package kategory.common

typealias Type = String
typealias Package = String
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kategory.common.messager

import me.eugeniomarletti.kotlin.processing.KotlinProcessingUtils
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.Element
import javax.tools.Diagnostic.Kind.ERROR
import javax.tools.Diagnostic.Kind.MANDATORY_WARNING
import javax.tools.Diagnostic.Kind.NOTE
import javax.tools.Diagnostic.Kind.WARNING

fun KotlinProcessingUtils.log(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(NOTE, message, element, annotationMirror, annotationValue)

fun KotlinProcessingUtils.logW(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(WARNING, message, element, annotationMirror, annotationValue)

fun KotlinProcessingUtils.logMW(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(MANDATORY_WARNING, message, element, annotationMirror, annotationValue)

fun KotlinProcessingUtils.logE(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(ERROR, message, element, annotationMirror, annotationValue)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kategory.common.utils

import kategory.common.messager.logE
import me.eugeniomarletti.kotlin.processing.KotlinAbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import me.eugeniomarletti.kotlin.metadata.kaptGeneratedOption
import java.io.File

class KnownException(message: String, val element: Element?) : RuntimeException(message) {
override val message: String get() = super.message as String
operator fun component1() = message
operator fun component2() = element
}

abstract class AbstractProcessor : KotlinAbstractProcessor(), ProcessorUtils {

val generatedDir: File? get() = options[kaptGeneratedOption]?.let(::File)

override final fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
if (!roundEnv.errorRaised()) {
try {
onProcess(annotations, roundEnv)
}
catch (e: KnownException) {
logE(e.message, e.element)
}
}
return false
}

protected abstract fun onProcess(annotations: Set<TypeElement>, roundEnv: RoundEnvironment)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package kategory.common.utils

import org.jetbrains.kotlin.serialization.ClassData
import org.jetbrains.kotlin.serialization.PackageData
import org.jetbrains.kotlin.serialization.ProtoBuf.Constructor
import org.jetbrains.kotlin.serialization.ProtoBuf.Function
import org.jetbrains.kotlin.serialization.ProtoBuf.Property
import org.jetbrains.kotlin.serialization.ProtoBuf.TypeParameter
import org.jetbrains.kotlin.serialization.deserialization.NameResolver

sealed class ClassOrPackageDataWrapper {
abstract val `package`: String
abstract val nameResolver: NameResolver
abstract val constructorList: List<Constructor>
abstract val functionList: List<Function>
abstract val propertyList: List<Property>
abstract val typeParameters: List<TypeParameter>
abstract fun getTypeParameter(typeParameterIndex: Int): TypeParameter?

class Package(
override val nameResolver: NameResolver,
val packageProto: org.jetbrains.kotlin.serialization.ProtoBuf.Package,
override val `package`: String
) : ClassOrPackageDataWrapper() {
override val constructorList: List<Constructor> get() = emptyList()
override val functionList: List<Function> get() = packageProto.functionList
override val propertyList: List<Property> get() = packageProto.propertyList
override val typeParameters: List<TypeParameter> = emptyList()
override fun getTypeParameter(typeParameterIndex: Int): TypeParameter? = null
}

class Class(
override val nameResolver: NameResolver,
val classProto: org.jetbrains.kotlin.serialization.ProtoBuf.Class,
override val `package`: String
) : ClassOrPackageDataWrapper() {
override val constructorList: List<Constructor> get() = classProto.constructorList
override val functionList: List<Function> get() = classProto.functionList
override val propertyList: List<Property> get() = classProto.propertyList
override val typeParameters: List<TypeParameter> = classProto.typeParameterList
override fun getTypeParameter(typeParameterIndex: Int): TypeParameter? = classProto.getTypeParameter(typeParameterIndex)
}
}

fun ClassData.asClassOrPackageDataWrapper(`package`: String) =
ClassOrPackageDataWrapper.Class(nameResolver, classProto, `package`)

fun PackageData.asClassOrPackageDataWrapper(`package`: String) =
ClassOrPackageDataWrapper.Package(nameResolver, packageProto, `package`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package kategory.common.utils

import kategory.implicits.implicitAnnotationName
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
import me.eugeniomarletti.kotlin.metadata.KotlinMetadata
import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
import me.eugeniomarletti.kotlin.metadata.KotlinPackageMetadata
import me.eugeniomarletti.kotlin.metadata.getJvmMethodSignature
import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
import me.eugeniomarletti.kotlin.metadata.kotlinPropertyAnnotationsFunPostfix
import org.jetbrains.kotlin.serialization.ProtoBuf
import javax.lang.model.element.Element
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement

interface ProcessorUtils : KotlinMetadataUtils {

fun KotlinMetadata.asClassOrPackageDataWrapper(classElement: TypeElement): ClassOrPackageDataWrapper? {
val `package` = elementUtils.getPackageOf(classElement).toString()
return when (this) {
is KotlinClassMetadata -> data.asClassOrPackageDataWrapper(`package`)
is KotlinPackageMetadata -> data.asClassOrPackageDataWrapper(`package`)
else -> null
}
}

fun getClassOrPackageDataWrapper(classElement: TypeElement): ClassOrPackageDataWrapper {
val metadata = classElement.kotlinMetadata ?: knownError("These annotations can only be used in Kotlin")
return metadata.asClassOrPackageDataWrapper(classElement) ?: knownError("This annotation can't be used on this element")
}

fun ClassOrPackageDataWrapper.getFunction(methodElement: ExecutableElement) =
methodElement.jvmMethodSignature.let { methodSignature ->
functionList
.firstOrNull { methodSignature == it.getJvmMethodSignature(nameResolver) }
?: knownError("Can't find annotated method $methodSignature")
}
}

fun knownError(message: String, element: Element? = null): Nothing = throw KnownException(message, element)

fun String.plusIfNotBlank(
postfix: String = "",
prefix: String = ""
) = if (this.isNotBlank()) prefix + this + postfix else this

val String.escapedClassName
get() = split('/', '.').joinToString("`.`").plusIfNotBlank(prefix = "`", postfix = "`")

val ProtoBuf.Class.Kind.isCompanionOrObject get() = when (this) {
ProtoBuf.Class.Kind.OBJECT,
ProtoBuf.Class.Kind.COMPANION_OBJECT -> true
else -> false
}

fun ClassOrPackageDataWrapper.getParameter(function: ProtoBuf.Function, parameterElement: VariableElement) =
parameterElement.simpleName.toString().let { parameterName ->
function.valueParameterList
.firstOrNull { parameterName == nameResolver.getString(it.name) }
?: knownError("Can't find annotated parameter $parameterName in ${function.getJvmMethodSignature(nameResolver)}")
}

fun ClassOrPackageDataWrapper.getPropertyOrNull(methodElement: ExecutableElement) =
methodElement.simpleName.toString()
.takeIf { it.endsWith(kotlinPropertyAnnotationsFunPostfix) }
?.substringBefore(kotlinPropertyAnnotationsFunPostfix)
?.let { propertyName -> propertyList.firstOrNull { propertyName == nameResolver.getString(it.name) } }

fun ProtoBuf.Type.extractFullName(
classData: ClassOrPackageDataWrapper,
outputTypeAlias: Boolean = true,
failOnGeneric: Boolean = true
): String {
val nameResolver = classData.nameResolver

if (failOnGeneric && !hasClassName()) knownError("Generic $implicitAnnotationName types are not yet supported")

val name = when {
hasTypeParameter() -> classData.getTypeParameter(typeParameter)!!.name
hasTypeParameterName() -> typeParameterName
outputTypeAlias && hasAbbreviatedType() -> abbreviatedType.typeAliasName
else -> className
}.let { nameResolver.getString(it).escapedClassName }

val argumentList = if (outputTypeAlias && hasAbbreviatedType()) abbreviatedType.argumentList else argumentList
val arguments = argumentList
.takeIf { it.isNotEmpty() }
?.joinToString(prefix = "<", postfix = ">") {
when {
it.hasType() -> it.type.extractFullName(classData, outputTypeAlias, failOnGeneric)
!failOnGeneric -> "*"
else -> knownError("Wildcard $implicitAnnotationName types are not yet supported")
}
}
?: ""

val nullability = if (nullable) "?" else ""

return name + arguments + nullability
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kategory.derive

import kategory.common.utils.ClassOrPackageDataWrapper
import javax.lang.model.element.TypeElement

class AnnotatedDeriving(
val classElement: TypeElement,
val classOrPackageProto: ClassOrPackageDataWrapper,
val companionClassProto: ClassOrPackageDataWrapper,
val derivingTypeclasses: List<ClassOrPackageDataWrapper>,
val typeclassSuperTypes: Map<ClassOrPackageDataWrapper.Class, List<ClassOrPackageDataWrapper>>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kategory.derive

import kategory.deriving

val derivingAnnotationKClass = deriving::class
val derivingAnnotationClass = derivingAnnotationKClass.java
val derivingAnnotationName = "@" + derivingAnnotationClass.simpleName
Loading

0 comments on commit 539de80

Please sign in to comment.