All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.woojiahao.style.utility.Measurement.kt Maven / Gradle / Ivy

package com.github.woojiahao.style.utility

import com.github.woojiahao.style.utility.Measurement.Type.*
import org.apache.commons.lang3.builder.EqualsBuilder
import org.apache.commons.lang3.builder.HashCodeBuilder

class Measurement(val value: T, val type: Type) {
  enum class Type(val measurement: String, val toPixel: Double = 1.0) {
    PIXELS("px"),
    INCHES("in", 96.0),
    CENTIMETERS("cm", (96.0 / 2.54)),
    MILLIMETERS("mm", (96.0 / 25.4));

    companion object {
      fun getOrNull(target: String) =
        values().firstOrNull { it.measurement.toLowerCase() == target.toLowerCase() }
    }
  }

  companion object {
    private val measurementRegex: Regex
      get() {
        val regexString = values().joinToString("|") { it.measurement }
        return Regex("^([\\d.]+)(|($regexString))\$")
      }

    fun toMeasurement(from: String): Measurement? {
      if (!measurementRegex.matches(from)) return null

      val parts = measurementRegex.matchEntire(from)?.groupValues ?: return null
      if (parts.isEmpty()) return null

      val value = parts[1]
      val unit = parts[2]

      return if (unit.isEmpty()) value.toDoubleOrNull()?.px
      else {
        val type = Type.getOrNull(unit) ?: return null
        return value.toDouble() match type
      }
    }
  }

  @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
  operator fun plus(other: Measurement): Measurement {
    require(type.measurement == other.type.measurement) { "Measurement types to add must be the same" }

    val num = when (value) {
      is Int -> {
        other.value as Int
        other.value + value
      }
      is Long -> {
        other.value as Long
        other.value + value
      }
      is Double -> {
        other.value as Double
        other.value + value
      }
      is Float -> {
        other.value as Float
        other.value + value
      }
      is Short -> {
        other.value as Short
        other.value + value
      }
      is Byte -> {
        other.value as Byte
        other.value + value
      }
      else -> throw IllegalStateException("Measurement type didn't line up for some reason")
    } as T

    return num match other.type
  }

  @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
  operator fun minus(other: Measurement): Measurement {
    require(type.measurement == other.type.measurement) { "Measurement types to add must be the same" }

    val num = when (value) {
      is Int -> {
        other.value as Int
        value - other.value
      }
      is Long -> {
        other.value as Long
        value - other.value
      }
      is Double -> {
        other.value as Double
        value - other.value
      }
      is Float -> {
        other.value as Float
        value - other.value
      }
      is Short -> {
        other.value as Short
        value - other.value
      }
      is Byte -> {
        other.value as Byte
        value - other.value
      }
      else -> throw IllegalStateException("Measurement type didn't line up for some reason")
    } as T

    return num match other.type
  }

  @Suppress("UNCHECKED_CAST")
  fun convert(to: Type = PIXELS): Measurement {
    val pixelMeasurement = type.toPixel * value.toDouble()
    val targetMeasurement = pixelMeasurement / to.toPixel
    return (targetMeasurement as T) match to
  }

  override fun toString() = "$value${type.measurement}"

  @Suppress("UNCHECKED_CAST")
  override fun equals(other: Any?): Boolean {
    if (other !is Measurement<*>) return false

    other as Measurement

    return EqualsBuilder()
      .append(value, other.value)
      .append(type.measurement, other.type.measurement)
      .isEquals
  }

  override fun hashCode() =
    HashCodeBuilder(17, 37)
      .append(value)
      .append(type.measurement)
      .toHashCode()
}

infix fun  T.match(type: Measurement.Type) =
  when (type) {
    PIXELS -> this.px
    INCHES -> this.`in`
    CENTIMETERS -> this.cm
    MILLIMETERS -> this.mm
  }

val  T.px
  get() = Measurement(this, PIXELS)

val  T.`in`
  get() = Measurement(this, INCHES)

val  T.cm
  get() = Measurement(this, CENTIMETERS)

val  T.mm
  get() = Measurement(this, MILLIMETERS)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy