161 lines
3.7 KiB
Kotlin
161 lines
3.7 KiB
Kotlin
package jp.juggler.subwaytooter.util
|
||
|
||
import jp.juggler.util.groupEx
|
||
import java.math.BigInteger
|
||
import java.util.ArrayList
|
||
import java.util.regex.Pattern
|
||
|
||
class VersionString(src : String?) : Comparable<VersionString> {
|
||
|
||
private val src : String
|
||
|
||
private val node_list = ArrayList<Any>()
|
||
|
||
val isEmpty : Boolean
|
||
get() = node_list.isEmpty()
|
||
|
||
override fun toString() : String {
|
||
return src
|
||
}
|
||
|
||
private class RC(val x : Int) : Comparable<RC> {
|
||
override fun compareTo(other : RC) : Int {
|
||
val i = x - other.x
|
||
return if(i > 0) 1 else if(i < 0) - 1 else 0
|
||
}
|
||
|
||
override fun toString() : String {
|
||
return "RC($x)"
|
||
}
|
||
}
|
||
|
||
override fun compareTo(other : VersionString) : Int {
|
||
return compare(this, other)
|
||
}
|
||
|
||
init {
|
||
this.src = src ?: ""
|
||
if(src?.isNotEmpty() == true) {
|
||
val end = src.length
|
||
var next = 0
|
||
while(next < end) {
|
||
var c = src[next]
|
||
|
||
when {
|
||
isDelimiter(c) -> {
|
||
// 先頭の区切り文字を無視する
|
||
++ next
|
||
}
|
||
|
||
Character.isDigit(c) -> {
|
||
// 数字列のノード
|
||
val start = next ++
|
||
while(next < end && Character.isDigit(src[next])) ++ next
|
||
val value = BigInteger(src.substring(start, next))
|
||
node_list.add(value)
|
||
}
|
||
|
||
else -> {
|
||
val m = reRcX.matcher(src)
|
||
|
||
if(DUMP) {
|
||
if(m.find(next)) {
|
||
println("next=$next, matct_start=${m.start()}")
|
||
} else {
|
||
println("next=$next, not match.")
|
||
}
|
||
}
|
||
|
||
if(m.find(next) && m.start() == next) {
|
||
// RCノード
|
||
next = m.end()
|
||
val numStr = m.groupEx(1)
|
||
val num = if(numStr?.isNotEmpty() == true) {
|
||
numStr.toInt()
|
||
} else {
|
||
0
|
||
}
|
||
node_list.add(RC(num))
|
||
} else {
|
||
// 区切り文字と数字以外の文字が並ぶノード
|
||
val start = next ++
|
||
while(next < end) {
|
||
c = src[next]
|
||
if(isDelimiter(c)) break
|
||
if(Character.isDigit(c)) break
|
||
++ next
|
||
}
|
||
val value = src.substring(start, next)
|
||
node_list.add(value)
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
companion object : Comparator<VersionString> {
|
||
|
||
private const val DUMP = false
|
||
|
||
private fun isDelimiter(c : Char) : Boolean {
|
||
return c == '.' || c == ' '
|
||
}
|
||
|
||
private val reRcX = Pattern.compile("rc(\\d*)")
|
||
|
||
private fun checkTail(b : Any) : Int {
|
||
// 1.0 < 1.0.n => -1
|
||
// 1.0 < 1.0xxx => -1
|
||
// 1.0 > 1.0rc1 => 1
|
||
return if(b is RC) 1 else - 1
|
||
}
|
||
|
||
private fun checkBigInteger(a : BigInteger, b : Any) : Int {
|
||
if(b is BigInteger) return a.compareTo(b)
|
||
// 数字 > 数字以外
|
||
// 1.5.n > 1.5xxx
|
||
// 1.5.n > 1.5rc1
|
||
return 1
|
||
}
|
||
|
||
private fun checkRc(a : RC, b : Any) : Int {
|
||
if(b is RC) return a.compareTo(b)
|
||
// RC < string
|
||
// 1.5rc < 1.5xxx
|
||
return - 1
|
||
}
|
||
|
||
// return -1 if a<b , return 1 if a>b , return 0 if a==b
|
||
override fun compare(a : VersionString, b : VersionString) : Int {
|
||
var idx = 0
|
||
loop@ while(true) {
|
||
val ao = if(idx >= a.node_list.size) null else a.node_list[idx]
|
||
val bo = if(idx >= b.node_list.size) null else b.node_list[idx]
|
||
if(DUMP) println("a=$ao,b=$bo")
|
||
|
||
val i = when {
|
||
ao == null -> {
|
||
if(bo == null) return 0
|
||
checkTail(bo)
|
||
}
|
||
|
||
bo == null -> - checkTail(ao)
|
||
ao is BigInteger -> checkBigInteger(ao, bo)
|
||
bo is BigInteger -> - checkBigInteger(bo, ao)
|
||
ao is RC -> checkRc(ao, bo)
|
||
bo is RC -> - checkRc(bo, ao)
|
||
else -> a.toString().compareTo(b.toString())
|
||
}
|
||
if(i == 0) {
|
||
++ idx
|
||
continue@loop
|
||
}
|
||
return i
|
||
}
|
||
}
|
||
}
|
||
}
|