thrifty
thrifty copied to clipboard
Reflective extensions code dump
Talked offline, here's a dump of reflective extensions we currently employ because 🙃
package com.uber.thriftyextensions
import com.microsoft.thrifty.schema.Field
import com.microsoft.thrifty.schema.Location
import com.microsoft.thrifty.schema.Requiredness
import com.microsoft.thrifty.schema.UserElement
import com.microsoft.thrifty.schema.UserType
import com.microsoft.thrifty.schema.parser.ConstValueElement
import com.microsoft.thrifty.schema.parser.FieldElement
import java.lang.reflect.Modifier
import java.util.UUID
/*
* Unsafe reflection-based extensions to Thrifty's APIs. Necessary because there's no APIs available
* for adjusting [FieldElement] APIs.
*/
/**
* Creates a new copy of this [Field] with `required` [Field.required]ness reflectively set via its
* internal [FieldElement] field.
*
* @receiver the source [Field] to use. A defensive copy will be made.
* @return the new [Field] instance.
*/
fun Field.makeRequired(): Field {
val newElement = (elementField.get(this) as FieldElement).copy(requiredness = Requiredness.REQUIRED)
return toBuilder().build()
.apply {
elementField.set(this, newElement)
}
}
/**
* Creates a new copy of this [Field] with `optional` [Field.required]ness reflectively set via its
* internal [FieldElement] field.
*
* @receiver the source [Field] to use. A defensive copy will be made.
* @return the new [Field] instance.
*/
fun Field.makeOptional(): Field {
val newElement = (elementField.get(this) as FieldElement).copy(requiredness = Requiredness.OPTIONAL)
return toBuilder().build()
.apply {
elementField.set(this, newElement)
}
}
/**
* Creates a new copy of this [Field] with the specified [value] default value reflectively set via
* its internal [FieldElement] field.
*
* @receiver the source [Field] to use. A defensive copy will be made.
* @return the new [Field] instance.
*/
fun Field.withDefaultValue(value: ConstValueElement): Field {
val newElement = (elementField.get(this) as FieldElement).copy(constValue = value)
return toBuilder().build()
.apply {
elementField.set(this, newElement)
}
}
/**
* Creates a new copy of this [Field] with the specified [newId] reflectively set via its internal
* [FieldElement] field.
*
* @param newId the new id to set.
* @receiver the source [Field] to use. A defensive copy will be made.
* @return the new [Field] instance.
*/
fun Field.newId(newId: Int): Field {
val newElement = (elementField.get(this) as FieldElement).copy(fieldId = newId)
return toBuilder().build()
.apply {
elementField.set(this, newElement)
}
}
/**
* Ensures [this] list of [Field]s are valid (such as ensuring they've got unique field IDs).
*
* @param ensuredLocation the [Location] of the element holding this list to ensure fields match it.
* @receiver the source list to check. A defensive new copy will be made.
* @return the new fields list with the new field appended to the end.
*/
fun List<Field>.asSafeFields(ensuredLocation: Location): List<Field> {
if (distinctBy { it.id }.size == size) {
// They're all distinct, just return them with locations ensured
return map {
it.toBuilder()
.location(it.location.matchTo(ensuredLocation))
.build()
}
}
val safeFields = fold(emptyList<Field>()) { cur, next ->
cur.safeAddField(next, ensuredLocation)
}
check(safeFields.size == size) {
"Data lost!"
}
return safeFields
}
/**
* Safely adds a new [Field] to this collection while ensuring that it does not conflict with the
* existing field ids.
*
* @param newField the new [Field] to add.
* @param ensuredLocation the [Location] of the element holding this list to ensure fields match it.
* @receiver the source list to add this to. A defensive new copy will be made.
* @return the new fields list with the new field appended to the end.
*/
fun List<Field>.safeAddField(newField: Field, ensuredLocation: Location): List<Field> {
val defensiveCopy = newField.toBuilder()
.location(newField.location.matchTo(ensuredLocation))
.build()
if (none { it.id == newField.id }) {
return plus(defensiveCopy)
}
val finalField = map(Field::id).max()?.let { maxId ->
defensiveCopy.newId(maxId + 1)
} ?: defensiveCopy
return plus(finalField)
}
private fun Location.matchTo(source: Location): Location {
return if (base == source.base && path == source.path) {
this
} else {
Location.get(source.base, source.path)
}
}
/**
* Creates a new copy of this [Field] with the specified [newUUID] reflectively set via its internal
* [FieldElement] field.
*
* @param newUUID the new UUID to use. Defaults to a randomly generated one.
* @receiver the source [Field] to use. A defensive copy will be made.
* @return the new [Field] instance.
*/
fun <T : UserElement> T.newUuid(newUUID: UUID = UUID.randomUUID()): T {
if (this is UserType) {
UserType::class.java.getDeclaredField("mixin")
} else {
javaClass.getDeclaredField("mixin")
}
.apply {
isAccessible = true
val modifiersField = java.lang.reflect.Field::class.java
.getDeclaredField("modifiers")
modifiersField.isAccessible = true
modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
}
.get(this)
.let { mixin ->
// UserElementMixin
mixin.javaClass.getDeclaredField("uuid")
.apply {
isAccessible = true
val modifiersField = java.lang.reflect.Field::class.java
.getDeclaredField("modifiers")
modifiersField.isAccessible = true
modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
}
.apply {
val olduuid = uuid
set(mixin, newUUID)
check(olduuid != uuid) {
"New UUID setting failed!"
}
}
}
return this
}
/**
* Sets a new default value for a given [Field].
*
* @param const the new [ConstValueElement] to set
* @return this same [Field] instance.
*/
fun Field.setDefaultValue(const: ConstValueElement): Field {
val newElement = (elementField.get(this) as FieldElement).copy(constValue = const)
elementField.set(this, newElement)
return this
}
private val elementField = Field::class.java.getDeclaredField("element")
.apply {
isAccessible = true
val modifiersField = java.lang.reflect.Field::class.java
.getDeclaredField("modifiers")
modifiersField.isAccessible = true
modifiersField.setInt(this, modifiers and Modifier.FINAL.inv())
}