March 06, 2006

MessageFormat or how not to read error messages

in an earlier post i was babbling on about how neat the com.ibm.icu.text.MessageFormat class was. i was also on about how you'd need a java wrapper class to really make use it. i thought that because whenever i tried something like:

<cfscript>
ozLocale="en_AU@calendar=gregorian";
thisPattern="On {0,date,short} at {0,time,short}, I left {1} for the {2}. I took {3,number,currency}";
thisLocale=createObject("java","com.ibm.icu.util.ULocale").init(ozLocale);
args=arrayNew(1);
args[1]=now();
args[2]="the office";
args[3]="microbrewery";
args[4]=javacast("int",100);
mf=createObject("java","com.ibm.icu.text.MessageFormat").
init(thisPattern,thisLocale);
thisMsg=mf.format(args);
</cfscript>

<cfdump var="#thisMSG#">


coldfusion would always throw an error at the thisMsg=mf.format(args) bit along the lines of: Error casting an object of type to an incompatible type. This usually indicates a programming error in Java, although it could also mean you have tried to use a foreign object in a different way than it was designed. which for some reason made me think it was because the format() method is overloaded and i couldn't figure out the right combination of argument classes to get it to work. my knee jerk reaction to this is to build a wrapper class and move on, which i promptly did.

i was puttering around with something this weekend (a method to count business days using icu4j's Holiday class) when i actually got the overloaded method error (while trying to add my birthday as a national holiday in the US virgin islands, en_VI). re-visiting the format() method errors it finally dawned on me that the error message was perfectly accurate and the real issue (besides me being a knee jerk reactionist and thick as a brick) was with the args array. coldfusion arrays aren't exactly java Arrays (if i recall correctly they're java.util.Vectors). back in the Triassic era, christian cantrell's blog had an entry concerning this problem where he pointed out a simple solution using the inherited toArray() method. so changing thisMsg=mf.format(args) to thisMsg=mf.format(args.toArray()) made that method work plenty fine. initial benchmarks show this java-based method to be considerably faster than our in-house one, not to mention saving all the locale formatting code we had to use prior to substituting the actual data. we'll be releasing updates to our resource bundle CFCs incorporating this new method sometime this week.

the sharp-eyed among you probably noticed the peculiar way i defined the locale en_AU@calendar=gregorian. icu4j locales (ULocales to be precise) have, besides the usual language, country, variant identifiers, keywords. keywords allow you to create a locale using a specific calendar, collation or currency (see the ICU user guide for details). in practice that means you can control the way MessageFormat formats your dates and currencies without having to mess around with them prior to submitting the data to the format() method. you can use any of the seven odd calendars that icu4j knows about, for instance en_AU@calendar=buddhist would produce dates formatted using the Buddhist calendar (BE), en_AU@calendar=islamic-civil would format dates using the civil version of the Islamic calendar, etc. very cool if you ask me. this is another area where icu4j kind of glances in the rear-view mirror as it blows by core java's i18n bits ;-)

0 Comments:

Post a Comment

<< Home