ultrasonic-app-subsonic-and.../core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/VersionAwareJacksonConverte...

88 lines
3.2 KiB
Kotlin

package org.moire.ultrasonic.api.subsonic
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.ObjectReader
import java.lang.reflect.Type
import okhttp3.RequestBody
import okhttp3.ResponseBody
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
import retrofit2.Converter
import retrofit2.Retrofit
import retrofit2.converter.jackson.JacksonConverterFactory
/**
* Retrofit Converter Factory which uses Jackson for conversion and maintains the
* version of the Subsonic API.
* @param notifier: callback function to call when the Subsonic API version changes
*/
class VersionAwareJacksonConverterFactory(
private val notifier: (SubsonicAPIVersions) -> Unit = {}
) : Converter.Factory() {
constructor(
notifier: (SubsonicAPIVersions) -> Unit = {},
mapper: ObjectMapper
) : this(notifier) {
this.mapper = mapper
jacksonConverterFactory = JacksonConverterFactory.create(mapper)
}
private var mapper: ObjectMapper? = null
private var jacksonConverterFactory: JacksonConverterFactory? = null
override fun responseBodyConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
val javaType: JavaType = mapper!!.typeFactory.constructType(type)
val reader: ObjectReader? = mapper!!.readerFor(javaType)
return VersionAwareResponseBodyConverter<Any>(notifier, reader!!)
}
override fun requestBodyConverter(
type: Type,
parameterAnnotations: Array<Annotation>,
methodAnnotations: Array<Annotation>,
retrofit: Retrofit
): Converter<*, RequestBody>? {
return jacksonConverterFactory?.requestBodyConverter(
type, parameterAnnotations, methodAnnotations, retrofit
)
}
companion object {
@JvmOverloads // Guarding public API nullability.
fun create(
notifier: (SubsonicAPIVersions) -> Unit = {},
mapper: ObjectMapper? = ObjectMapper()
): VersionAwareJacksonConverterFactory {
if (mapper == null) throw NullPointerException("mapper == null")
return VersionAwareJacksonConverterFactory(notifier, mapper)
}
}
class VersionAwareResponseBodyConverter<T> (
private val notifier: (SubsonicAPIVersions) -> Unit = {},
private val adapter: ObjectReader
) : Converter<ResponseBody, T> {
override fun convert(value: ResponseBody): T {
value.use {
// The response stream contains the version of the API for parsing the stream
// to an object. Currently the parsing is independent from the version as new
// versions only contain extra optional fields.
val response: T = adapter.readValue(value.charStream())
if (response is SubsonicResponse) {
try {
notifier(response.version)
} catch (e: IllegalArgumentException) {
// no-op
}
}
return response
}
}
}
}