Value.kt
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.health.services.client.data
import android.os.Parcel
import android.os.Parcelable
/** A [Parcelable] wrapper that can hold a value of a specified type. */
@Suppress("DataClassPrivateConstructor")
public data class Value
private constructor(
// TODO(b/175054913): Investigate using a AutoOneOf instead.
val format: Int,
val doubleList: List<Double>,
val longValue: Long,
) : Parcelable {
/**
* Returns this [Value] represented as an `long`.
*
* @throws IllegalStateException if [isLong] is `false`
*/
public fun asLong(): Long {
check(isLong) { "Attempted to read value as long, but value is not of type long" }
return longValue
}
/**
* Returns this [Value] represented as an `boolean`.
*
* @throws IllegalStateException if [isBoolean] is `false`
*/
public fun asBoolean(): Boolean {
check(isBoolean) { "Attempted to read value as boolean, but value is not of type boolean" }
return longValue != 0L
}
/**
* Returns this [Value] represented as a `double`.
*
* @throws IllegalStateException if [isDouble] is `false`
*/
public fun asDouble(): Double {
check(isDouble) { "Attempted to read value as double, but value is not of type double" }
return doubleList[0]
}
/**
* Returns this [Value] represented as a `double[]`.
*
* @throws IllegalStateException if [isDoubleArray] is `false`
*/
public fun asDoubleArray(): DoubleArray {
check(isDoubleArray) {
"Attempted to read value as double array, but value is not correct type"
}
return doubleList.toDoubleArray()
}
/** Whether or not this [Value] can be represented as an `long`. */
public val isLong: Boolean
get() = format == FORMAT_LONG
/** Whether or not this [Value] can be represented as an `boolean`. */
public val isBoolean: Boolean
get() = format == FORMAT_BOOLEAN
/** Whether or not this [Value] can be represented as a `double`. */
public val isDouble: Boolean
get() = format == FORMAT_DOUBLE
/** Whether or not this [Value] can be represented as a `double[]`. */
public val isDoubleArray: Boolean
get() = format == FORMAT_DOUBLE_ARRAY
override fun describeContents(): Int {
return 0
}
/**
* Writes the value of this object to [dest].
*
* @throws IllegalStateException if [format] is invalid
*/
override fun writeToParcel(dest: Parcel, flags: Int) {
val format = format
dest.writeInt(format)
when (format) {
FORMAT_BOOLEAN, FORMAT_LONG -> {
dest.writeLong(longValue)
return
}
FORMAT_DOUBLE -> {
dest.writeDouble(asDouble())
return
}
FORMAT_DOUBLE_ARRAY -> {
val doubleArray = asDoubleArray()
dest.writeInt(doubleArray.size)
dest.writeDoubleArray(doubleArray)
return
}
else -> {}
}
throw IllegalStateException(String.format("Unexpected format: %s", format))
}
public companion object {
/** The format used for a [Value] represented as a `double`. */
public const val FORMAT_DOUBLE: Int = 1
/** The format used for a [Value] represented as an `long`. */
public const val FORMAT_LONG: Int = 2
/** The format used for a [Value] represented as an `boolean`. */
public const val FORMAT_BOOLEAN: Int = 4
/** The format used for a [Value] represented as a `double[]`. */
public const val FORMAT_DOUBLE_ARRAY: Int = 3
@JvmField
public val CREATOR: Parcelable.Creator<Value> =
object : Parcelable.Creator<Value> {
override fun createFromParcel(parcel: Parcel): Value {
val format = parcel.readInt()
when (format) {
FORMAT_BOOLEAN, FORMAT_LONG ->
return Value(format, listOf(), parcel.readLong())
FORMAT_DOUBLE ->
return Value(format, listOf(parcel.readDouble()), /* longValue= */ 0)
FORMAT_DOUBLE_ARRAY -> {
val doubleArray = DoubleArray(parcel.readInt())
parcel.readDoubleArray(doubleArray)
return Value(format, doubleArray.toList(), /* longValue= */ 0)
}
else -> {}
}
throw IllegalStateException(String.format("Unexpected format: %s", format))
}
override fun newArray(size: Int): Array<Value?> {
return arrayOfNulls(size)
}
}
/** Creates a [Value] that represents a `long`. */
@JvmStatic
public fun ofLong(value: Long): Value {
return Value(FORMAT_LONG, listOf(), value)
}
/** Creates a [Value] that represents an `boolean`. */
@JvmStatic
public fun ofBoolean(value: Boolean): Value {
return Value(FORMAT_BOOLEAN, listOf(), if (value) 1 else 0)
}
/** Creates a [Value] that represents a `double`. */
@JvmStatic
public fun ofDouble(value: Double): Value {
return Value(FORMAT_DOUBLE, listOf(value), longValue = 0)
}
/** Creates a [Value] that represents a `double[]`. */
@JvmStatic
public fun ofDoubleArray(vararg doubleArray: Double): Value {
return Value(FORMAT_DOUBLE_ARRAY, doubleArray.toList(), longValue = 0)
}
/**
* Compares two [Value] s based on their representation.
*
* @throws IllegalStateException if `first` and `second` do not share the same format or are
* represented as a `double[]`
*/
internal fun compare(first: Value, second: Value): Int {
check(first.format == second.format) {
"Attempted to compare Values with different formats"
}
when (first.format) {
FORMAT_LONG -> return first.longValue.compareTo(second.longValue)
FORMAT_BOOLEAN -> return first.asBoolean().compareTo(second.asBoolean())
FORMAT_DOUBLE -> return first.doubleList[0].compareTo(second.doubleList[0])
FORMAT_DOUBLE_ARRAY ->
throw IllegalStateException(
"Attempted to compare Values with invalid format (double array)"
)
else -> {}
}
throw IllegalStateException(String.format("Unexpected format: %s", first.format))
}
/**
* Adds two [Value] s based on their representation.
*
* @throws IllegalStateException if `first` and `second` do not share the same format or are
* represented as a `double[]` or `boolean`
*/
@JvmStatic
public fun sum(first: Value, second: Value): Value {
require(first.format == second.format) {
"Attempted to add Values with different formats"
}
when (first.format) {
FORMAT_LONG -> return ofLong(first.asLong() + second.asLong())
FORMAT_DOUBLE -> return ofDouble(first.asDouble() + second.asDouble())
FORMAT_BOOLEAN ->
throw IllegalStateException(
"Attempted to add Values with invalid format (boolean)"
)
FORMAT_DOUBLE_ARRAY ->
throw IllegalStateException(
"Attempted to add Values with invalid format (double array)"
)
else -> {}
}
throw IllegalStateException(String.format("Unexpected format: %s", first.format))
}
}
}