0
votes

I have an enum class SettingsVisibility inside an database entity, which I am converting to a bitmask to store in the database. I am trying to reverse the conversion to bitmask, and get a list of enums as a return value. So if I have an enum with values ONE(1), TWO(2), FOUR(4), then it'll store as Enum(7). I want to take 7 and convert it to {ONE, TWO, FOUR}.

My code is below. I have the SettingsVisibility enum with integer values which are stored in the DB. When I try to retrieve from the database, Objectbox will use the given PropertyConvertor to marshall/unmarshall the data. When I want to convertToEntityProperty, it should return a list of just the saved enums, but at the moment it returns a list of all the enums. I can pass a databaseValue of 12 and it will return all enums instead of just 2 (LOCATION AND PAYMENTS).

I think the issue is the usage of enumClass.enumConstants because it gets all the values, but then the filter doesn't work on this, so I am stuck.

@Entity
data class Settings(
        @Id override var id: Long = 0,
        @Convert(converter = DocumentVisibilityConverter::class, dbType = Int::class)
        val showItems: List<SettingsVisibility>
) : Identifiable<Long> {
    lateinit var organisation: ToOne<Organisation>
 
    constructor() : this(
            showItems = emptyList(),
    )

    enum class SettingsVisibility(override val bit: Int) : Flags {
        USERS(1),
        FINANCE(2),
        LOCATION(4),
        PAYMENTS(8),
        MESSAGES(16),
        ERRORS(32),
        CANCELLATIONS(64)
    }

    internal class DocumentVisibilityConverter
        : BoxConverters.EnumFlagConverter<SettingsVisibility>(SettingsVisibility::class.java)
}

So for example, if I store the first 3, the database value will be 7 (1+2+4).

The database is ObjectBox and here are the property converters:

    abstract class EnumFlagConverter<E>(private val enumClass: Class<E>) : PropertyConverter<List<E>, Int> where E : Enum<E>, E : Flags {
        override fun convertToDatabaseValue(entityProperty: List<E>?): Int? {
            return entityProperty?.toBitMask()?.value
        }

        override fun convertToEntityProperty(databaseValue: Int?): List<E>? {
            return databaseValue?.let(::BitMask)?.enabledValues(enumClass)
        }
    }

class BitMask(val value: Int)

interface Flags {
    val bit: Int

    fun toBitMask() = BitMask(bit)


fun <T> BitMask.enabledValues(enumClass: Class<T>): List<T>? where T : Enum<T>, T : Flags? {
    return enumClass.enumConstants?.filter(::hasFlag)
}

infix fun <T : Flags?> BitMask.hasFlag(flag: T): Boolean {
    if (value == 0 || (value > 0 && flag?.bit == 0)) {
        return false
    }

    return true
}

Maybe the logic in hasFlag is wrong, because I think that just gets every enum if it isn't 0.

1

1 Answers

0
votes

Answer was to replace return true, with:

if (flag?.bit?.toByte() == null) {
    return false
}
return (this.value.toByte().and(flag.bit.toByte()) == flag.bit.toByte())

This is basically: bit & mask == bit