# 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.get('number')
        	if (number == null) return null        def formatName = attrs.get('formatName')
            def format = attrs.get('format')
            def type = attrs.get('type')
            def locale = resolveLocale(attrs.get('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 java.text.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.get('currencyCode') != null) {
            	Currency currency=Currency.getInstance(attrs.get('currencyCode') as String)
            	decimalFormat.setCurrency(currency)
            }
            if(attrs.get('currencySymbol') != null) {
            	dcfs = decimalFormat.getDecimalFormatSymbols()
            	dcfs.setCurrencySymbol(attrs.get('currencySymbol') as String)
            	decimalFormat.setDecimalFormatSymbols(dcfs)
            }
            if(attrs.get('groupingUsed') != null) {
    			if(attrs.get('groupingUsed') instanceof Boolean) {
    				decimalFormat.setGroupingUsed(attrs.get('groupingUsed'))
    			} else {
    				// accept true, y, 1, yes
    				decimalFormat.setGroupingUsed(attrs.get('groupingUsed').toString().toBoolean() || attrs.get('groupingUsed').toString()=='yes')
    			}
            }
            if(attrs.get('maxIntegerDigits') != null) {
            	decimalFormat.setMaximumIntegerDigits(attrs.get('maxIntegerDigits') as Integer)
            }
            if(attrs.get('minIntegerDigits') != null) {
            	decimalFormat.setMinimumIntegerDigits(attrs.get('minIntegerDigits') as Integer)
            }
            if(attrs.get('maxFractionDigits') != null) {
            	decimalFormat.setMaximumFractionDigits(attrs.get('maxFractionDigits') as Integer)
            }
            if(attrs.get('minFractionDigits') != null) {
            	decimalFormat.setMinimumFractionDigits(attrs.get('minFractionDigits') as Integer)
            }
            if(attrs.get('roundingMode') != null) {
            	def roundingMode=attrs.get('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
        }