Skip to content

Commit 6b2a5ec

Browse files
committed
WIP
1 parent 5ee3ee2 commit 6b2a5ec

10 files changed

Lines changed: 199 additions & 108 deletions

File tree

compiler/base/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
POM_ARTIFACT_ID=gsonpath-compiler-base
22
POM_NAME=gsonpath-compiler-base
3-
VERSION_NAME=1.5.1
3+
VERSION_NAME=1.5.2
44
POM_PACKAGING=jar

compiler/base/src/main/java/gsonpath/util/TypeHandler.kt

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@ package gsonpath.util
33
import com.squareup.javapoet.ClassName
44
import com.squareup.javapoet.TypeName
55
import javax.annotation.processing.ProcessingEnvironment
6-
import javax.lang.model.element.Element
7-
import javax.lang.model.element.ElementKind
8-
import javax.lang.model.element.Modifier
9-
import javax.lang.model.element.TypeElement
6+
import javax.lang.model.element.*
107
import javax.lang.model.type.DeclaredType
118
import javax.lang.model.type.ExecutableType
129
import javax.lang.model.type.TypeMirror
1310
import kotlin.reflect.KClass
1411

1512
interface TypeHandler {
16-
fun getTypeName(typeMirror: TypeMirror): TypeName?
17-
fun getClassName(typeMirror: TypeMirror): TypeName?
13+
fun getTypeName(typeMirror: TypeMirror): TypeName
14+
fun getClassName(typeMirror: TypeMirror): TypeName
1815
fun isSubtype(t1: TypeMirror, t2: TypeMirror): Boolean
1916
fun asElement(t: TypeMirror): Element?
2017
fun getAllMembers(typeElement: TypeElement): List<Element>
@@ -30,14 +27,22 @@ data class FieldElementContent(
3027
)
3128

3229
data class MethodElementContent(
33-
val element: Element,
34-
val generifiedElement: ExecutableType
30+
val element: ExecutableElement,
31+
val methodName: String,
32+
val returnTypeMirror: TypeMirror,
33+
val returnTypeName: TypeName,
34+
val parameterElementContents: List<ParameterElementContent>
35+
)
36+
37+
data class ParameterElementContent(
38+
val element: VariableElement,
39+
val typeName: TypeName
3540
)
3641

3742
class ProcessorTypeHandler(private val processingEnv: ProcessingEnvironment) : TypeHandler {
38-
override fun getTypeName(typeMirror: TypeMirror): TypeName? = TypeName.get(typeMirror)
43+
override fun getTypeName(typeMirror: TypeMirror): TypeName = TypeName.get(typeMirror)
3944

40-
override fun getClassName(typeMirror: TypeMirror): TypeName? = ClassName.get(typeMirror)
45+
override fun getClassName(typeMirror: TypeMirror): TypeName = ClassName.get(typeMirror)
4146

4247
override fun asElement(t: TypeMirror): Element? {
4348
return processingEnv.typeUtils.asElement(t)
@@ -78,7 +83,18 @@ class ProcessorTypeHandler(private val processingEnv: ProcessingEnvironment) : T
7883
true
7984
}
8085
}
81-
.map { MethodElementContent(it, getGenerifiedTypeMirror(typeElement, it) as ExecutableType) }
86+
.map { methodElement ->
87+
val generifiedMethodMirror = getGenerifiedTypeMirror(typeElement, methodElement) as ExecutableType
88+
MethodElementContent(
89+
methodElement as ExecutableElement,
90+
methodElement.simpleName.toString(),
91+
generifiedMethodMirror.returnType,
92+
getTypeName(generifiedMethodMirror.returnType),
93+
methodElement.parameters.map {
94+
ParameterElementContent(it, getTypeName(it.asType()))
95+
}
96+
)
97+
}
8298
.toList()
8399
}
84100

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package gsonpath.adapter.common
2+
3+
import com.squareup.javapoet.ClassName
4+
import com.squareup.javapoet.ParameterizedTypeName
5+
import com.squareup.javapoet.TypeName
6+
import com.squareup.javapoet.WildcardTypeName
7+
import gsonpath.ProcessingException
8+
import gsonpath.util.MethodElementContent
9+
import javax.lang.model.element.TypeElement
10+
11+
class SubTypeGetterValidator {
12+
fun validateGsonSubtypeGetterMethod(classElement: TypeElement, methodContent: MethodElementContent) {
13+
val elementTypeName = TypeName.get(classElement.asType())
14+
val expectedReturnType = ParameterizedTypeName.get(
15+
ClassName.get(Class::class.java), WildcardTypeName.subtypeOf(elementTypeName))
16+
17+
if (methodContent.returnTypeName != expectedReturnType) {
18+
throw ProcessingException("Incorrect return type for @GsonSubtypeGetter method. It must be " +
19+
"Class<? extends ${classElement.simpleName}>", methodContent.element)
20+
}
21+
}
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package gsonpath.adapter.common
2+
3+
import gsonpath.ProcessingException
4+
import javax.lang.model.element.TypeElement
5+
6+
class SubTypeJsonKeysValidator {
7+
8+
fun validateJsonKeys(classElement: TypeElement, jsonKeys: Array<String>) {
9+
if (jsonKeys.isEmpty()) {
10+
throw ProcessingException("At least one json key must be defined for GsonSubType", classElement)
11+
}
12+
13+
if (jsonKeys.any { it.isBlank() }) {
14+
throw ProcessingException("A blank json key is not valid for GsonSubType", classElement)
15+
}
16+
17+
jsonKeys.groupingBy { it }
18+
.eachCount()
19+
.filter { it.value > 1 }
20+
.keys
21+
.firstOrNull()
22+
.let {
23+
if (it != null) {
24+
throw ProcessingException("The json key '\"$it\"' appears more than once", classElement)
25+
}
26+
}
27+
}
28+
}
Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
package gsonpath.adapter.common
22

3-
import com.squareup.javapoet.ClassName
4-
import com.squareup.javapoet.ParameterizedTypeName
5-
import com.squareup.javapoet.TypeName
6-
import com.squareup.javapoet.WildcardTypeName
73
import gsonpath.GsonSubtype
84
import gsonpath.GsonSubtypeGetter
95
import gsonpath.ProcessingException
106
import gsonpath.adapter.util.NullableUtil
117
import gsonpath.model.FieldType
128
import gsonpath.util.MethodElementContent
139
import gsonpath.util.TypeHandler
14-
import javax.lang.model.element.ExecutableElement
1510
import javax.lang.model.element.TypeElement
1611

1712
interface SubTypeMetadataFactory {
@@ -23,41 +18,21 @@ interface SubTypeMetadataFactory {
2318
}
2419

2520
class SubTypeMetadataFactoryImpl(
26-
private val typeHandler: TypeHandler) : SubTypeMetadataFactory {
21+
private val typeHandler: TypeHandler,
22+
private val subTypeJsonKeysValidator: SubTypeJsonKeysValidator,
23+
private val subTypeGetterValidator: SubTypeGetterValidator) : SubTypeMetadataFactory {
2724

2825
override fun getGsonSubType(
2926
gsonSubType: GsonSubtype,
3027
fieldType: FieldType.Other,
3128
fieldName: String,
3229
element: TypeElement): SubTypeMetadata {
3330

34-
val jsonKeys = gsonSubType.jsonKeys
35-
validateJsonKeys(element, jsonKeys)
31+
subTypeJsonKeysValidator.validateJsonKeys(element, gsonSubType.jsonKeys)
3632

3733
return getGsonSubtypeGetterMethod(element)
38-
.also { methodContent -> validateGsonSubtypeGetterMethod(element, methodContent) }
39-
.let { methodContent -> createSubTypeMetadata(methodContent, jsonKeys) }
40-
}
41-
42-
private fun validateJsonKeys(classElement: TypeElement, jsonKeys: Array<String>) {
43-
if (jsonKeys.isEmpty()) {
44-
throw ProcessingException("At least one json key must be defined for GsonSubType", classElement)
45-
}
46-
47-
if (jsonKeys.any { it.isBlank() }) {
48-
throw ProcessingException("A blank json key is not valid for GsonSubType", classElement)
49-
}
50-
51-
jsonKeys.groupingBy { it }
52-
.eachCount()
53-
.filter { it.value > 1 }
54-
.keys
55-
.firstOrNull()
56-
.let {
57-
if (it != null) {
58-
throw ProcessingException("The json key '\"$it\"' appears more than once", classElement)
59-
}
60-
}
34+
.also { methodContent -> subTypeGetterValidator.validateGsonSubtypeGetterMethod(element, methodContent) }
35+
.let { methodContent -> createSubTypeMetadata(methodContent, gsonSubType.jsonKeys) }
6136
}
6237

6338
private fun getGsonSubtypeGetterMethod(classElement: TypeElement): MethodElementContent {
@@ -72,38 +47,25 @@ class SubTypeMetadataFactoryImpl(
7247
}
7348
}
7449

75-
private fun validateGsonSubtypeGetterMethod(classElement: TypeElement, methodContent: MethodElementContent) {
76-
val actualReturnType = typeHandler.getTypeName(methodContent.generifiedElement.returnType)
77-
val elementTypeName = TypeName.get(classElement.asType())
78-
val expectedReturnType = ParameterizedTypeName.get(ClassName.get(Class::class.java), WildcardTypeName.subtypeOf(elementTypeName))
79-
80-
if (actualReturnType != expectedReturnType) {
81-
throw ProcessingException("Incorrect return type for @GsonSubtypeGetter method. It must be Class<? extends ${classElement.simpleName}>", methodContent.element)
82-
}
83-
}
84-
8550
private fun createSubTypeMetadata(methodContent: MethodElementContent, jsonKeys: Array<String>): SubTypeMetadata {
86-
val executableType = methodContent.element as ExecutableElement
87-
88-
val parameters = executableType.parameters
51+
val parameters = methodContent.parameterElementContents
8952
if (parameters.size != jsonKeys.size) {
9053
throw ProcessingException("The parameters size does not match the json keys size", methodContent.element)
9154
}
9255

9356
val fieldInfoList = parameters.zip(jsonKeys)
9457
.mapIndexed { index, (parameter, key) ->
95-
val nonNullAnnotationExists = parameter.annotationMirrors
58+
val nonNullAnnotationExists = parameter.element.annotationMirrors
9659
.any { NullableUtil.isNullableKeyword(it.annotationType.asElement().simpleName.toString()) }
9760

98-
val parameterTypeName = TypeName.get(parameter.asType())
99-
val nullable = !nonNullAnnotationExists && !parameterTypeName.isPrimitive
61+
val nullable = !nonNullAnnotationExists && !parameter.typeName.isPrimitive
10062

101-
GsonSubTypeFieldInfo(key, "subTypeElement$index", parameterTypeName, nullable)
63+
GsonSubTypeFieldInfo(key, "subTypeElement$index", parameter.typeName, nullable)
10264
}
10365

10466
return SubTypeMetadata(
10567
fieldInfoList,
106-
methodContent.element.simpleName.toString()
68+
methodContent.methodName
10769
)
10870
}
10971
}

compiler/standard/src/main/java/gsonpath/adapter/standard/interf/InterfaceModelMetadataFactory.kt

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ import com.squareup.javapoet.TypeName
44
import gsonpath.ProcessingException
55
import gsonpath.util.MethodElementContent
66
import gsonpath.util.TypeHandler
7-
import javax.lang.model.element.Element
87
import javax.lang.model.element.TypeElement
9-
import javax.lang.model.type.ExecutableType
10-
import javax.lang.model.type.TypeMirror
118

129
class InterfaceModelMetadataFactory(private val typeHandler: TypeHandler) {
1310

@@ -18,32 +15,25 @@ class InterfaceModelMetadataFactory(private val typeHandler: TypeHandler) {
1815
private fun createMetadata(methodElementContent: MethodElementContent): InterfaceModelMetadata {
1916
val methodElement = methodElementContent.element
2017

21-
// Ensure that any generics have been converted into their actual return types.
22-
val returnTypeMirror: TypeMirror = methodElementContent.generifiedElement.returnType
23-
val typeName = typeHandler.getTypeName(returnTypeMirror)
24-
25-
if (typeName == null || typeName == TypeName.VOID) {
18+
val typeName = methodElementContent.returnTypeName
19+
if (typeName == TypeName.VOID) {
2620
throw ProcessingException("Gson Path interface methods must have a return type", methodElement)
2721
}
2822

29-
(methodElement.asType() as ExecutableType).let {
30-
if (it.parameterTypes.isNotEmpty()) {
31-
throw ProcessingException("Gson Path interface methods must not have parameters", methodElement)
32-
}
23+
if (methodElementContent.parameterElementContents.isNotEmpty()) {
24+
throw ProcessingException("Gson Path interface methods must not have parameters", methodElement)
3325
}
3426

35-
return InterfaceModelMetadata(typeName, methodElement.getFieldName(), methodElement,
36-
methodElement.getMethodName(), returnTypeMirror)
27+
return InterfaceModelMetadata(typeName, methodElementContent.getFieldName(), methodElement,
28+
methodElementContent.methodName, methodElementContent.returnTypeMirror)
3729
}
3830

39-
private fun Element.getMethodName() = simpleName.toString()
40-
4131
/**
4232
* Transform the method name into the field name by removing the first camel-cased portion.
4333
* e.g. 'getName' becomes 'name'
4434
*/
45-
private fun Element.getFieldName() = getMethodName().let { methodName ->
46-
methodName.indexOfFirst(Char::isUpperCase)
35+
private fun MethodElementContent.getFieldName(): String {
36+
return methodName.indexOfFirst(Char::isUpperCase)
4737
.takeIf { it != -1 }
4838
?.let { methodName[it].toLowerCase() + methodName.substring(it + 1) }
4939
?: methodName

compiler/standard/src/main/java/gsonpath/dependencies/DependencyFactory.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package gsonpath.dependencies
22

33
import gsonpath.adapter.common.SubTypeMetadataFactoryImpl
4+
import gsonpath.adapter.common.SubTypeGetterValidator
5+
import gsonpath.adapter.common.SubTypeJsonKeysValidator
46
import gsonpath.adapter.enums.EnumFieldLabelMapper
57
import gsonpath.adapter.enums.EnumGsonAdapterGenerator
68
import gsonpath.adapter.standard.adapter.AdapterModelMetadataFactory
@@ -38,7 +40,7 @@ object DependencyFactory {
3840
FieldPathFetcher(SerializedNameFetcher, FieldNamingPolicyMapper()))
3941
val gsonObjectTreeFactory = GsonObjectTreeFactory(gsonObjectFactory)
4042

41-
val subTypeMetadataFactory = SubTypeMetadataFactoryImpl(typeHandler)
43+
val subTypeMetadataFactory = SubTypeMetadataFactoryImpl(typeHandler, SubTypeJsonKeysValidator(), SubTypeGetterValidator())
4244
val extensions = loadExtensions(processingEnv)
4345
val extensionsHandler = ExtensionsHandler(processingEnv, extensions)
4446
val readFunctions = ReadFunctions(extensionsHandler)

compiler/testing/base/src/main/java/gsonpath/generator/processingExceptionMatcher.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import javax.lang.model.element.Element
88
fun processingExceptionMatcher(element: Element?, message: String): TypeSafeMatcher<ProcessingException> {
99
return object : TypeSafeMatcher<ProcessingException>() {
1010
override fun describeTo(description: Description) {
11+
description.appendText("message: '$message'")
1112
}
1213

1314
override fun matchesSafely(item: ProcessingException): Boolean {

0 commit comments

Comments
 (0)