aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/firmament/features/texturepack/NumberMatcher.kt
blob: 7e6665f9b946e7a2145a8f92af558d066f129fef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package moe.nea.firmament.features.texturepack

import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import moe.nea.firmament.util.useMatch

abstract class NumberMatcher {
    abstract fun test(number: Number): Boolean


    companion object {
        fun parse(jsonElement: JsonElement): NumberMatcher? {
            if (jsonElement is JsonPrimitive) {
                if (jsonElement.isString) {
                    val string = jsonElement.asString
                    return parseRange(string) ?: parseOperator(string)
                }
                if (jsonElement.isNumber) {
                    val number = jsonElement.asNumber
                    val hasDecimals = (number.toString().contains("."))
                    return MatchNumberExact(if (hasDecimals) number.toLong() else number.toDouble())
                }
            }
            return null
        }

        private val intervalSpec =
            "(?<beginningOpen>[\\[\\(])(?<beginning>[0-9.]+)?,(?<ending>[0-9.]+)?(?<endingOpen>[\\]\\)])"
                .toPattern()

        fun parseRange(string: String): RangeMatcher? {
            intervalSpec.useMatch<Nothing>(string) {
                // Open in the set-theory sense, meaning does not include its end.
                val beginningOpen = group("beginningOpen") == "("
                val endingOpen = group("endingOpen") == ")"
                val beginning = group("beginning")?.toDouble()
                val ending = group("ending")?.toDouble()
                return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen)
            }
            return null
        }

        enum class Operator(val operator: String) {
            LESS("<") {
                override fun matches(comparisonResult: Int): Boolean {
                    return comparisonResult < 0
                }
            },
            LESS_EQUALS("<=") {
                override fun matches(comparisonResult: Int): Boolean {
                    return comparisonResult <= 0
                }
            },
            GREATER(">") {
                override fun matches(comparisonResult: Int): Boolean {
                    return comparisonResult > 0
                }
            },
            GREATER_EQUALS(">=") {
                override fun matches(comparisonResult: Int): Boolean {
                    return comparisonResult >= 0
                }
            },
            ;

            abstract fun matches(comparisonResult: Int): Boolean
        }

        private val operatorPattern = "(?<operator>${Operator.entries.joinToString("|") {it.operator}})(?<value>[0-9.]+)".toPattern()

        fun parseOperator(string: String): OperatorMatcher? {
            operatorPattern.useMatch<Nothing>(string) {
                val operatorName = group("operator")
                val operator = Operator.entries.find { it.operator == operatorName }!!
                val value = group("value").toDouble()
                return OperatorMatcher(operator, value)
            }
            return null
        }

        data class OperatorMatcher(val operator: Operator, val value: Double) : NumberMatcher() {
            override fun test(number: Number): Boolean {
                return operator.matches(number.toDouble().compareTo(value))
            }
        }


        data class MatchNumberExact(val number: Number) : NumberMatcher() {
            override fun test(number: Number): Boolean {
                return when (this.number) {
                    is Double -> number.toDouble() == this.number.toDouble()
                    else -> number.toLong() == this.number.toLong()
                }
            }
        }

        data class RangeMatcher(
            val beginning: Double?,
            val beginningInclusive: Boolean,
            val ending: Double?,
            val endingInclusive: Boolean,
        ) : NumberMatcher() {
            override fun test(number: Number): Boolean {
                val value = number.toDouble()
                if (beginning != null) {
                    if (beginningInclusive) {
                        if (value < beginning) return false
                    } else {
                        if (value <= beginning) return false
                    }
                }
                if (ending != null) {
                    if (endingInclusive) {
                        if (value > ending) return false
                    } else {
                        if (value >= ending) return false
                    }
                }
                return true
            }
        }
    }

}