Я считаю, что класс DecimalFormat (и тег FormatNumber от Grails по расширению) немного непрозрачен для определенных случаев использования, и я до сих пор не нашел разумного способа сделать довольно простое форматирование с ним без некрасивой предварительной обработки для генерации соответствующая строка формата. Несколько месяцев назад я собрал простой тег форматирования чисел, который по существу создает строку формата и выполняет некоторую минимальную обработку самого числа.
Он не такой общий или элегантный, как мне бы хотелось (это все, что нам было нужно в то время - это супер базовый, но он все еще не позволяет выполнять некрасивую обработку GSP), но его должно быть легко читать, и это очевидно где его можно было бы тривиально улучшить (т.е. сделать масштабирование итеративным, а не наивным if-elseif slop, позволяя пользователю передавать пользовательские маркеры масштабирования, допуская специальный валидатор чисел в качестве параметра и т. д.).
// Formats a number to 3 significant digits, appending appropriate scale marker
// (k, m, b, t, etc.). Defining var allows you to use a string representation
// of the formatted number anywhere you need it within the tag body, and
// provides the scale as well (in case highlighting or other special formatting
// based upon scale is desired).
def formatNumberScaled = {attrs, body -> // number, prefix, suffix, invalid, var
Double number
String numberString
String scale
try {
number = attrs.'number'.toDouble()
} catch (Exception e) {
number = Double.NaN
}
if (number.isNaN() || number.isInfinite()) {
numberString = scale = attrs.'invalid' ?: "N/A"
} else {
Boolean negative = number < 0d
number = negative ? -number : number
if (number < 1000d) {
scale = ''
} else if (number < 1000000d) {
scale = 'k'
number /= 1000d
} else if (number < 1000000000d) {
scale = 'm'
number /= 1000000d
} else if (number < 1000000000000d) {
scale = 'b'
number /= 1000000000d
} else if (number < 1000000000000000d) {
scale = 't'
number /= 1000000000000d
}
String format
if (number < 10d) {
format = '#.00'
} else if (number < 100d) {
format = '##.0'
} else {
format = '###'
}
format = "'${attrs.'prefix' ?: ''}'${format}'${scale} ${attrs.'suffix' ?: ''}'"
numberString = g.formatNumber('number': negative ? -number : number, 'format': format)
}
// Now, either print the number or output the tag body with
// the appropriate variables set
if (attrs.'var') {
out << body((attrs.'var'): numberString, 'scale': scale)
} else {
out << numberString
}
}