com.zeoflow.depot.writer.ClassWriter.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of depot-compiler Show documentation
Show all versions of depot-compiler Show documentation
The Depot persistence library provides an abstraction layer over SQLite to allow for more robust database access while using the full power of SQLite.
The newest version!
/*
* Copyright (C) 2021 ZeoFlow SRL
*
* 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 com.zeoflow.depot.writer
import com.zeoflow.depot.DepotProcessor
import com.zeoflow.depot.ext.S
import com.zeoflow.depot.ext.typeName
import com.zeoflow.depot.compiler.processing.XProcessingEnv
import com.zeoflow.depot.solver.CodeGenScope.Companion.CLASS_PROPERTY_PREFIX
import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import com.zeoflow.depot.compiler.processing.ExperimentalProcessingApi
import com.zeoflow.depot.compiler.processing.XFiler
import kotlin.reflect.KClass
/**
* Base class for all writers that can produce a class.
*/
abstract class ClassWriter(private val className: ClassName) {
private val sharedFieldSpecs = mutableMapOf()
private val sharedMethodSpecs = mutableMapOf()
private val sharedFieldNames = mutableSetOf()
private val sharedMethodNames = mutableSetOf()
private val metadata = mutableMapOf, Any>()
abstract fun createTypeSpecBuilder(): TypeSpec.Builder
/**
* Read additional metadata that can be put by sub code generators.
*
* @see set for more details.
*/
operator fun get(key: KClass<*>): T? {
@Suppress("UNCHECKED_CAST")
return metadata[key] as? T
}
/**
* Add additional metadata to the ClassWriter that can be read back later.
* This is useful for additional functionality where a sub code generator needs to bubble up
* information to the main ClassWriter without copying it in every intermediate step.
*
* @see get
*/
operator fun set(key: KClass<*>, value: Any) {
metadata[key] = value
}
@ExperimentalProcessingApi
fun write(processingEnv: XProcessingEnv) {
val builder = createTypeSpecBuilder()
sharedFieldSpecs.values.forEach { builder.addField(it) }
sharedMethodSpecs.values.forEach { builder.addMethod(it) }
addGeneratedAnnotationIfAvailable(builder, processingEnv)
addSuppressWarnings(builder)
JavaFile.builder(className.packageName(), builder.build())
.build()
.writeTo(processingEnv.filer)
}
private fun addSuppressWarnings(builder: TypeSpec.Builder) {
val suppressSpec = AnnotationSpec.builder(SuppressWarnings::class.typeName)
.addMember(
"value",
"{$S, $S}",
"unchecked", "deprecation"
).build()
builder.addAnnotation(suppressSpec)
}
@ExperimentalProcessingApi
private fun addGeneratedAnnotationIfAvailable(
adapterTypeSpecBuilder: TypeSpec.Builder,
processingEnv: XProcessingEnv
) {
processingEnv.findGeneratedAnnotation()?.let {
val generatedAnnotationSpec =
AnnotationSpec.builder(it.className)
.addMember("value", S, DepotProcessor::class.java.canonicalName)
.build()
adapterTypeSpecBuilder.addAnnotation(generatedAnnotationSpec)
}
}
private fun makeUnique(set: MutableSet, value: String): String {
if (!value.startsWith(CLASS_PROPERTY_PREFIX)) {
return makeUnique(set, "$CLASS_PROPERTY_PREFIX$value")
}
if (set.add(value)) {
return value
}
var index = 1
while (true) {
if (set.add("${value}_$index")) {
return "${value}_$index"
}
index++
}
}
fun getOrCreateField(sharedField: SharedFieldSpec): FieldSpec {
return sharedFieldSpecs.getOrPut(
sharedField.getUniqueKey(),
{
sharedField.build(this, makeUnique(sharedFieldNames, sharedField.baseName))
}
)
}
fun getOrCreateMethod(sharedMethod: SharedMethodSpec): MethodSpec {
return sharedMethodSpecs.getOrPut(
sharedMethod.getUniqueKey(),
{
sharedMethod.build(this, makeUnique(sharedMethodNames, sharedMethod.baseName))
}
)
}
abstract class SharedFieldSpec(val baseName: String, val type: TypeName) {
abstract fun getUniqueKey(): String
abstract fun prepare(writer: ClassWriter, builder: FieldSpec.Builder)
fun build(classWriter: ClassWriter, name: String): FieldSpec {
val builder = FieldSpec.builder(type, name)
prepare(classWriter, builder)
return builder.build()
}
}
abstract class SharedMethodSpec(val baseName: String) {
abstract fun getUniqueKey(): String
abstract fun prepare(methodName: String, writer: ClassWriter, builder: MethodSpec.Builder)
fun build(writer: ClassWriter, name: String): MethodSpec {
val builder = MethodSpec.methodBuilder(name)
prepare(name, writer, builder)
return builder.build()
}
}
}
fun JavaFile.writeTo(generator: XFiler, mode: XFiler.Mode = XFiler.Mode.Isolating) {
generator.write(this, mode)
}