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.Parcelable
import androidx.health.services.client.proto.DataProto
/** A [Parcelable] wrapper that can hold a value of a specified type. */
@Suppress("DataClassPrivateConstructor", "ParcelCreator")
public class Value internal constructor(proto: DataProto.Value) :
ProtoParcelable<DataProto.Value>() {
/** @hide */
override val proto: DataProto.Value by lazy { proto }
public val format: Int =
when (proto.valueCase) {
DataProto.Value.ValueCase.BOOL_VAL -> FORMAT_BOOLEAN
DataProto.Value.ValueCase.DOUBLE_VAL -> FORMAT_DOUBLE
DataProto.Value.ValueCase.LONG_VAL -> FORMAT_LONG
DataProto.Value.ValueCase.DOUBLE_ARRAY_VAL -> FORMAT_DOUBLE_ARRAY
else -> throw IllegalStateException("Unexpected format: ${proto.valueCase}")
}
/**
* Returns this [Value] as a `String`.
*
* @throws IllegalStateException if [format] is unknown
*/
override fun toString(): String =
"Value(format=$format, " +
when (format) {
FORMAT_BOOLEAN -> "boolVal=${proto.boolVal})"
FORMAT_LONG -> "longVal=${proto.longVal})"
FORMAT_DOUBLE -> "doubleVal=${proto.doubleVal})"
FORMAT_DOUBLE_ARRAY -> "doubleArrayVal=${proto.doubleArrayVal.doubleArrayList})"
else -> throw IllegalStateException("Unexpected format: ${proto.valueCase}")
}
/**
* 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 proto.longVal
}
/**
* 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 proto.boolVal
}
/**
* 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 proto.doubleVal
}
/**
* 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 proto.doubleArrayVal.doubleArrayList.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
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> = newCreator {
Value(DataProto.Value.parseFrom(it))
}
/** Creates a [Value] that represents a `long`. */
@JvmStatic
public fun ofLong(value: Long): Value =
Value(DataProto.Value.newBuilder().setLongVal(value).build())
/** Creates a [Value] that represents an `boolean`. */
@JvmStatic
public fun ofBoolean(value: Boolean): Value =
Value(DataProto.Value.newBuilder().setBoolVal(value).build())
/** Creates a [Value] that represents a `double`. */
@JvmStatic
public fun ofDouble(value: Double): Value =
Value(DataProto.Value.newBuilder().setDoubleVal(value).build())
/** Creates a [Value] that represents a `double[]`. */
@JvmStatic
public fun ofDoubleArray(vararg doubleArray: Double): Value =
Value(
DataProto.Value.newBuilder()
.setDoubleArrayVal(
DataProto.Value.DoubleArray.newBuilder()
.addAllDoubleArray(doubleArray.toList())
)
.build()
)
/**
* 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[]`
*/
@JvmStatic
public 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.asLong().compareTo(second.asLong())
FORMAT_BOOLEAN -> return first.asBoolean().compareTo(second.asBoolean())
FORMAT_DOUBLE -> return first.asDouble().compareTo(second.asDouble())
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 IllegalArgumentException 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"
}
return when (first.format) {
FORMAT_LONG -> ofLong(first.asLong() + second.asLong())
FORMAT_DOUBLE -> ofDouble(first.asDouble() + second.asDouble())
FORMAT_BOOLEAN ->
throw IllegalArgumentException(
"Attempted to add Values with invalid format (boolean)"
)
FORMAT_DOUBLE_ARRAY ->
throw IllegalArgumentException(
"Attempted to add Values with invalid format (double array)"
)
else -> throw IllegalArgumentException("Unexpected format: ${first.format}")
}
}
/**
* Subtracts two [Value] s based on their representation (i.e. returns `first` - `second`).
*
* @throws IllegalArgumentException if `first` and `second` do not share the same format or
* are represented as a `double[]` or `boolean`
*
* @hide
*/
@JvmStatic
public fun difference(first: Value, second: Value): Value {
require(first.format == second.format) {
"Attempted to subtract Values with different formats"
}
require(first.format == FORMAT_LONG || first.format == FORMAT_DOUBLE) {
"Provided values are not supported for difference"
}
return when (first.format) {
FORMAT_LONG -> ofLong(first.asLong() - second.asLong())
FORMAT_DOUBLE -> ofDouble(first.asDouble() - second.asDouble())
else -> throw IllegalArgumentException("Unexpected format: ${first.format}")
}
}
/**
* Gets the modulo of two [Value] s based on their representation. (i.e. returns `first` %
* `second`)
*
* @throws IllegalArgumentException if `first` and `second` do not share the same format or
* are represented as a `double[]` or `boolean`
*
* @hide
*/
@JvmStatic
public fun modulo(first: Value, second: Value): Value {
require(first.format == second.format) {
"Attempted to modulo Values with different formats"
}
require(first.format == FORMAT_LONG || first.format == FORMAT_DOUBLE) {
"Provided values are not supported for modulo"
}
return when (first.format) {
FORMAT_LONG -> ofLong(first.asLong() % second.asLong())
FORMAT_DOUBLE -> ofDouble(first.asDouble() % second.asDouble())
else -> throw IllegalArgumentException("Unexpected format: ${first.format}")
}
}
/**
* Checks if a given [Value] is zero or not.
*
* @throws IllegalArgumentException if `value` is a `double[]` or `bolean`
*
* @hide
*/
@JvmStatic
public fun isZero(value: Value): Boolean {
return when (value.format) {
FORMAT_LONG -> value.asLong() == 0L
FORMAT_DOUBLE -> value.asDouble() == 0.0
else -> throw IllegalArgumentException("Unexpected format: ${value.format}")
}
}
}
}