1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.room.writer 18 19 import androidx.room.RoomProcessor 20 import androidx.room.ext.S 21 import androidx.room.ext.typeName 22 import androidx.room.solver.CodeGenScope.Companion.CLASS_PROPERTY_PREFIX 23 import com.squareup.javapoet.AnnotationSpec 24 import com.squareup.javapoet.ClassName 25 import com.squareup.javapoet.FieldSpec 26 import com.squareup.javapoet.JavaFile 27 import com.squareup.javapoet.MethodSpec 28 import com.squareup.javapoet.TypeName 29 import com.squareup.javapoet.TypeSpec 30 import javax.annotation.processing.ProcessingEnvironment 31 32 /** 33 * Base class for all writers that can produce a class. 34 */ 35 abstract class ClassWriter(private val className: ClassName) { 36 private val sharedFieldSpecs = mutableMapOf<String, FieldSpec>() 37 private val sharedMethodSpecs = mutableMapOf<String, MethodSpec>() 38 private val sharedFieldNames = mutableSetOf<String>() 39 private val sharedMethodNames = mutableSetOf<String>() 40 createTypeSpecBuildernull41 abstract fun createTypeSpecBuilder(): TypeSpec.Builder 42 43 fun write(processingEnv: ProcessingEnvironment) { 44 val builder = createTypeSpecBuilder() 45 sharedFieldSpecs.values.forEach { builder.addField(it) } 46 sharedMethodSpecs.values.forEach { builder.addMethod(it) } 47 addGeneratedAnnotationIfAvailable(builder, processingEnv) 48 addSuppressUnchecked(builder) 49 JavaFile.builder(className.packageName(), builder.build()) 50 .build() 51 .writeTo(processingEnv.filer) 52 } 53 addSuppressUncheckednull54 private fun addSuppressUnchecked(builder: TypeSpec.Builder) { 55 val suppressSpec = AnnotationSpec.builder(SuppressWarnings::class.typeName()).addMember( 56 "value", 57 S, 58 "unchecked" 59 ).build() 60 builder.addAnnotation(suppressSpec) 61 } 62 addGeneratedAnnotationIfAvailablenull63 private fun addGeneratedAnnotationIfAvailable(adapterTypeSpecBuilder: TypeSpec.Builder, 64 processingEnv: ProcessingEnvironment) { 65 val generatedAnnotationAvailable = processingEnv 66 .elementUtils 67 .getTypeElement("$GENERATED_PACKAGE.$GENERATED_NAME") != null 68 if (generatedAnnotationAvailable) { 69 val className = ClassName.get(GENERATED_PACKAGE, GENERATED_NAME) 70 val generatedAnnotationSpec = 71 AnnotationSpec.builder(className).addMember( 72 "value", 73 S, 74 RoomProcessor::class.java.canonicalName).build() 75 adapterTypeSpecBuilder.addAnnotation(generatedAnnotationSpec) 76 } 77 } 78 makeUniquenull79 private fun makeUnique(set: MutableSet<String>, value: String): String { 80 if (!value.startsWith(CLASS_PROPERTY_PREFIX)) { 81 return makeUnique(set, "$CLASS_PROPERTY_PREFIX$value") 82 } 83 if (set.add(value)) { 84 return value 85 } 86 var index = 1 87 while (true) { 88 if (set.add("${value}_$index")) { 89 return "${value}_$index" 90 } 91 index++ 92 } 93 } 94 getOrCreateFieldnull95 fun getOrCreateField(sharedField: SharedFieldSpec): FieldSpec { 96 return sharedFieldSpecs.getOrPut(sharedField.getUniqueKey(), { 97 sharedField.build(this, makeUnique(sharedFieldNames, sharedField.baseName)) 98 }) 99 } 100 getOrCreateMethodnull101 fun getOrCreateMethod(sharedMethod: SharedMethodSpec): MethodSpec { 102 return sharedMethodSpecs.getOrPut(sharedMethod.getUniqueKey(), { 103 sharedMethod.build(this, makeUnique(sharedMethodNames, sharedMethod.baseName)) 104 }) 105 } 106 107 abstract class SharedFieldSpec(val baseName: String, val type: TypeName) { 108 getUniqueKeynull109 abstract fun getUniqueKey(): String 110 111 abstract fun prepare(writer: ClassWriter, builder: FieldSpec.Builder) 112 113 fun build(classWriter: ClassWriter, name: String): FieldSpec { 114 val builder = FieldSpec.builder(type, name) 115 prepare(classWriter, builder) 116 return builder.build() 117 } 118 } 119 120 abstract class SharedMethodSpec(val baseName: String) { 121 getUniqueKeynull122 abstract fun getUniqueKey(): String 123 abstract fun prepare(methodName: String, writer: ClassWriter, builder: MethodSpec.Builder) 124 125 fun build(writer: ClassWriter, name: String): MethodSpec { 126 val builder = MethodSpec.methodBuilder(name) 127 prepare(name, writer, builder) 128 return builder.build() 129 } 130 } 131 132 companion object { 133 private const val GENERATED_PACKAGE = "javax.annotation" 134 private const val GENERATED_NAME = "Generated" 135 } 136 } 137