# formatNumber

## Purpose

Allows the formatting of numbers using the same patterns defined by the DecimalFormat class. Supports also attributes that are used in the JSTL formatNumber tag.

## Examples

    <g:formatNumber number="${myNumber}" format="\\$###,##0" />

Example of formatting an EUR currency amount:

    <g:formatNumber number="${myCurrencyAmount}" type="currency" currencyCode="EUR" />

Example of formatting a USD currency amount:

    <g:formatNumber number="${myCurrencyAmount}" type="currency" currencyCode="USD" />

For USD, the currency symbol will be based on the locale of the request. For the "en_US" locale, the result is "$123.45" and for "fi_FI" locale, the formatting is "123,45 USD". The formatting rules come from the java.util.Currency class.

Example of left padding a number with zeros:

    <g:formatNumber number="${myNumber}" type="number" minIntegerDigits="9" />

Example of formatting a number showing 2 fraction digits:

    <g:formatNumber number="${myNumber}" type="number" maxFractionDigits="2" />

Example of formatting a number showing 2 fraction digits, rounding with RoundingMode.HALF_DOWN:

    <g:formatNumber number="${myNumber}" type="number" maxFractionDigits="2" roundingMode="HALF_DOWN" />

## Description

Attributes

- `number` (required) - The number object to formate
- `format` (optional) - The formatting pattern to use for the number, see DecimalFormat
- `formatName` (optional) - Look up `format` from the default MessageSource / ResourceBundle (i18n/.properties file) with this key.Look up `format` from the default MessageSource / ResourceBundle (i18n/.properties file) with this key. If `format` and `formatName` are empty, `format` is looked up with '`default.number.format`' key. If the key is missing, '0' formatting pattern is used.
- `type` (optional) - The type of formatter to use: 'number', 'currency' or 'percent' . `format` or `formatName` aren't used when `type` is specified.
- `locale` (optional) - Override the locale of the request , String or java.util.Locale value
- `groupingUsed` (optional) - Set whether or not grouping will be used in this format.
- `minIntegerDigits` (optional) - Sets the minimum number of digits allowed in the integer portion of a number.
- `maxIntegerDigits` (optional) - Sets the maximum number of digits allowed in the integer portion of a number.
- `minFractionDigits` (optional) - Sets the minimum number of digits allowed in the fraction portion of a number.
- `maxFractionDigits` (optional) - Sets the maximum number of digits allowed in the fraction portion of a number.
- `currencyCode` (optional) - The standard currency code ('EUR', 'USD', etc.), uses formatting settings for the currency. type='currency' attribute is recommended.
- `currencySymbol` (optional) - Force the currency symbol to some symbol, recommended way is to use `currencyCode` attribute instead (takes symbol information from java.util.Currency)
- `roundingMode` (optional) - Sets the RoundingMode used in this DecimalFormat. Usual values: HALF_UP, HALF_DOWN. If roundingMode is UNNECESSARY and ArithemeticException raises, the original number formatted with default number formatting will be returned.

## Source

Show Source

    def formatNumber = { attrs ->
            if (!attrs.containsKey('number')) {
                throwTagError("Tag [formatNumber] is missing required attribute [number]")
            }        def number = attrs.number
            if (number == null) return        def formatName = attrs.formatName
            def format = attrs.format
            def type = attrs.type
            def locale = resolveLocale(attrs.locale)        if (type == null) {
                if (!format && formatName) {
                    format = messageHelper(formatName,null,null,locale)
                    if (!format) {
                        throwTagError("Attribute [formatName] of Tag [formatNumber] specifies a format key [$formatName] that does not exist within a message bundle!")
                    }
                }
                else if (!format) {
                    format = messageHelper( "number.format", { messageHelper( "default.number.format", "0", null, locale) } ,null ,locale)
                }
            }        DecimalFormatSymbols dcfs = locale ? new DecimalFormatSymbols( locale ) : new DecimalFormatSymbols()        DecimalFormat decimalFormat
            if (!type) {
                decimalFormat = new DecimalFormat(format, dcfs)
            }
            else {
                if (type == 'currency') {
                    decimalFormat = NumberFormat.getCurrencyInstance(locale)
                }
                else if (type == 'number') {
                    decimalFormat = NumberFormat.getNumberInstance(locale)
                }
                else if (type == 'percent') {
                    decimalFormat = NumberFormat.getPercentInstance(locale)
                }
                else {
                    throwTagError("Attribute [type] of Tag [formatNumber] specifies an unknown type. Known types are currency, number and percent.")
                }
            }        // ensure formatting accuracy
            decimalFormat.setParseBigDecimal(true)        if (attrs.currencyCode != null) {
                Currency currency = Currency.getInstance(attrs.currencyCode as String)
                decimalFormat.setCurrency(currency)
            }
            if (attrs.currencySymbol != null) {
                dcfs = decimalFormat.getDecimalFormatSymbols()
                dcfs.setCurrencySymbol(attrs.currencySymbol as String)
                decimalFormat.setDecimalFormatSymbols(dcfs)
            }
            if (attrs.groupingUsed != null) {
                if (attrs.groupingUsed instanceof Boolean) {
                    decimalFormat.setGroupingUsed(attrs.groupingUsed)
                }
                else {
                    // accept true, y, 1, yes
                    decimalFormat.setGroupingUsed(attrs.groupingUsed.toString().toBoolean() ||
                        attrs.groupingUsed.toString() == 'yes')
                }
            }
            if (attrs.maxIntegerDigits != null) {
                decimalFormat.setMaximumIntegerDigits(attrs.maxIntegerDigits as Integer)
            }
            if (attrs.minIntegerDigits != null) {
                decimalFormat.setMinimumIntegerDigits(attrs.minIntegerDigits as Integer)
            }
            if (attrs.maxFractionDigits != null) {
                decimalFormat.setMaximumFractionDigits(attrs.maxFractionDigits as Integer)
            }
            if (attrs.minFractionDigits != null) {
                decimalFormat.setMinimumFractionDigits(attrs.minFractionDigits as Integer)
            }
            if (attrs.roundingMode != null) {
                def roundingMode = attrs.roundingMode
                if (!(roundingMode instanceof RoundingMode)) {
                    roundingMode = RoundingMode.valueOf(roundingMode)
                }
                decimalFormat.setRoundingMode(roundingMode)
            }        if (!(number instanceof Number)) {
                number = decimalFormat.parse(number as String)
            }        def formatted
            try {
                formatted = decimalFormat.format(number)
            }
            catch(ArithmeticException e) {
                // if roundingMode is UNNECESSARY and ArithemeticException raises, just return original number formatted with default number formatting
                formatted = NumberFormat.getNumberInstance(locale).format(number)
            }
            return formatted
        }    def resolveLocale(localeAttr) {
            def locale = localeAttr
            if (locale != null && !(locale instanceof Locale)) {
                locale = StringUtils.parseLocaleString(locale as String)
            }
            if (locale == null) {
                locale = RCU.getLocale(request)
                if (locale == null) {
                    locale = Locale.getDefault()
                }
            }
            return locale
        }