Skip to content

Commit d62db60

Browse files
Added TypeInfo implementation and tests.
1 parent a0f8356 commit d62db60

File tree

7 files changed

+312
-15
lines changed

7 files changed

+312
-15
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright 2020-2021 Matthew Layton
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 io.onixlabs.corda.core
18+
19+
/**
20+
* Creates a [TypeInfo] containing the underlying type information for the specified type [T].
21+
*
22+
* @param T The underlying type from which to obtain type information.
23+
*/
24+
inline fun <reified T> typeInfo(): TypeInfo<T> {
25+
return typeReference<T>().toTypeInfo()
26+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Copyright 2020-2021 Matthew Layton
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 io.onixlabs.corda.core
18+
19+
import net.corda.core.serialization.CordaSerializable
20+
import java.lang.reflect.ParameterizedType
21+
import java.lang.reflect.Type
22+
import java.lang.reflect.WildcardType
23+
import java.util.*
24+
25+
/**
26+
* Represents a type graph which can be obtained from a reified type.
27+
*
28+
* @param T The underlying type of the type being represented.
29+
* @param typeClass The class of the underlying type.
30+
* @param typeArguments The arguments of the type, if the type is generic.
31+
*/
32+
@CordaSerializable
33+
class TypeInfo<T> private constructor(val typeClass: Class<T>, val typeArguments: List<TypeInfo<*>>) {
34+
35+
companion object {
36+
37+
/**
38+
* Creates a [TypeInfo] instance from the specified [TypeReference].
39+
*
40+
* @param T The underlying type of the type being represented.
41+
* @param typeReference The [TypeReference] from which to obtain type information.
42+
* @return Returns a [TypeInfo] containing the type information created from the specified [TypeReference].
43+
*/
44+
fun <T> fromTypeReference(typeReference: TypeReference<T>): TypeInfo<T> {
45+
return fromType(typeReference.type)
46+
}
47+
48+
/**
49+
* Creates a [TypeInfo] instance from the specified reified type.
50+
* @param T The underlying type of the type being represented.
51+
* @return Returns a [TypeInfo] containing the type information created from the specified reified type.
52+
*/
53+
inline fun <reified T> fromType(): TypeInfo<T> {
54+
return fromTypeReference(object : TypeReference<T>() {})
55+
}
56+
57+
/**
58+
* Creates a [TypeInfo] instance from the specified [Type].
59+
*
60+
* @param T The underlying type of the type being represented.
61+
* @param type The [Type] from which to obtain type information.
62+
* @return Returns a [TypeInfo] containing the type information created from the specified [Type].
63+
*/
64+
private fun <T> fromType(type: Type): TypeInfo<T> {
65+
66+
fun getArgumentList(type: Type): List<Type> = when (type) {
67+
is ParameterizedType -> type.actualTypeArguments.toList()
68+
is WildcardType -> type.upperBounds.flatMap { getArgumentList(it) }
69+
else -> emptyList()
70+
}
71+
72+
return TypeInfo(type.toTypedClass(), getArgumentList(type).map { fromType<Any>(it) })
73+
}
74+
}
75+
76+
/**
77+
* Determines whether the specified object is equal to the current object.
78+
*
79+
* @param other The object to compare with the current object.
80+
* @return Returns true if the specified object is equal to the current object; otherwise, false.
81+
*/
82+
override fun equals(other: Any?): Boolean {
83+
return this === other || (other is TypeInfo<*>
84+
&& typeClass == other.typeClass
85+
&& typeArguments == other.typeArguments
86+
&& toString() == other.toString())
87+
}
88+
89+
/**
90+
* Serves as the default hash function.
91+
*
92+
* @return Returns a hash code for the current object.
93+
*/
94+
override fun hashCode(): Int {
95+
return Objects.hash(typeClass, typeArguments)
96+
}
97+
98+
/**
99+
* Returns a string that represents the current object.
100+
*
101+
* @return Returns a string that represents the current object.
102+
*/
103+
override fun toString(): String = buildString {
104+
append(typeClass.simpleName)
105+
if (typeArguments.isNotEmpty()) append("<${typeArguments.joinToString(", ")}>")
106+
}
107+
}

onixlabs-corda-core-contract/src/main/kotlin/io/onixlabs/corda/core/TypeReference.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ abstract class TypeReference<T> : Comparable<TypeReference<T>> {
4444
return 0
4545
}
4646

47+
/**
48+
* Creates a [TypeInfo] from this [TypeReference].
49+
*
50+
* @return Returns a [TypeInfo] from this [TypeReference].
51+
*/
52+
fun toTypeInfo(): TypeInfo<T> {
53+
return TypeInfo.fromTypeReference(this)
54+
}
55+
4756
/**
4857
* Gets the underlying generic type.
4958
*

onixlabs-corda-core-workflow/src/main/kotlin/io/onixlabs/corda/core/query/FindStateFlow.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import net.corda.core.node.services.vault.QueryCriteria
3737
@StartableByRPC
3838
@StartableByService
3939
abstract class FindStateFlow<T> : FlowLogic<StateAndRef<T>?>() where T : ContractState {
40-
protected val contractStateType: Class<T> = javaClass.getArgumentType(0).toTypedClass()
40+
protected open val contractStateType: Class<T> get() = javaClass.getArgumentType(0).toTypedClass()
4141
protected abstract val criteria: QueryCriteria
4242
protected abstract val pageSpecification: PageSpecification
4343

onixlabs-corda-core-workflow/src/main/kotlin/io/onixlabs/corda/core/query/FindStatesFlow.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import net.corda.core.node.services.vault.Sort
3838
@StartableByRPC
3939
@StartableByService
4040
abstract class FindStatesFlow<T : ContractState> : FlowLogic<List<StateAndRef<T>>>() {
41-
protected val contractStateType: Class<T> = javaClass.getArgumentType(0).toTypedClass()
41+
protected open val contractStateType: Class<T> get() = javaClass.getArgumentType(0).toTypedClass()
4242
protected abstract val criteria: QueryCriteria
4343
protected abstract val pageSpecification: PageSpecification
4444
protected abstract val sorting: Sort
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* Copyright 2020-2021 Matthew Layton
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 io.onixlabs.corda.test
18+
19+
import io.onixlabs.corda.core.typeInfo
20+
import org.junit.jupiter.api.Test
21+
import java.math.BigDecimal
22+
import kotlin.test.assertEquals
23+
24+
class TypeInfoTests {
25+
26+
@Test
27+
fun `TypeInfo should correctly identify the Int type`() {
28+
29+
// Arrange
30+
val expected = Integer::class.java
31+
val typeInfo = typeInfo<Int>()
32+
33+
// Act
34+
val actual = typeInfo.typeClass as Class<*>
35+
36+
// Assert
37+
assertEquals(expected, actual)
38+
}
39+
40+
@Test
41+
fun `TypeInfo should correctly identify the String type`() {
42+
43+
// Arrange
44+
val expected = String::class.java
45+
val typeInfo = typeInfo<String>()
46+
47+
// Act
48+
val actual = typeInfo.typeClass as Class<*>
49+
50+
// Assert
51+
assertEquals(expected, actual)
52+
}
53+
54+
@Test
55+
fun `TypeInfo should correctly identify the BigDecimal type`() {
56+
57+
// Arrange
58+
val expected = BigDecimal::class.java
59+
val typeInfo = typeInfo<BigDecimal>()
60+
61+
// Act
62+
val actual = typeInfo.typeClass as Class<*>
63+
64+
// Assert
65+
assertEquals(expected, actual)
66+
}
67+
68+
@Test
69+
fun `TypeInfo should correctly identify the List of Int type`() {
70+
71+
// Arrange
72+
val expected = List::class.java
73+
val expectedParameter1 = typeInfo<Int>()
74+
val typeInfo = typeInfo<List<Int>>()
75+
76+
// Act
77+
val actual = typeInfo.typeClass as Class<*>
78+
val actualParameter1 = typeInfo.typeArguments.single()
79+
80+
// Assert
81+
assertEquals(expected, actual)
82+
assertEquals(expectedParameter1, actualParameter1)
83+
}
84+
85+
@Test
86+
fun `TypeInfo should correctly identify the List of String type`() {
87+
88+
// Arrange
89+
val expected = List::class.java
90+
val expectedParameter1 = typeInfo<String>()
91+
val typeInfo = typeInfo<List<String>>()
92+
93+
// Act
94+
val actual = typeInfo.typeClass as Class<*>
95+
val actualParameter1 = typeInfo.typeArguments.single()
96+
97+
// Assert
98+
assertEquals(expected, actual)
99+
assertEquals(expectedParameter1, actualParameter1)
100+
}
101+
102+
@Test
103+
fun `TypeInfo should correctly identify the List of BigDecimal type`() {
104+
105+
// Arrange
106+
val expected = List::class.java
107+
val expectedParameter1 = typeInfo<BigDecimal>()
108+
val typeInfo = typeInfo<List<BigDecimal>>()
109+
110+
// Act
111+
val actual = typeInfo.typeClass as Class<*>
112+
val actualParameter1 = typeInfo.typeArguments.single()
113+
114+
// Assert
115+
assertEquals(expected, actual)
116+
assertEquals(expectedParameter1, actualParameter1)
117+
}
118+
119+
@Test
120+
fun `TypeInfo should correctly identify the Map of String to Int type`() {
121+
122+
// Arrange
123+
val expected = Map::class.java
124+
val expectedParameter1 = typeInfo<String>()
125+
val expectedParameter2 = typeInfo<Int>()
126+
val typeInfo = typeInfo<Map<String, Int>>()
127+
128+
// Act
129+
val actual = typeInfo.typeClass as Class<*>
130+
val actualParameter1 = typeInfo.typeArguments.first()
131+
val actualParameter2 = typeInfo.typeArguments.last()
132+
133+
// Assert
134+
assertEquals(expected, actual)
135+
assertEquals(expectedParameter1, actualParameter1)
136+
assertEquals(expectedParameter2, actualParameter2)
137+
}
138+
139+
@Test
140+
fun `TypeInfo should correctly identify the List of wildcard type`() {
141+
142+
// Arrange
143+
val expected = List::class.java
144+
val expectedParameter1 = typeInfo<Any>()
145+
val typeInfo = typeInfo<List<*>>()
146+
147+
// Act
148+
val actual = typeInfo.typeClass as Class<*>
149+
val actualParameter1 = typeInfo.typeArguments.first()
150+
151+
// Assert
152+
assertEquals(expected, actual)
153+
assertEquals(expectedParameter1, actualParameter1)
154+
}
155+
}

0 commit comments

Comments
 (0)