loggersoft.kotlin.utils.IntegerFormatter.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2018 Alexander Kornilov ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package loggersoft.kotlin.utils
import java.text.ParseException
/**
* Backend for formatting integers by string pattern.
*
* ### Format description: `+0(number)b|B|x|X`
* - `+` - forces to proceed the result with a plus or minus sign (+ or -) even for positive numbers.
* By default, only negative numbers are preceded with a minus sign.
* - `0` - left-pads the number with zeroes (0) instead of spaces when padding is specified.
* - `number` - minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces.
* The value is not truncated even if the result is larger.
* - `b|B` - binary representation.
* - `x` - hexadecimal representation.
* - `X` - hexadecimal representation (uppercase).
*
* ### Examples:
* - format="+05", number=3 => "+0003"
* - format="+5", number=3 => " +3"
* - format="x", number=255 => "ff"
* - format="04X", number=255 => "00FF"
*
* @param format pattern for formatting (see details above).
* @throws ParseException
*
* @author Alexander Kornilov ([email protected]).
*/
class IntegerFormatter(format: String) {
/**
* Indicates that sign should be displayed even for positive numbers.
*/
val sign: Boolean
/**
* Minimum number of characters to be printed.
*/
val pad: Int
/**
* Indicates that pad should be zeros.
*/
val padZero: Boolean
/**
* The radix of the number representation.
* Default value is 10.
*/
val radix: Int
/**
* Indicates that digits of the number which represented as a letter (e.g. in hex) should be in uppercase.
*/
val capital: Boolean
init {
val result = Regex("""^\s*(\+?)\s*(\d*)\s*([xXbB]?)\s*$""").matchEntire(format) ?: throw ParseException(format, -1)
sign = !result.groups[1]?.value.isNullOrEmpty()
val padGroup = result.groups[2]?.value
if (padGroup != null && padGroup.isNotEmpty()) { pad = padGroup.toInt(); padZero = padGroup[0] == '0' } else { pad = 0; padZero = false }
val radixGroup = result.groups[3]?.value
if (radixGroup != null && radixGroup.isNotEmpty()) {
when(radixGroup[0]) {
'b' -> { radix = 2; capital = false }
'B' -> { radix = 2; capital = true }
'x' -> { radix = 16; capital = false }
'X' -> { radix = 16; capital = true }
else -> { radix = 10; capital = false }
}
} else { radix = 10; capital = false }
}
/**
* Formats digits according settings: [sign], [pad], [padZero], [radix] and [capital].
* @param negative indicates that the source number is negative.
* @param initValue initial value to pass into [getDigit].
* @param [getDigit] returns pair with value to process and next digit or `null` if the end of number reached.
*
* @return string representation of number according to format.
*/
inline fun format(negative: Boolean, initValue: T, getDigit: (radix: Int, value: T) -> Pair?): String {
val digits = mutableListOf()
var value = initValue
var isFirst = true
while(true) {
val (nextValue, digit) = getDigit(radix, value) ?: break
require(digit in 0 until radix)
value = nextValue
if (digit == 0) { if (isFirst) continue } else isFirst = false
digits.add(0, digit)
}
val result = StringBuilder()
val isZero = digits.isEmpty()
val signPrefix = if (isZero) String.Empty else if (negative) "-" else if (sign) "+" else String.Empty
val digitsCount = signPrefix.length + if (isZero) 1 else digits.size
if (padZero) result.append(signPrefix)
if (digitsCount < pad) (pad - digitsCount).repeat { result.append(if (padZero) '0' else ' ') }
if (!padZero) result.append(signPrefix)
if (!isZero) for (digit in digits) {
result.append((digit + if (digit in 0..9) 48 else if (capital) 55 else 87).toChar())
} else result.append('0')
return result.toString()
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy