This may not be as straightforward as it sounds - to define a format for a number attribute in ADF BC. Especially if you are going to have large number (more than 15 digits). Most likely you are going to experience precision/scale and rounding issues, for BigDecimal and Number type attributes with format mask applied. Sounds frustrating? Yes it is. I hope my blog post will help you to implement proper number formatting.
Firstly I'm going to demonstrate, how it works by default. I'm going to use format mask for 18 digits and 2 decimals. Format mask for a number attribute of BigDecimal type would fail with invalid precision/scale error - this is surprising, especially knowing that attribute is defined with (20, 2) precision and scale:
There is one more issue to handle - automatic rounding for large numbers (more than 15 digits). In this example I'm trying to enter a number with 18 digits:
Suddenly number is rounded, immediately after focus is moving out of the field - this is bad, user can't enter correct large number:
Let's see, how we can fix it. I will explain solution, based on Salary attribute with (20, 2) precision and scale. By default, this attribute is generated with BigDecimal type, I'm going to keep the same type, no need to change to oracle.jbo.domain.Number:
I have set format mask on the VO level, for the same Salary attribute. There is another issue - maximum length of the attribute value user can enter on UI. Maximum length property is reading precision and calculates maximum number of characters, however this is wrong when format mask is applied:
There will be extra comma and dot signs coming from the format, so I would advice to set Display Width to be equal to the attribute precision plus maximum number of commas and dots (26 in my case). Later we would need to change maximumLength property on the UI to use Display Width instead of precision:
Once you set a format mask, formatter class name is assigned for the attribute. Formatter class name is saved in resource bundle, for each formatted attribute separately:
You must change formatter class name to the custom one, don't use DefaultNumberFormatter class - out of the box formatter doesn't work with BigDecimals. Sample application comes with custom Number formatter class. Format method is overridden to support BigDecimal, standard formatter method breaks BigDecimal number by converting it to Double (this is the reason for rounding error) - this is fixed in the sample:
Method to parse is changed as well (this is fixing precision/scale number for the BigDecimal). Instead of calling parse method from super, I'm calling custom method:
I'm setting a property to enable BigDecimal parsing, this allows to work correctly with precision/scale:
In addition, precision and scale are checked by custom method - if there is invalid number of digits, user will be informed:
I was mentioning it earlier - maximumLength property on the UI is updated to point to Display Width set on the VO attribute UI Hints:
We can do a test now, type long number with 18 digits (remember (20, 2) precision/scale for Salary attribute):
Correct format will be applied:
Type decimal part and now there will be no error about precision/scale - it will be successfully accepted:
You can see from the log how format is being applied, it works well with custom formatter class provided in the sample application - ADFFormattingApp.zip:
Firstly I'm going to demonstrate, how it works by default. I'm going to use format mask for 18 digits and 2 decimals. Format mask for a number attribute of BigDecimal type would fail with invalid precision/scale error - this is surprising, especially knowing that attribute is defined with (20, 2) precision and scale:
There is one more issue to handle - automatic rounding for large numbers (more than 15 digits). In this example I'm trying to enter a number with 18 digits:
Suddenly number is rounded, immediately after focus is moving out of the field - this is bad, user can't enter correct large number:
Let's see, how we can fix it. I will explain solution, based on Salary attribute with (20, 2) precision and scale. By default, this attribute is generated with BigDecimal type, I'm going to keep the same type, no need to change to oracle.jbo.domain.Number:
I have set format mask on the VO level, for the same Salary attribute. There is another issue - maximum length of the attribute value user can enter on UI. Maximum length property is reading precision and calculates maximum number of characters, however this is wrong when format mask is applied:
There will be extra comma and dot signs coming from the format, so I would advice to set Display Width to be equal to the attribute precision plus maximum number of commas and dots (26 in my case). Later we would need to change maximumLength property on the UI to use Display Width instead of precision:
Once you set a format mask, formatter class name is assigned for the attribute. Formatter class name is saved in resource bundle, for each formatted attribute separately:
You must change formatter class name to the custom one, don't use DefaultNumberFormatter class - out of the box formatter doesn't work with BigDecimals. Sample application comes with custom Number formatter class. Format method is overridden to support BigDecimal, standard formatter method breaks BigDecimal number by converting it to Double (this is the reason for rounding error) - this is fixed in the sample:
Method to parse is changed as well (this is fixing precision/scale number for the BigDecimal). Instead of calling parse method from super, I'm calling custom method:
I'm setting a property to enable BigDecimal parsing, this allows to work correctly with precision/scale:
In addition, precision and scale are checked by custom method - if there is invalid number of digits, user will be informed:
I was mentioning it earlier - maximumLength property on the UI is updated to point to Display Width set on the VO attribute UI Hints:
We can do a test now, type long number with 18 digits (remember (20, 2) precision/scale for Salary attribute):
Correct format will be applied:
Type decimal part and now there will be no error about precision/scale - it will be successfully accepted:
You can see from the log how format is being applied, it works well with custom formatter class provided in the sample application - ADFFormattingApp.zip:
Great post, thanks
ReplyDeleteHowever, The class NumberFormatter gives one error in method getDecimalFormat
The error is:
getDecimalFormat() in com.redsamurai.model.generic.format.NumberFormatter cannot override getDecimalFormat() in oracle.jbo.format.DefaultNumberFormatter; attempting to assign weaker access privileges; was protected"
I'm using jdev 12.1.3
I was testing it with 11g R2 only. In 12c you could use out of the box DefaultBigDecimal formatter, you you could remove "extends" from DefaultNumberFormatter - this should also work.
ReplyDeleteAndrejus
I have tested it now in 12c, you should change private to public for: public DecimalFormat getDecimalFormat(), it will work.
ReplyDeleteRegards,
Andrejus
thanks Andrejus,
ReplyDeleteNow works
Regards,
Bernat
Thanks
ReplyDeleteHi Andrejus,
ReplyDeleteI have a column which is Bigdecimal of (28,8) and I have used NumberFormatter class and I have number format "\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#.\#\#\#\#\#\#\#\#" and whenever I enter number above 20 it is throwing error message "has invalid precision/scale". So could you please guide me how to overcome with this.
Regards,
Priya
Hi Andrejus
ReplyDeleteI used your formatter class for the big decimal issue and it works fine.
When I enter 18 digits in a number(18,2) precision and scale i have no issues.
I have used a length of 26 as you mentioned, and changed the maximum length on the UI to point at the width.
But when I type in 26 digits in the text area as it allows you to enter that amount of characters, the format fails. Please try replicate this on your example, I'm using 12c.
regards Sihle
I think this is correct behavior.
ReplyDeleteRegards,
Andrejus