<commit_before>/*
 * Copyright (c) 2010-2019 Contributors to the openHAB project
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.openhab.habdroid.model

import android.graphics.Color
import android.location.Location
import android.os.Parcelable

import kotlinx.android.parcel.Parcelize

import java.util.IllegalFormatException
import java.util.Locale
import java.util.regex.Pattern
import kotlin.math.roundToInt

@Parcelize
data class HsvState internal constructor(val hue: Float, val saturation: Float, val value: Float) : Parcelable {
    fun toColor(): Int {
        return Color.HSVToColor(floatArrayOf(hue, saturation, value))
    }
}

@Parcelize
data class ParsedState internal constructor(
    val asString: String,
    val asBoolean: Boolean,
    val asNumber: NumberState?,
    val asHsv: HsvState?,
    val asBrightness: Int?,
    val asLocation: Location?
) : Parcelable {
    companion object {
        internal fun parseAsBoolean(state: String): Boolean {
            // If state is ON for switches return True
            if (state == "ON") {
                return true
            }

            val brightness = parseAsBrightness(state)
            if (brightness != null) {
                return brightness != 0
            }
            return try {
                val decimalValue = Integer.valueOf(state)
                decimalValue > 0
            } catch (e: NumberFormatException) {
                false
            }
        }

        internal fun parseAsNumber(state: String, format: String?): NumberState? {
            return when (state) {
                "ON" -> NumberState(100F)
                "OFF" -> NumberState(0F)
                else -> {
                    val spacePos = state.indexOf(' ')
                    val number = if (spacePos >= 0) state.substring(0, spacePos) else state
                    val unit = if (spacePos >= 0) state.substring(spacePos + 1) else null
                    return try {
                        NumberState(number.toFloat(), unit, format)
                    } catch (e: NumberFormatException) {
                        null
                    }
                }
            }
        }

        internal fun parseAsHsv(state: String): HsvState? {
            val stateSplit = state.split(",")
            if (stateSplit.size == 3) { // We need exactly 3 numbers to operate this
                try {
                    return HsvState(stateSplit[0].toFloat(),
                        stateSplit[1].toFloat() / 100,
                        stateSplit[2].toFloat() / 100)
                } catch (e: NumberFormatException) {
                    // fall through
                }
            }
            return null
        }

        internal fun parseAsLocation(state: String): Location? {
            val splitState = state.split(",")
            // Valid states are either "latitude,longitude" or "latitude,longitude,elevation",
            if (splitState.size == 2 || splitState.size == 3) {
                try {
                    val l = Location("openhab")
                    l.latitude = splitState[0].toDouble()
                    l.longitude = splitState[1].toDouble()
                    l.time = System.currentTimeMillis()
                    if (splitState.size == 3) {
                        l.altitude = splitState[2].toDouble()
                    }
                    // Do our best to avoid parsing e.g. HSV values into location by
                    // sanity checking the values
                    if (Math.abs(l.latitude) <= 90 && Math.abs(l.longitude) <= 180) {
                        return l
                    }
                } catch (e: NumberFormatException) {
                    // ignored
                }
            }
            return null
        }

        internal fun parseAsBrightness(state: String): Int? {
            val hsbMatcher = HSB_PATTERN.matcher(state)
            if (hsbMatcher.find()) {
                try {
                    return hsbMatcher.group(3).toInt()
                } catch (e: NumberFormatException) {
                    // fall through
                }
            }
            return null
        }

        private val HSB_PATTERN = Pattern.compile("^([0-9]*\\.?[0-9]+),([0-9]*\\.?[0-9]+),([0-9]*\\.?[0-9]+)$")
    }

    @Parcelize
    class NumberState internal constructor(
        val value: Float,
        val unit: String? = null,
        val format: String? = null
    ) : Parcelable {
        override fun toString(): String {
            return toString(Locale.getDefault())
        }

        /**
         * Like [toString][.toString], but using a specific locale for formatting.
         */
        fun toString(locale: Locale): String {
            if (!format.isNullOrEmpty()) {
                val actualFormat = format.replace("%unit%", unit.orEmpty())
                val actualValue: Number = if (actualFormat.contains("%d")) value.roundToInt() else value
                try {
                    return String.format(locale, actualFormat, actualValue)
                } catch (e: IllegalFormatException) {
                    // State format pattern doesn't match the actual data type
                    // -> ignore and fall back to our own formatting
                }
            }
            return if (unit == null) formatValue() else "${formatValue()} $unit"
        }

        fun formatValue(): String {
            return value.toString()
        }
    }
}

fun ParsedState.NumberState?.withValue(value: Float): ParsedState.NumberState {
    return ParsedState.NumberState(value, this?.unit, this?.format)<commit_msg>Use format specifier also when updating non-UoM items. (#1469)<commit_after>/*
 * Copyright (c) 2010-2019 Contributors to the openHAB project
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.openhab.habdroid.model

import android.graphics.Color
import android.location.Location
import android.os.Parcelable

import kotlinx.android.parcel.Parcelize

import java.util.IllegalFormatException
import java.util.Locale
import java.util.regex.Pattern
import kotlin.math.roundToInt

@Parcelize
data class HsvState internal constructor(val hue: Float, val saturation: Float, val value: Float) : Parcelable {
    fun toColor(): Int {
        return Color.HSVToColor(floatArrayOf(hue, saturation, value))
    }
}

@Parcelize
data class ParsedState internal constructor(
    val asString: String,
    val asBoolean: Boolean,
    val asNumber: NumberState?,
    val asHsv: HsvState?,
    val asBrightness: Int?,
    val asLocation: Location?
) : Parcelable {
    companion object {
        internal fun parseAsBoolean(state: String): Boolean {
            // If state is ON for switches return True
            if (state == "ON") {
                return true
            }

            val brightness = parseAsBrightness(state)
            if (brightness != null) {
                return brightness != 0
            }
            return try {
                val decimalValue = Integer.valueOf(state)
                decimalValue > 0
            } catch (e: NumberFormatException) {
                false
            }
        }

        internal fun parseAsNumber(state: String, format: String?): NumberState? {
            return when (state) {
                "ON" -> NumberState(100F)
                "OFF" -> NumberState(0F)
                else -> {
                    val spacePos = state.indexOf(' ')
                    val number = if (spacePos >= 0) state.substring(0, spacePos) else state
                    val unit = if (spacePos >= 0) state.substring(spacePos + 1) else null
                    return try {
                        NumberState(number.toFloat(), unit, format)
                    } catch (e: NumberFormatException) {
                        null
                    }
                }
            }
        }

        internal fun parseAsHsv(state: String): HsvState? {
            val stateSplit = state.split(",")
            if (stateSplit.size == 3) { // We need exactly 3 numbers to operate this
                try {
                    return HsvState(stateSplit[0].toFloat(),
                        stateSplit[1].toFloat() / 100,
                        stateSplit[2].toFloat() / 100)
                } catch (e: NumberFormatException) {
                    // fall through
                }
            }
            return null
        }

        internal fun parseAsLocation(state: String): Location? {
            val splitState = state.split(",")
            // Valid states are either "latitude,longitude" or "latitude,longitude,elevation",
            if (splitState.size == 2 || splitState.size == 3) {
                try {
                    val l = Location("openhab")
                    l.latitude = splitState[0].toDouble()
                    l.longitude = splitState[1].toDouble()
                    l.time = System.currentTimeMillis()
                    if (splitState.size == 3) {
                        l.altitude = splitState[2].toDouble()
                    }
                    // Do our best to avoid parsing e.g. HSV values into location by
                    // sanity checking the values
                    if (Math.abs(l.latitude) <= 90 && Math.abs(l.longitude) <= 180) {
                        return l
                    }
                } catch (e: NumberFormatException) {
                    // ignored
                }
            }
            return null
        }

        internal fun parseAsBrightness(state: String): Int? {
            val hsbMatcher = HSB_PATTERN.matcher(state)
            if (hsbMatcher.find()) {
                try {
                    return hsbMatcher.group(3).toInt()
                } catch (e: NumberFormatException) {
                    // fall through
                }
            }
            return null
        }

        private val HSB_PATTERN = Pattern.compile("^([0-9]*\\.?[0-9]+),([0-9]*\\.?[0-9]+),([0-9]*\\.?[0-9]+)$")
    }

    @Parcelize
    class NumberState internal constructor(
        val value: Float,
        val unit: String? = null,
        val format: String? = null
    ) : Parcelable {
        override fun toString(): String {
            return toString(Locale.getDefault())
        }

        /**
         * Like [toString][.toString], but using a specific locale for formatting.
         */
        fun toString(locale: Locale): String {
            if (!format.isNullOrEmpty()) {
                val actualFormat = format.replace("%unit%", unit.orEmpty())
                try {
                    return String.format(locale, actualFormat, actualValue)
                } catch (e: IllegalFormatException) {
                    // State format pattern doesn't match the actual data type
                    // -> ignore and fall back to our own formatting
                }
            }
            return if (unit == null) formatValue() else "${formatValue()} $unit"
        }

        fun formatValue(): String {
            return actualValue.toString()
        }

        private var actualValue: Number =
            if (format != null && format.contains("%d")) value.roundToInt() else value
    }
}

fun ParsedState.NumberState?.withValue(value: Float): ParsedState.NumberState {
    return ParsedState.NumberState(value, this?.unit, this?.format)
{'fraction_non_alphanumeric': 0.10062310542270124, 'fraction_numerical': 0.010272819131020546, 'mean_word_length': 1.5657809462086845, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4043103', 'n_tokens_mistral': 3190, 'n_tokens_neox': 2978, 'n_words': 834}