mirror of
https://github.com/corda/corda.git
synced 2025-05-29 13:44:25 +00:00
CORDA-979 - Make public java setter accessible from within Java
Backport from master
This commit is contained in:
parent
98b091a0eb
commit
ac416690e0
@ -123,12 +123,18 @@ abstract class PropertyAccessor(
|
|||||||
class PropertyAccessorGetterSetter(
|
class PropertyAccessorGetterSetter(
|
||||||
initialPosition: Int,
|
initialPosition: Int,
|
||||||
getter: PropertySerializer,
|
getter: PropertySerializer,
|
||||||
private val setter: Method?) : PropertyAccessor(initialPosition, getter) {
|
private val setter: Method) : PropertyAccessor(initialPosition, getter) {
|
||||||
|
init {
|
||||||
|
/**
|
||||||
|
* Play nicely with Java interop, public methods aren't marked as accessible
|
||||||
|
*/
|
||||||
|
setter.isAccessible = true
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Invokes the setter on the underlying object passing in the serialized value.
|
* Invokes the setter on the underlying object passing in the serialized value.
|
||||||
*/
|
*/
|
||||||
override fun set(instance: Any, obj: Any?) {
|
override fun set(instance: Any, obj: Any?) {
|
||||||
setter?.invoke(instance, *listOf(obj).toTypedArray())
|
setter.invoke(instance, *listOf(obj).toTypedArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,12 +89,18 @@ fun isConcrete(clazz: Class<*>): Boolean = !(clazz.isInterface || Modifier.isAbs
|
|||||||
* @property getter the method of a class that returns a fields value. Determined by
|
* @property getter the method of a class that returns a fields value. Determined by
|
||||||
* locating a function named getXyz for the property named in field as xyz.
|
* locating a function named getXyz for the property named in field as xyz.
|
||||||
*/
|
*/
|
||||||
data class PropertyDescriptor(var field: Field?, var setter: Method?, var getter: Method?) {
|
data class PropertyDescriptor(var field: Field?, var setter: Method?, var getter: Method?, var iser: Method?) {
|
||||||
override fun toString() = StringBuilder("").apply {
|
override fun toString() = StringBuilder("").apply {
|
||||||
appendln("Property - ${field?.name ?: "null field"}\n")
|
appendln("Property - ${field?.name ?: "null field"}\n")
|
||||||
appendln(" getter - ${getter?.name ?: "no getter"}")
|
appendln(" getter - ${getter?.name ?: "no getter"}")
|
||||||
append(" setter - ${setter?.name ?: "no setter"}")
|
appendln(" setter - ${setter?.name ?: "no setter"}")
|
||||||
|
appendln(" iser - ${iser?.name ?: "no isXYZ defined"}")
|
||||||
}.toString()
|
}.toString()
|
||||||
|
|
||||||
|
constructor() : this(null, null, null, null)
|
||||||
|
constructor(field: Field?) : this(field, null, null, null)
|
||||||
|
|
||||||
|
fun preferredGetter() : Method? = getter ?: iser
|
||||||
}
|
}
|
||||||
|
|
||||||
object PropertyDescriptorsRegex {
|
object PropertyDescriptorsRegex {
|
||||||
@ -122,12 +128,10 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
|
|||||||
val classProperties = mutableMapOf<String, PropertyDescriptor>()
|
val classProperties = mutableMapOf<String, PropertyDescriptor>()
|
||||||
|
|
||||||
var clazz: Class<out Any?>? = this
|
var clazz: Class<out Any?>? = this
|
||||||
do {
|
clazz!!.declaredFields.forEach { classProperties.put(it.name, PropertyDescriptor(it)) }
|
||||||
// get the properties declared on this instance of class
|
|
||||||
clazz!!.declaredFields.forEach { classProperties.put(it.name, PropertyDescriptor(it, null, null)) }
|
|
||||||
|
|
||||||
// then pair them up with the declared getter and setter
|
do {
|
||||||
// Note: It is possible for a class to have multiple instancess of a function where the types
|
// Note: It is possible for a class to have multiple instances of a function where the types
|
||||||
// differ. For example:
|
// differ. For example:
|
||||||
// interface I<out T> { val a: T }
|
// interface I<out T> { val a: T }
|
||||||
// class D(override val a: String) : I<String>
|
// class D(override val a: String) : I<String>
|
||||||
@ -138,45 +142,45 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
|
|||||||
//
|
//
|
||||||
// In addition, only getters that take zero parameters and setters that take a single
|
// In addition, only getters that take zero parameters and setters that take a single
|
||||||
// parameter will be considered
|
// parameter will be considered
|
||||||
clazz.declaredMethods?.map { func ->
|
clazz!!.declaredMethods?.map { func ->
|
||||||
|
if (!Modifier.isPublic(func.modifiers)) return@map
|
||||||
|
if (func.name == "getClass") return@map
|
||||||
|
|
||||||
PropertyDescriptorsRegex.re.find(func.name)?.apply {
|
PropertyDescriptorsRegex.re.find(func.name)?.apply {
|
||||||
try {
|
// take into account those constructor properties that don't directly map to a named
|
||||||
classProperties.getValue(groups[2]!!.value.decapitalize()).apply {
|
// property which are, by default, already added to the map
|
||||||
when (groups[1]!!.value) {
|
classProperties.computeIfAbsent(groups[2]!!.value.decapitalize()) { PropertyDescriptor() }.apply {
|
||||||
"set" -> {
|
when (groups[1]!!.value) {
|
||||||
if (func.parameterCount == 1) {
|
"set" -> {
|
||||||
if (setter == null) setter = func
|
if (func.parameterCount == 1) {
|
||||||
else if (TypeToken.of(setter!!.genericReturnType).isSupertypeOf(func.genericReturnType)) {
|
if (setter == null) setter = func
|
||||||
setter = func
|
else if (TypeToken.of(setter!!.genericReturnType).isSupertypeOf(func.genericReturnType)) {
|
||||||
}
|
setter = func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"get" -> {
|
}
|
||||||
if (func.parameterCount == 0) {
|
"get" -> {
|
||||||
if (getter == null) getter = func
|
if (func.parameterCount == 0) {
|
||||||
else if (TypeToken.of(getter!!.genericReturnType).isSupertypeOf(func.genericReturnType)) {
|
if (getter == null) getter = func
|
||||||
getter = func
|
else if (TypeToken.of(getter!!.genericReturnType).isSupertypeOf(func.genericReturnType)) {
|
||||||
}
|
getter = func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"is" -> {
|
}
|
||||||
if (func.parameterCount == 0) {
|
"is" -> {
|
||||||
val rtnType = TypeToken.of(func.genericReturnType)
|
if (func.parameterCount == 0) {
|
||||||
if ((rtnType == TypeToken.of(Boolean::class.java))
|
val rtnType = TypeToken.of(func.genericReturnType)
|
||||||
|| (rtnType == TypeToken.of(Boolean::class.javaObjectType))) {
|
if ((rtnType == TypeToken.of(Boolean::class.java))
|
||||||
if (getter == null) getter = func
|
|| (rtnType == TypeToken.of(Boolean::class.javaObjectType))) {
|
||||||
}
|
if (iser == null) iser = func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: NoSuchElementException) {
|
|
||||||
// handles the getClass case from java.lang.Object
|
|
||||||
return@apply
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clazz = clazz?.superclass
|
clazz = clazz.superclass
|
||||||
} while (clazz != null)
|
} while (clazz != null)
|
||||||
|
|
||||||
return classProperties
|
return classProperties
|
||||||
@ -260,7 +264,7 @@ fun propertiesForSerializationFromSetters(
|
|||||||
return mutableListOf<PropertyAccessorGetterSetter>().apply {
|
return mutableListOf<PropertyAccessorGetterSetter>().apply {
|
||||||
var idx = 0
|
var idx = 0
|
||||||
properties.forEach { property ->
|
properties.forEach { property ->
|
||||||
val getter: Method? = property.value.getter
|
val getter: Method? = property.value.preferredGetter()
|
||||||
val setter: Method? = property.value.setter
|
val setter: Method? = property.value.setter
|
||||||
|
|
||||||
if (getter == null || setter == null) return@forEach
|
if (getter == null || setter == null) return@forEach
|
||||||
@ -270,13 +274,20 @@ fun propertiesForSerializationFromSetters(
|
|||||||
"takes too many arguments")
|
"takes too many arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
val setterType = setter.parameterTypes.getOrNull(0)!!
|
val setterType = setter.parameterTypes[0]!!
|
||||||
|
|
||||||
if (!(TypeToken.of(property.value.field?.genericType!!).isSupertypeOf(setterType))) {
|
if (!(TypeToken.of(property.value.field?.genericType!!).isSupertypeOf(setterType))) {
|
||||||
throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " +
|
throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " +
|
||||||
"takes parameter of type $setterType yet underlying type is " +
|
"takes parameter of type $setterType yet underlying type is " +
|
||||||
"${property.value.field?.genericType!!}")
|
"${property.value.field?.genericType!!}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure the setter returns the same type (within inheritance bounds) the getter accepts
|
||||||
|
if (!(TypeToken.of (setterType).isSupertypeOf(getter.returnType))) {
|
||||||
|
throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " +
|
||||||
|
"takes parameter of type $setterType yet the defined getter returns a value of type " +
|
||||||
|
"${getter.returnType}")
|
||||||
|
}
|
||||||
this += PropertyAccessorGetterSetter(
|
this += PropertyAccessorGetterSetter(
|
||||||
idx++,
|
idx++,
|
||||||
PropertySerializer.make(property.value.field!!.name, PublicPropertyReader(getter),
|
PropertySerializer.make(property.value.field!!.name, PublicPropertyReader(getter),
|
||||||
|
@ -286,12 +286,8 @@ public class SetterConstructorTests {
|
|||||||
tm.setA(10);
|
tm.setA(10);
|
||||||
assertEquals("10", tm.getA());
|
assertEquals("10", tm.getA());
|
||||||
|
|
||||||
TypeMismatch post = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(tm),
|
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf (
|
||||||
TypeMismatch.class);
|
NotSerializableException.class);
|
||||||
|
|
||||||
// because there is a type mismatch in the class, it won't return that info as a BEAN property and thus
|
|
||||||
// we won't serialise it and thus on deserialization it won't be initialized
|
|
||||||
Assertions.assertThatThrownBy(() -> post.getA()).isInstanceOf(NullPointerException.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -306,11 +302,7 @@ public class SetterConstructorTests {
|
|||||||
tm.setA("10");
|
tm.setA("10");
|
||||||
assertEquals((Integer)10, tm.getA());
|
assertEquals((Integer)10, tm.getA());
|
||||||
|
|
||||||
TypeMismatch2 post = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(tm),
|
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf(
|
||||||
TypeMismatch2.class);
|
NotSerializableException.class);
|
||||||
|
|
||||||
// because there is a type mismatch in the class, it won't return that info as a BEAN property and thus
|
|
||||||
// we won't serialise it and thus on deserialization it won't be initialized
|
|
||||||
assertEquals(null, post.getA());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user