001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: LocaleManager.java,v $
023   Revision 1.18  2004/05/12 18:03:42  markl
024   javadoc updates
025
026   Revision 1.17  2004/05/05 21:22:45  markl
027   Comment header updates.
028
029   Revision 1.16  2004/03/16 06:37:57  markl
030   renamed getDefaultLocaleManager() to getDefault()
031
032   Revision 1.15  2003/02/06 07:44:42  markl
033   Don't change default Locale if we don't need to.
034
035   Revision 1.14  2003/01/19 09:42:20  markl
036   Added getShortDateFormat() method.
037
038   Revision 1.13  2002/08/11 09:49:09  markl
039   Javadoc correction.
040
041   Revision 1.12  2001/08/28 20:28:21  markl
042   Fixes to defer access to Toolkit for situations where no X Display is
043   available.
044
045   Revision 1.11  2001/06/26 06:11:01  markl
046   Made constructor public.
047
048   Revision 1.10  2001/03/18 06:37:26  markl
049   No longer a singleton, added getLocale() method.
050
051   Revision 1.9  2001/03/12 02:57:41  markl
052   Source code cleanup.
053
054   Revision 1.8  2000/06/09 01:47:54  markl
055   Added Collator instance and getCollator() method.
056
057   Revision 1.7  2000/03/17 10:41:30  markl
058   Simple fix to correct timezone in date formats.
059
060   Revision 1.6  1999/07/29 06:45:38  markl
061   Fixed a NPE problem.
062
063   Revision 1.5  1999/07/25 13:40:07  markl
064   Added getDateFormatSymbols() method.
065
066   Revision 1.4  1999/06/28 08:19:31  markl
067   Added many new methods and more sophisticated parsing/formatting logic.
068
069   Revision 1.3  1999/06/08 06:45:57  markl
070   Added lots of new methods.
071
072   Revision 1.2  1999/04/19 05:32:13  markl
073   New I18N support.
074
075   Revision 1.1  1999/04/18 10:26:20  markl
076   Initial revision
077   ----------------------------------------------------------------------------
078*/
079
080package kiwi.util;
081
082import java.io.*;
083import java.text.*;
084import java.util.*;
085
086import kiwi.text.*;
087
088/** The Kiwi locale manager. This class
089 * retrieves resource bundles from one or more <code>ResourceManager</code>s,
090 * and provides convenience methods for formatting various types of data
091 * according to the rules of the current locale.
092 * <p>
093 * In the case of decimal-based values such as percentages and currency
094 * amounts, if the corresponding parse fails, this class will resort to
095 * parsing the string as a generic decimal value, throwing an exception if
096 * that also fails. This allows for the parsing of values that are formatted
097 * specifically or generically. For example, the currency value $49.55 will
098 * parse to the decimal value <tt>49.55</tt> whether the source string is
099 * <tt>"$49.55"</tt> or <tt>"49.55"</tt>.
100 *
101 * @author Mark Lindner
102 */
103
104public class LocaleManager implements FormatConstants
105  {
106  private static LocaleManager defaultLocaleManager = new LocaleManager();
107  private static Collator collator;
108  private NumberFormat currencyFormat, percentFormat, decimalFormat,
109    integerFormat;
110  private Vector resourceManagers;
111  private static ParsePosition pos = new ParsePosition(0);
112  private DateFormat dateFormat[] = new DateFormat[3];
113  private DateFormat timeFormat[] = new DateFormat[3];
114  private DateFormat dateTimeFormat[] = new DateFormat[3];  
115  private int lengthTypes[] = { DateFormat.SHORT, DateFormat.MEDIUM,
116                                DateFormat.LONG };
117  private DateFormatSymbols dateFormatSymbols = null;
118  
119  /** The default number of decimal digits to retain when formatting
120   * currency values.
121   */
122
123  public static final int DEFAULT_CURRENCY_DECIMALS = 2;
124
125  /** The default number of decimal digits to retain when formatting
126   * percentage values.
127   */
128
129  public static final int DEFAULT_PERCENTAGE_DECIMALS = 4;
130
131  /** The default number of decimal digits to retain when formatting
132   * numeric values.
133   */
134
135  public static final int DEFAULT_NUMBER_DECIMALS = 4;
136
137  /** Construct a new <code>LocaleManager</code>.
138   *
139   * @since Kiwi 1.3
140   *
141   * @param resourceManager The <code>ResourceManager</code> that will be
142   * used to load resource bundles.
143   */
144  
145  public LocaleManager(ResourceManager resourceManager)
146    {
147    resourceManagers = new Vector();
148    resourceManagers.addElement(resourceManager);
149
150    setLocale(Locale.getDefault());
151    }
152
153  /** Construct a new <code>LocaleManager</code>.
154   *
155   * @since Kiwi 1.3
156   *
157   */
158  
159 public LocaleManager()
160    {
161    this(ResourceManager.getKiwiResourceManager());
162    }
163
164  /** Get the default locale.
165   *
166   * @return The current locale for this <code>LocaleManager</code>.
167   *
168   * @since Kiwi 1.3
169   */
170
171  public Locale getLocale()
172    {
173    return(Locale.getDefault());
174    }
175  
176  /** Set the default locale. This method should be called in place of
177   * <code>Locale.setDefault()</code> (though it does call this method itself).
178   *
179   * @param locale The new locale.
180   */
181  
182  public void setLocale(Locale locale)
183    {
184    if(! locale.equals(Locale.getDefault()))
185      Locale.setDefault(locale);
186
187    for(int i = 0; i < 3; i++)
188      {
189      dateFormat[i] = DateFormat.getDateInstance(lengthTypes[i]);
190      dateFormat[i].setLenient(false);
191      dateFormat[i].setTimeZone(TimeZone.getDefault());
192      timeFormat[i] = DateFormat.getTimeInstance(lengthTypes[i]);
193      timeFormat[i].setLenient(false);
194      dateTimeFormat[i] = DateFormat.getDateTimeInstance(lengthTypes[i],
195                                                         lengthTypes[i]);
196      dateTimeFormat[i].setLenient(false);
197      }
198    
199    currencyFormat = NumberFormat.getCurrencyInstance();
200    percentFormat = NumberFormat.getPercentInstance();
201    decimalFormat = NumberFormat.getNumberInstance();
202
203    integerFormat = NumberFormat.getNumberInstance();
204    integerFormat.setParseIntegerOnly(true);
205    integerFormat.setMaximumFractionDigits(0);
206
207    dateFormatSymbols = new DateFormatSymbols(locale);
208
209    collator = Collator.getInstance(locale);
210    }
211
212  /** Get a reference to the default <code>LocaleManager</code>.
213   *
214   * @since Kiwi 2.0
215   *
216   * @return The default <code>LocaleManager</code>.
217   */
218  
219  public static LocaleManager getDefault()
220    {
221    return(defaultLocaleManager);
222    }
223  
224  /** Register a <code>ResourceManager</code> with the locale manager. The
225   * locale manager searches through all registered resource managers when
226   * searching for a resource bundle. The Kiwi internal resource manager is
227   * always registered.
228   *
229   * @param manager The <code>ResourceManager</code> to register.
230   */
231  
232  public void addResourceManager(ResourceManager manager)
233    {
234    synchronized(resourceManagers)
235      {
236      // don't allow the same manager to be added twice
237
238      int i = resourceManagers.indexOf(manager);
239      if(i < 0)
240        resourceManagers.addElement(manager);
241      }
242    }
243
244  /** Unregister a <code>ResourceManager</code> from the locale manager. The
245   * Kiwi internal resource manager is always registered and cannot be removed.
246   *
247   * @param manager The <code>ResourceManager</code> to unregister.
248   */
249  
250  public void removeResourceManager(ResourceManager manager)
251    {
252    synchronized(resourceManagers)
253      {
254      // don't allow the default manager to be removed
255      
256      int i = resourceManagers.indexOf(manager);
257      if(i > 0)
258        resourceManagers.removeElement(manager);
259      }
260    }
261
262  /** Retrieve the named resource bundle. The locale manager queries all
263   * resource managers sequentially for the desired resource bundle, starting
264   * with the Kiwi internal resource manager, returning as soon as the
265   * bundle is found.
266   *
267   * @exception java.util.ResourceNotFoundException If the specified bundle
268   * could not be located by any of the registered resource managers.
269   * @return The <code>LocaleData</code> object representing the specified
270   * resource bundle.
271   */
272  
273  public LocaleData getLocaleData(String name) throws ResourceNotFoundException
274    {
275    LocaleData ld = null;
276
277    synchronized(resourceManagers)
278      {
279      Enumeration e = resourceManagers.elements();
280
281      while(e.hasMoreElements())
282        {
283        ResourceManager rm = (ResourceManager)e.nextElement();
284        try
285          {
286          ld = rm.getResourceBundle(name);
287          }
288        catch(ResourceNotFoundException ex)
289          {
290          }
291        }
292      }
293      
294    if(ld != null)
295      return(ld);
296
297    throw(new ResourceNotFoundException(name));
298    }
299
300  /** Format a date according to the rules of the current locale, with the
301   * default (medium) format length.
302   *
303   * @param date The date to format.
304   * @return A string representation of the value.
305   */
306  
307  public String formatDate(Calendar date)
308    {
309    return(formatDate(date.getTime(), MEDIUM));
310    }
311
312  /** Format a date according to the rules of the current locale, with the
313   * default (medium) format length.
314   *
315   * @param date The date to format.
316   * @return A string representation of the value.
317   */
318  
319  public String formatDate(Date date)
320    {
321    return(formatDate(date, MEDIUM));
322    }
323
324  /** Format a date according to the rules of the current locale, with the
325   * specified format length.
326   *
327   * @param date The date to format.
328   * @param type The format length; one of the symbolic constants
329   * <code>SHORT</code>, <code>MEDIUM</code>, or <code>LONG</code>.
330   * @return A string representation of the value.
331   */
332  
333  public String formatDate(Date date, int type)
334    {
335    return(dateFormat[type].format(date));
336    }  
337
338  /** Format a time according to the rules of the current locale, with the
339   * default (medium) format length.
340   *
341   * @param date The date to format.
342   * @return A string representation of the value.
343   */
344  
345  public String formatTime(Calendar date)
346    {
347    return(formatTime(date.getTime(), MEDIUM));
348    }
349
350  /** Format a time according to the rules of the current locale, with the
351   * default (medium) format length.
352   *
353   * @param date The date to format.
354   * @return A string representation of the value.
355   */
356  
357  public String formatTime(Date date)
358    {
359    return(formatTime(date, MEDIUM));
360    }
361
362  /** Format a time according to the rules of the current locale, with the
363   * specified format length.
364   *
365   * @param date The date to format.
366   * @param type The format length; one of the symbolic constants
367   * <code>SHORT</code>, <code>MEDIUM</code>, or <code>LONG</code>.
368   * @return A string representation of the value.
369   */
370  
371  public String formatTime(Date date, int type)
372    {
373    return(timeFormat[type].format(date));
374    }  
375
376  /** Format a date and time according to the rules of the current locale,
377   * with the default (medium) format length.
378   *
379   * @param date The date to format.
380   * @return A string representation of the value.
381   */
382  
383  public String formatDateTime(Calendar date)
384    {
385    return(formatDateTime(date.getTime(), MEDIUM));
386    }
387
388  /** Format a date and time according to the rules of the current locale,
389   * with the default (medium) format length.
390   *
391   * @param date The date to format.
392   * @return A string representation of the value.
393   */
394  
395  public String formatDateTime(Date date)
396    {
397    return(formatDateTime(date, MEDIUM));
398    }
399
400  /** Format a date and time according to the rules of the current locale,
401   * with the specified format length.
402   *
403   * @param date The date to format.
404   * @param type The format length; one of the symbolic constants
405   * <code>SHORT</code>, <code>MEDIUM</code>, or <code>LONG</code>.
406   * @return A string representation of the value.
407   */
408  
409  public String formatDateTime(Date date, int type)
410    {
411    return(dateTimeFormat[type].format(date));
412    }  
413
414  /** Parse a date value from a string using the rules of the current locale,
415   * with the default (medium) format length.
416   *
417   * @param s The string to parse.
418   * @return The resulting <code>Date</code> object.
419   * @exception java.text.ParseException If the value could not be parsed.
420   */
421
422  public Date parseDate(String s) throws ParseException
423    {
424    return(parseDate(s, MEDIUM));
425    }
426  
427  /** Parse a date value from a string using the rules of the current locale,
428   * with the specified format length.
429   *
430   * @param s The string to parse.
431   * @param type The format length; one of the symbolic constants
432   * <code>SHORT</code>, <code>MEDIUM</code>, or <code>LONG</code>.
433   * @return The resulting <code>Date</code> object.
434   * @exception java.text.ParseException If the value could not be parsed.
435   */
436  
437  public synchronized Date parseDate(String s, int type) throws ParseException
438    {
439    pos.setIndex(0);
440    Date d = dateFormat[type].parse(s, pos);
441    trapGarbage(s);
442    return(d);
443    }
444
445  /** Parse a time value from a string using the rules of the current locale,
446   * with the default (medium) format length.
447   *
448   * @param s The string to parse.
449   * @return The resulting <code>Date</code> object.
450   * @exception java.text.ParseException If the value could not be parsed.
451   */
452
453  public Date parseTime(String s) throws ParseException
454    {
455    return(parseTime(s, MEDIUM));
456    }
457  
458  /** Parse a time value from a string using the rules of the current locale,
459   * with the specified format length.
460   *
461   * @param s The string to parse.
462   * @param type The format length; one of the symbolic constants
463   * <code>SHORT</code>, <code>MEDIUM</code>, or <code>LONG</code>.
464   * @return The resulting <code>Date</code> object.
465   * @exception java.text.ParseException If the value could not be parsed.
466   */
467  
468  public synchronized Date parseTime(String s, int type) throws ParseException
469    {
470    pos.setIndex(0);
471    Date d = timeFormat[type].parse(s, pos);
472    trapGarbage(s);
473    return(d);
474    }
475
476  /** Parse a date and time value from a string using the rules of the current
477   * locale, with the default (medium) format length.
478   *
479   * @param s The string to parse.
480   * @return The resulting <code>Date</code> object.
481   * @exception java.text.ParseException If the value could not be parsed.
482   */
483
484  public Date parseDateTime(String s) throws ParseException
485    {
486    return(parseDateTime(s, MEDIUM));
487    }
488  
489  /** Parse a date and time value from a string using the rules of the current
490   * locale, with the specified format length.
491   *
492   * @param s The string to parse.
493   * @param type The format length; one of the symbolic constants
494   * <code>SHORT</code>, <code>MEDIUM</code>, or <code>LONG</code>.
495   * @return The resulting <code>Date</code> object.
496   * @exception java.text.ParseException If the value could not be parsed.
497   */
498  
499  public synchronized Date parseDateTime(String s, int type)
500    throws ParseException
501    {
502    pos.setIndex(0);
503    Date d = dateTimeFormat[type].parse(s, pos);
504    trapGarbage(s);
505    return(d);
506    }
507  
508  /** Format a currency value according to the rules of the current locale,
509   * with <code>DEFAULT_CURRENCY_DECIMALS</code> decimal places retained, and
510   * grouping turned off.
511   *
512   * @param value The currency value to format.
513   * @return A string representation of the value.
514   */
515  
516  public String formatCurrency(double value)
517    {
518    return(formatCurrency(value, DEFAULT_CURRENCY_DECIMALS, false));
519    }
520
521  /** Format a currency value according to the rules of the current locale,
522   * with the specified number of decimal places retained, and grouping turned
523   * off.
524   *
525   * @param value The currency value to format.
526   * @param decimals The number of decimal places to retain.
527   * @return A string representation of the value.
528   */
529  
530  public String formatCurrency(double value, int decimals)
531    {
532    return(formatCurrency(value, decimals, false));
533    }
534
535  /** Format a currency value according to the rules of the current locale,
536   * with the specified number of decimal places retained, and the specified
537   * grouping policy.
538   *
539   * @param value The currency value to format.
540   * @param decimals The number of decimal places to retain.
541   * @param grouping A flag specifying whether grouping should be used.
542   * @return A string representation of the value.
543   */
544  
545  public synchronized String formatCurrency(double value, int decimals,
546                                            boolean grouping)
547    {
548    synchronized(currencyFormat)
549      {
550      currencyFormat.setMaximumFractionDigits(decimals);
551      currencyFormat.setMinimumFractionDigits(decimals);
552      currencyFormat.setGroupingUsed(grouping);
553      return(currencyFormat.format(value));
554      }
555    }
556  
557  /** Format a currency value according to the rules of the current locale,
558   * with <code>DEFAULT_CURRENCY_DECIMALS</code> decimal places retained, and
559   * grouping turned off.
560   *
561   * @param value The currency value to format.
562   * @return A string representation of the value.
563   */
564
565  public String formatCurrency(float value)
566    {
567    return(formatCurrency(value, DEFAULT_CURRENCY_DECIMALS, false));
568    }
569
570  /** Format a currency value according to the rules of the current locale,
571   * with the specified number of decimal places retained, and grouping turned
572   * off.
573   *
574   * @param value The currency value to format.
575   * @param decimals The number of decimal places to retain.
576   * @return A string representation of the value.
577   */
578  
579  public String formatCurrency(float value, int decimals)
580    {
581    return(formatCurrency(value, decimals, false));
582    }
583
584  /** Format a currency value according to the rules of the current locale,
585   * with the specified number of decimal places retained, and the specified
586   * grouping policy.
587   *
588   * @param value The currency value to format.
589   * @param decimals The number of decimal places to retain.
590   * @param grouping A flag specifying whether grouping should be used.
591   * @return A string representation of the value.
592   */
593  
594  public String formatCurrency(float value, int decimals, boolean grouping)
595    {
596    synchronized(currencyFormat)
597      {
598      currencyFormat.setMaximumFractionDigits(decimals);
599      currencyFormat.setMinimumFractionDigits(decimals);
600      currencyFormat.setGroupingUsed(grouping);
601      return(currencyFormat.format(value));
602      }
603    }
604
605  /** Parse a currency value from a string using the rules of the current
606   * locale. The input string may include grouping characters.
607   *
608   * @param s The string to parse.
609   * @return The resulting value.
610   * @exception java.text.ParseException If the value could not be parsed.
611   */
612   
613  public synchronized double parseCurrency(String s) throws ParseException
614    {
615    try
616      {
617      s = s.trim();
618      currencyFormat.setGroupingUsed(true);
619      pos.setIndex(0);
620      Number n = currencyFormat.parse(s, pos);
621      trapGarbage(s);
622      return((n == null) ? 0 : n.doubleValue());
623      }
624    catch(ParseException ex)
625      {
626      return(parseDecimal(s));
627      }
628    }
629  
630  /** Format a percentage value according to the rules of the current locale,
631   * with <code>DEFAULT_PERCENTAGE_DECIMALS</code> decimal places retained, and
632   * grouping turned off.
633   *
634   * @param value The percentage value to format.
635   * @return A string representation of the value.
636   */
637
638  public String formatPercentage(double value)
639    {
640    return(formatPercentage(value, DEFAULT_PERCENTAGE_DECIMALS, false));
641    }
642
643  /** Format a percentage value according to the rules of the current locale,
644   * with the specified number of decimal places retained, and grouping turned
645   * off.
646   *
647   * @param value The percentage value to format.
648   * @param decimals The number of decimal places to retain.
649   * @return A string representation of the value.
650   */
651  
652  public String formatPercentage(double value, int decimals)
653    {
654    return(formatPercentage(value, decimals, false));
655    }
656
657  /** Format a percentage value according to the rules of the current locale,
658   * with the specified number of decimal places retained, and the specified
659   * grouping policy.
660   *
661   * @param value The percentage value to format.
662   * @param decimals The number of decimal places to retain.
663   * @param grouping A flag specifying whether grouping should be used.
664   * @return A string representation of the value.
665   */
666  
667  public synchronized String formatPercentage(double value, int decimals,
668                                              boolean grouping)
669    {
670    percentFormat.setMinimumFractionDigits(decimals);
671    percentFormat.setMaximumFractionDigits(decimals);
672    percentFormat.setGroupingUsed(grouping);
673    return(percentFormat.format(value));
674    }
675
676  /** Format a percentage value according to the rules of the current locale,
677   * with <code>DEFAULT_PERCENTAGE_DECIMALS</code> decimal places retained, and
678   * grouping turned off.
679   *
680   * @param value The percentage value to format.
681   * @return A string representation of the value.
682   */
683
684  public String formatPercentage(float value)
685    {
686    return(formatPercentage(value, DEFAULT_PERCENTAGE_DECIMALS, false));
687    }
688
689  /** Format a percentage value according to the rules of the current locale,
690   * with the specified number of decimal places retained, and grouping turned
691   * off.
692   *
693   * @param value The percentage value to format.
694   * @param decimals The number of decimal places to retain.
695   * @return A string representation of the value.
696   */
697  
698  public String formatPercentage(float value, int decimals)
699    {
700    return(formatPercentage(value, decimals, false));
701    }
702
703  /** Format a percentage value according to the rules of the current locale,
704   * with the specified number of decimal places retained, and the specified
705   * grouping policy.
706   *
707   * @param value The percentage value to format.
708   * @param decimals The number of decimal places to retain.
709   * @param grouping A flag specifying whether grouping should be used.
710   * @return A string representation of the value.
711   */
712  
713  public synchronized String formatPercentage(float value, int decimals,
714                                              boolean grouping)
715    {
716    percentFormat.setMinimumFractionDigits(decimals);
717    percentFormat.setMaximumFractionDigits(decimals);
718    percentFormat.setGroupingUsed(grouping);
719    return(percentFormat.format(value));
720    }
721
722  /** Parse a percentage value from a string using the rules of the current
723   * locale. The input string may include grouping characters.
724   *
725   * @param s The string to parse.
726   * @return The resulting value.
727   * @exception java.text.ParseException If the value could not be parsed.
728   */
729  
730  public synchronized double parsePercentage(String s) throws ParseException
731    {
732    try
733      {
734      s = s.trim();
735      percentFormat.setGroupingUsed(true);
736      pos.setIndex(0);
737      Number n = percentFormat.parse(s, pos);
738      trapGarbage(s);
739      return((n == null) ? 0 : n.doubleValue());
740      }
741    catch(ParseException ex)
742      {
743      return(parseDecimal(s) / 100);
744      }
745    }
746
747  /** Format an integer value according to the rules of the current
748   * locale, with grouping turned off.
749   *
750   * @param value The integer value to format.
751   * @return A string representation of the value.
752   */
753  
754  public String formatInteger(int value)
755    {
756    return(formatInteger((long)value));
757    }
758
759  /** Format an integer value according to the rules of the current
760   * locale, with grouping turned off.
761   *
762   * @param value The integer value to format.
763   * @return A string representation of the value.
764   */
765  
766  public String formatInteger(long value)
767    {
768    return(formatInteger(value, false));
769    }
770
771  /** Format an integer value according to the rules of the current
772   * locale, and the specified grouping policy.
773   *
774   * @param value The integer value to format.
775   * @return A string representation of the value.
776   */
777  
778  public String formatInteger(int value, boolean grouping)
779    {
780    return(formatInteger((long)value, grouping));
781    }
782
783  /** Format an integer value according to the rules of the current
784   * locale, and the specified grouping policy.
785   *
786   * @param value The integer value to format.
787   * @return A string representation of the value.
788   */
789  
790  public synchronized String formatInteger(long value, boolean grouping)
791    {
792    integerFormat.setGroupingUsed(grouping);
793    return(integerFormat.format(value));
794    }
795
796  /** Parse an integer value from a string using the rules of the current
797   * locale. The input string may include grouping characters.
798   *
799   * @param s The string to parse.
800   * @return The resulting value.
801   * @exception java.text.ParseException If the value could not be parsed.
802   */
803  
804  public synchronized long parseInteger(String s) throws ParseException
805    {
806    s = s.trim();
807    integerFormat.setGroupingUsed(true);
808    pos.setIndex(0);
809    Number n = integerFormat.parse(s, pos);
810    trapGarbage(s);
811    return((n == null) ? 0 : n.longValue());
812    }
813  
814  /** Format a floating point value according to the rules of the current
815   * locale, with <code>DEFAULT_NUMBER_DECIMALS</code> decimal places
816   * retained, and grouping turned off.
817   *
818   * @param value The floating point value to format.
819   * @return A string representation of the value.
820   */
821  
822  public String formatDecimal(double value)
823    {
824    return(formatDecimal(value, DEFAULT_NUMBER_DECIMALS, false));
825    }
826
827  /** Format a floating point value according to the rules of the current
828   * locale, with the specified number of decimal places retained, and
829   * grouping turned off.
830   *
831   * @param value The floating point value to format.
832   * @param decimals The number of decimal places to retain.
833   * @return A string representation of the value.
834   */
835  
836  public String formatDecimal(double value, int decimals)
837    {
838    return(formatDecimal(value, decimals, false));
839    }
840
841  /** Format a floating point value according to the rules of the current
842   * locale, with the specified number of decimal places retained, and the
843   * specified grouping policy.
844   *
845   * @param value The floating point value to format.
846   * @param decimals The number of decimal places to retain.
847   * @param grouping A flag specifying whether grouping should be used.
848   * @return A string representation of the value.
849   */
850  
851  public synchronized String formatDecimal(double value, int decimals,
852                                           boolean grouping)
853    {
854    decimalFormat.setMinimumFractionDigits(decimals);
855    decimalFormat.setMaximumFractionDigits(decimals);
856    decimalFormat.setGroupingUsed(grouping);
857    return(decimalFormat.format(value));
858    }
859
860  /** Format a floating point value according to the rules of the current
861   * locale, with <code>DEFAULT_NUMBER_DECIMALS</code> decimal places
862   * retained, and grouping turned off.
863   *
864   * @param value The floating point value to format.
865   * @return A string representation of the value.
866   */
867  
868  public String formatDecimal(float value)
869    {
870    return(formatDecimal((double)value));
871    }
872
873  /** Format a floating point value according to the rules of the current
874   * locale, with the specified number of decimal places retained, and
875   * grouping turned off.
876   *
877   * @param value The floating point value to format.
878   * @param decimals The number of decimal places to retain.
879   * @return A string representation of the value.
880   */
881  
882  public String formatDecimal(float value, int decimals)
883    {
884    return(formatDecimal((double)value, decimals));
885    }
886
887  /** Format a floating point value according to the rules of the current
888   * locale, with the specified number of decimal places retained, and the
889   * specified grouping policy.
890   *
891   * @param value The floating point value to format.
892   * @param decimals The number of decimal places to retain.
893   * @param grouping A flag specifying whether grouping should be used.
894   * @return A string representation of the value.
895   */
896    
897  public String formatDecimal(float value, int decimals, boolean grouping)
898    {
899    return(formatDecimal((double)value, decimals, grouping));
900    }
901
902  /** Parse a numeric value from a string using the rules of the current
903   * locale. The input string may include grouping characters.
904   *
905   * @param s The string to parse.
906   * @return The resulting value.
907   * @exception java.text.ParseException If the value could not be parsed.
908   */
909  
910  public synchronized double parseDecimal(String s) throws ParseException
911    {
912    s = s.trim();
913    decimalFormat.setGroupingUsed(true);
914    pos.setIndex(0);
915    Number n = decimalFormat.parse(s, pos);
916    trapGarbage(s);
917    return((n == null) ? 0 : n.doubleValue());
918    }
919
920  /** Get an instance of the <code>DateFormatSymbols</code> object for the
921   * current locale.
922   *
923   * @return The <code>DateFormatSymbols</code> object.
924   */
925  
926  public DateFormatSymbols getDateFormatSymbols()
927    {
928    return(dateFormatSymbols);
929    }
930
931  /** Get an instance of the <code>Collator</code> object for the current
932   * locale.
933   *
934   * @return The <code>Collator</code> object.
935   */
936
937  public Collator getCollator()
938    {
939    return(collator);
940    }
941
942  /** Get a reference to the short date format object for the current locale.
943   *
944   * @return The <code>DateFormat</code> object.
945   *
946   * @since Kiwi 1.4
947   */
948
949  public DateFormat getShortDateFormat()
950    {
951    return(dateFormat[SHORT]);
952    }
953  
954  /*
955   */
956  
957  private void trapGarbage(String s) throws ParseException
958    {
959    if(pos.getIndex() != s.length())
960      throw(new ParseException("Garbage in string " + s, pos.getIndex()));
961    }
962  
963  }
964
965/* end of source file */