Skip to content

Commit

Permalink
Transformer: ignore fields of type List with @convert annotation.
Browse files Browse the repository at this point in the history
- objectbox/objectbox-java#481
- Extract and add transformer converter tests.
  • Loading branch information
greenrobot-team committed Jul 2, 2018
1 parent 6e448d1 commit 5dcda93
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package io.objectbox.gradle.transform

import io.objectbox.annotation.BaseEntity
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Transient
import javassist.bytecode.SignatureAttribute
Expand All @@ -34,6 +35,7 @@ object ClassConst {
val entityAnnotationName = Entity::class.qualifiedName!!
val baseEntityAnnotationName = BaseEntity::class.qualifiedName!!
val transientAnnotationName = Transient::class.qualifiedName!!
val convertAnnotationName = Convert::class.qualifiedName!!

val toOne = "io.objectbox.relation.ToOne"
val toOneDescriptor = "Lio/objectbox/relation/ToOne;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,24 @@ class ClassProber {
}

private fun extractAllListTypes(fields: List<FieldInfo>): List<String> {
return fields.filter {
it.descriptor == ClassConst.listDescriptor && !it.exIsTransient()
&& it.exGetAnnotation(ClassConst.transientAnnotationName) == null
}.map {
it.exGetSingleGenericTypeArgumentOrNull()?.name
}.filterNotNull()
return fields.mapNotNull {
val targetClassType = it.exGetSingleGenericTypeArgumentOrNull()
if (ClassConst.listDescriptor != it.descriptor
|| targetClassType == null
|| it.exIsTransient()
|| it.exGetAnnotation(ClassConst.transientAnnotationName) != null
|| it.exGetAnnotation(ClassConst.convertAnnotationName) != null) {
// exclude:
// - not List,
// - no target entity,
// - is transient,
// - is annotated with @Transient or @Convert
// note: this detection should be in sync with ClassTransformer#findRelationFields
null
} else {
targetClassType.name
}
}
}

private fun hasClassRef(classFile: ClassFile, className: String, classDescriptorName: String): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,29 @@ class ClassTransformer(val debug: Boolean = false) {
fieldTypeDescriptor: String, relationType: String)
: MutableList<RelationField> {
val fields = mutableListOf<RelationField>()
ctClass.declaredFields.filter { it.fieldInfo.descriptor == fieldTypeDescriptor }.forEach { field ->
val targetClassType = field.fieldInfo.exGetSingleGenericTypeArgumentOrNull()
if (ClassConst.listDescriptor == fieldTypeDescriptor) {
if (targetClassType == null || !context.entityTypes.contains(targetClassType.name)
|| Modifier.isTransient(field.modifiers)
|| field.fieldInfo.exGetAnnotation(ClassConst.transientAnnotationName) != null) {
return@forEach
ctClass.declaredFields
.filter { it.fieldInfo.descriptor == fieldTypeDescriptor }
.forEach { field ->
val targetClassType = field.fieldInfo.exGetSingleGenericTypeArgumentOrNull()
if (ClassConst.listDescriptor == fieldTypeDescriptor) {
// is List
if (targetClassType == null
|| !context.entityTypes.contains(targetClassType.name)
|| Modifier.isTransient(field.modifiers)
|| field.fieldInfo.exGetAnnotation(ClassConst.transientAnnotationName) != null
|| field.fieldInfo.exGetAnnotation(ClassConst.convertAnnotationName) != null) {
// exclude:
// - no target entity
// - does not hold the expected target entity,
// - is transient
// - is annotated with @Transient or @Convert
// note: this detection should be in sync with ClassProber#extractAllListTypes
return@forEach
}
}
val name = findRelationNameInEntityInfo(context, ctClassEntity, field, relationType)
fields += RelationField(field, name, relationType, targetClassType)
}
}
val name = findRelationNameInEntityInfo(context, ctClassEntity, field, relationType)
fields += RelationField(field, name, relationType, targetClassType)
}
return fields
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package io.objectbox.gradle.transform

import org.junit.Assert
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File
Expand All @@ -36,6 +37,8 @@ abstract class AbstractTransformTest {

val prober = ClassProber()

val transformer = ClassTransformer(true)

@Test
fun testClassDir() {
assertTrue(classDir.absolutePath, classDir.exists())
Expand All @@ -47,4 +50,25 @@ abstract class AbstractTransformTest {
return prober.probeClass(file, outDir)
}

fun testTransformOrCopy(kClass: KClass<*>, expectedTransformed: Int, expectedCopied: Int)
= testTransformOrCopy(listOf(kClass), expectedTransformed, expectedCopied)

fun testTransformOrCopy(kClasses: List<KClass<*>>, expectedTransformed: Int, expectedCopied: Int)
: Pair<ClassTransformerStats, List<File>> {
val tempDir = File.createTempFile(this.javaClass.name, "")
tempDir.delete()
assertTrue(tempDir.mkdir())
val probedClasses = kClasses.map { probeClass(it, tempDir) }
try {
val stats = transformer.transformOrCopyClasses(probedClasses)
Assert.assertEquals(expectedTransformed, stats.countTransformed)
Assert.assertEquals(expectedCopied, stats.countCopied)
val createdFiles = tempDir.walkBottomUp().toList().filter { it.isFile }
Assert.assertEquals(expectedTransformed + expectedCopied, createdFiles.size)
return Pair(stats, createdFiles)
} finally {
tempDir.deleteRecursively()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.objectbox.gradle.transform

import org.junit.Assert
import org.junit.Test


class ClassTransformerConverterTest : AbstractTransformTest() {

@Test
fun testTransformEntity_converter_shouldNotTransform() {
val classes = listOf(EntityConverterList::class,
EntityEmpty::class, EntityEmptyConverter::class)
val (stats) = testTransformOrCopy(classes, 0, 3)
Assert.assertEquals(0, stats.boxStoreFieldsAdded)
Assert.assertEquals(0, stats.toOnesFound)
Assert.assertEquals(0, stats.toManyFound)
Assert.assertEquals(0, stats.toManyInitializerAdded)
}

@Test
fun testTransformEntity_converterAndList_shouldTransformOnlyList() {
val classes = listOf(EntityConverterListAndList::class, EntityConverterListAndList_::class,
EntityEmpty::class, EntityEmptyConverter::class)
val (stats) = testTransformOrCopy(classes, 1, 3)
Assert.assertEquals(1, stats.boxStoreFieldsAdded)
Assert.assertEquals(0, stats.toOnesFound)
Assert.assertEquals(1, stats.toManyFound)
Assert.assertEquals(1, stats.toManyInitializerAdded)
}

@Test
fun testTransformEntity_converterAndToMany_shouldTransformOnlyToMany() {
val classes = listOf(EntityConverterAndToMany::class, EntityConverterAndToMany_::class)
val (stats) = testTransformOrCopy(classes, 1, 1)
Assert.assertEquals(1, stats.boxStoreFieldsAdded)
Assert.assertEquals(0, stats.toOnesFound)
Assert.assertEquals(1, stats.toManyFound)
Assert.assertEquals(1, stats.toManyInitializerAdded)
}

@Test
fun testTransformEntity_converterAndToOne_shouldTransformOnlyToOne() {
val classes = listOf(EntityConverterAndToOne::class, EntityConverterAndToOne_::class)
val (stats) = testTransformOrCopy(classes, 1, 1)
Assert.assertEquals(1, stats.boxStoreFieldsAdded)
Assert.assertEquals(1, stats.toOnesFound)
Assert.assertEquals(0, stats.toManyFound)
Assert.assertTrue(stats.constructorsCheckedForTransform >= 1)
Assert.assertEquals(stats.constructorsCheckedForTransform, stats.toOnesInitializerAdded)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@ import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import java.io.File
import kotlin.reflect.KClass

class ClassTransformerTest : AbstractTransformTest() {
val transformer = ClassTransformer(true)

@Rule @JvmField
val thrown : ExpectedException = ExpectedException.none()
Expand Down Expand Up @@ -120,27 +117,6 @@ class ClassTransformerTest : AbstractTransformTest() {
assertEquals(1, stats.toManyInitializerAdded)
}

@Test
fun testTransformEntity_toManyAndConverter() {
val classes = listOf(EntityToManyAndConverter::class, EntityToManyAndConverter_::class)
val (stats) = testTransformOrCopy(classes, 1, 1)
assertEquals(1, stats.boxStoreFieldsAdded)
assertEquals(0, stats.toOnesFound)
assertEquals(1, stats.toManyFound)
assertEquals(1, stats.toManyInitializerAdded)
}

@Test
fun testTransformEntity_toOneAndConverter() {
val classes = listOf(EntityToOneAndConverter::class, EntityToOneAndConverter_::class)
val (stats) = testTransformOrCopy(classes, 1, 1)
assertEquals(1, stats.boxStoreFieldsAdded)
assertEquals(1, stats.toOnesFound)
assertEquals(0, stats.toManyFound)
assertTrue(stats.constructorsCheckedForTransform >= 1)
assertEquals(stats.constructorsCheckedForTransform, stats.toOnesInitializerAdded)
}

@Test
fun testTransformEntity_toManySuffix() {
val classes = listOf(EntityToManySuffix::class, EntityToManySuffix_::class)
Expand Down Expand Up @@ -171,6 +147,8 @@ class ClassTransformerTest : AbstractTransformTest() {
assertEquals(1, stats.toManyInitializerAdded)
}



@Test
fun doNotTransform_constructorCallingConstructor() {
val classes = listOf(EntityMultipleCtors::class, EntityMultipleCtors_::class)
Expand Down Expand Up @@ -201,26 +179,4 @@ class ClassTransformerTest : AbstractTransformTest() {
assertTrue(actualPath, actualPath.endsWith(expectedPath))
}

fun testTransformOrCopy(kClass: KClass<*>, expectedTransformed: Int, expectedCopied: Int)
= testTransformOrCopy(listOf(kClass), expectedTransformed, expectedCopied)

fun testTransformOrCopy(kClasses: List<KClass<*>>, expectedTransformed: Int, expectedCopied: Int)
: Pair<ClassTransformerStats, List<File>> {
val tempDir = File.createTempFile(this.javaClass.name, "")
tempDir.delete()
assertTrue(tempDir.mkdir())
val probedClasses = kClasses.map { probeClass(it, tempDir) }
try {
val stats = transformer.transformOrCopyClasses(probedClasses)
assertEquals(expectedTransformed, stats.countTransformed)
assertEquals(expectedCopied, stats.countCopied)
val createdFiles = tempDir.walkBottomUp().toList().filter { it.isFile }
assertEquals(expectedTransformed + expectedCopied, createdFiles.size)
return Pair(stats, createdFiles)
} finally {
tempDir.deleteRecursively()
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,10 @@ package io.objectbox.gradle.transform
import io.objectbox.Cursor
import io.objectbox.EntityInfo
import io.objectbox.annotation.BaseEntity
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity
import io.objectbox.converter.PropertyConverter
import io.objectbox.relation.RelationInfo
import io.objectbox.relation.ToMany
import io.objectbox.relation.ToOne
import org.greenrobot.essentials.collections.LongHashSet
import org.junit.Rule


Expand Down Expand Up @@ -92,32 +89,6 @@ object EntityToManyLateInit_ : EntityInfo<EntityToOneLateInit> {
val entityEmpty = RelationInfo<EntityToManyLateInit, EntityEmpty>(null, null, null, null)
}

@Entity
class EntityToManyAndConverter {
lateinit var entityEmpty: ToMany<EntityEmpty>

@Convert(converter = TestConverter::class, dbType = String::class)
lateinit var convertedString: JustCopyMe
}

object EntityToManyAndConverter_ : EntityInfo<EntityToOneLateInit> {
@JvmField
val entityEmpty = RelationInfo<EntityToOneLateInit, EntityEmpty>(null, null, null, null)
}

@Entity
class EntityToOneAndConverter(val someExternalType: LongHashSet? = LongHashSet(8)) {
lateinit var entityEmpty: ToOne<EntityEmpty>

@Convert(converter = TestConverter::class, dbType = String::class)
lateinit var convertedString: JustCopyMe
}

object EntityToOneAndConverter_ : EntityInfo<EntityToOneLateInit> {
@JvmField
val entityEmpty = RelationInfo<EntityToOneLateInit, EntityEmpty>(null, null, null, null)
}

@Entity
class EntityToManySuffix {
lateinit var entityEmptyToMany: ToMany<EntityEmpty>
Expand Down Expand Up @@ -231,13 +202,3 @@ class CursorWithExistingImpl : Cursor<EntityBoxStoreField>() {
}

class JustCopyMe

class TestConverter : PropertyConverter<String, String> {
override fun convertToEntityProperty(databaseValue: String?): String? {
return databaseValue?.substring(0, databaseValue.length - 1)
}

override fun convertToDatabaseValue(entityProperty: String?): String? {
return if (entityProperty == null) null else entityProperty + "!"
}
}

0 comments on commit 5dcda93

Please sign in to comment.