I found that the DecimalFormat class (and the Grails formatNumber format extension tag) is a bit opaque for certain use cases, and I still haven't found a reasonable way to make it fairly simple formatting without some ugly preprocessing to generate the appropriate format string. A few months ago, I put together a simple number formatting tag that essentially creates a format string and does some minimal processing for the number itself.
It's not as generic or elegant as I would like (all we need at the time is super basic, but it still holds some ugly processing from GSP), but it should be easy to read, and this is obvious where you can it would be trivial to improve (i.e. make scaling iterative rather than naive if-elseif slop, allowing the user to pass custom scaling tokens, allowing you to use your own number validator as a parameter, etc.).
// 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 } }
Chris king
source share