OSDN Git Service

bb2a4c3592a474a0b779baa4c0738d48eee41f21
[pf3gnuchains/gcc-fork.git] / libjava / java / text / MessageFormat.java
1 // MessageFormat.java - Localized message formatting.
2
3 /* Copyright (C) 1999, 2001  Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 package java.text;
12
13 import java.util.Date;
14 import java.util.Locale;
15 import java.util.Vector;
16
17 /**
18  * @author Tom Tromey <tromey@cygnus.com>
19  * @date March 3, 1999
20  */
21 /* Written using "Java Class Libraries", 2nd edition, plus online
22  * API docs for JDK 1.2 from http://www.javasoft.com.
23  * Status:  Believed complete and correct to 1.2, except serialization.
24  *          and parsing.
25  */
26
27 final class MessageFormatElement
28 {
29   // Argument number.
30   int argNumber;
31   // Formatter to be used.  This is the format set by setFormat.
32   Format setFormat;
33   // Formatter to be used based on the type.
34   Format format;
35
36   // Argument will be checked to make sure it is an instance of this
37   // class.
38   Class formatClass;
39
40   // Formatter type.
41   String type;
42   // Formatter style.
43   String style;
44
45   // Text to follow this element.
46   String trailer;
47
48   // Recompute the locale-based formatter.
49   void setLocale (Locale loc)
50     {
51       if (type == null)
52         ;
53       else if (type.equals("number"))
54         {
55           formatClass = java.lang.Number.class;
56
57           if (style == null)
58             format = NumberFormat.getInstance(loc);
59           else if (style.equals("currency"))
60             format = NumberFormat.getCurrencyInstance(loc);
61           else if (style.equals("percent"))
62             format = NumberFormat.getPercentInstance(loc);
63           else if (style.equals("integer"))
64             {
65               NumberFormat nf = NumberFormat.getNumberInstance(loc);
66               nf.setMaximumFractionDigits(0);
67               nf.setGroupingUsed(false);
68               format = nf;
69             }
70           else
71             {
72               format = NumberFormat.getNumberInstance(loc);
73               DecimalFormat df = (DecimalFormat) format;
74               try
75                 {
76                   df.applyPattern(style);
77                 }
78               catch (ParseException x)
79                 {
80                   throw new IllegalArgumentException (x.getMessage());
81                 }
82             }
83         }
84       else if (type.equals("time") || type.equals("date"))
85         {
86           formatClass = java.util.Date.class;
87
88           int val = DateFormat.DEFAULT;
89           if (style == null)
90             ;
91           else if (style.equals("short"))
92             val = DateFormat.SHORT;
93           else if (style.equals("medium"))
94             val = DateFormat.MEDIUM;
95           else if (style.equals("long"))
96             val = DateFormat.LONG;
97           else if (style.equals("full"))
98             val = DateFormat.FULL;
99
100           if (type.equals("time"))
101             format = DateFormat.getTimeInstance(val, loc);
102           else
103             format = DateFormat.getDateInstance(val, loc);
104
105           if (style != null && val == DateFormat.DEFAULT)
106             {
107               SimpleDateFormat sdf = (SimpleDateFormat) format;
108               sdf.applyPattern(style);
109             }
110         }
111       else if (type.equals("choice"))
112         {
113           formatClass = java.lang.Number.class;
114
115           if (style == null)
116             throw new
117               IllegalArgumentException ("style required for choice format");
118           format = new ChoiceFormat (style);
119         }
120     }
121 }
122
123 public class MessageFormat extends Format
124 {
125   // Helper that returns the text up to the next format opener.  The
126   // text is put into BUFFER.  Returns index of character after end of
127   // string.  Throws IllegalArgumentException on error.
128   private static final int scanString (String pat, int index,
129                                        StringBuffer buffer)
130     {
131       int max = pat.length();
132       buffer.setLength(0);
133       for (; index < max; ++index)
134         {
135           char c = pat.charAt(index);
136           if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'')
137             {
138               buffer.append(pat.charAt(index + 1));
139               index += 2;
140             }
141           else if (c == '\'' && index + 1 < max
142                    && pat.charAt(index + 1) == '\'')
143             {
144               buffer.append(c);
145               ++index;
146             }
147           else if (c == '{')
148             break;
149           else if (c == '}')
150             throw new IllegalArgumentException ();
151           else
152             buffer.append(c);
153         }
154       return index;
155     }
156
157   // This helper retrieves a single part of a format element.  Returns
158   // the index of the terminating character.
159   private static final int scanFormatElement (String pat, int index,
160                                               StringBuffer buffer,
161                                               char term)
162     {
163       int max = pat.length();
164       buffer.setLength(0);
165       int brace_depth = 1;
166
167       for (; index < max; ++index)
168         {
169           char c = pat.charAt(index);
170           if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'')
171             {
172               buffer.append(c);
173               buffer.append(pat.charAt(index + 1));
174               buffer.append(c);
175               index += 2;
176             }
177           else if (c == '\'' && index + 1 < max
178                    && pat.charAt(index + 1) == '\'')
179             {
180               buffer.append(c);
181               ++index;
182             }
183           else if (c == '{')
184             {
185               buffer.append(c);
186               ++brace_depth;
187             }
188           else if (c == '}')
189             {
190               if (--brace_depth == 0)
191                 break;
192               buffer.append(c);
193             }
194           // Check for TERM after braces, because TERM might be `}'.
195           else if (c == term)
196             break;
197           else
198             buffer.append(c);
199         }
200       return index;
201     }
202
203   // This is used to parse a format element and whatever non-format
204   // text might trail it.
205   private static final int scanFormat (String pat, int index,
206                                        StringBuffer buffer, Vector elts,
207                                        Locale locale)
208     {
209       MessageFormatElement mfe = new MessageFormatElement ();
210       elts.addElement(mfe);
211
212       int max = pat.length();
213
214       // Skip the opening `{'.
215       ++index;
216
217       // Fetch the argument number.
218       index = scanFormatElement (pat, index, buffer, ',');
219       try
220         {
221           mfe.argNumber = Integer.parseInt(buffer.toString());
222         }
223       catch (NumberFormatException nfx)
224         {
225           throw new IllegalArgumentException ();
226         }
227
228       // Extract the element format.
229       if (index < max && pat.charAt(index) == ',')
230         {
231           index = scanFormatElement (pat, index + 1, buffer, ',');
232           mfe.type = buffer.toString();
233
234           // Extract the style.
235           if (index < max && pat.charAt(index) == ',')
236             {
237               index = scanFormatElement (pat, index + 1, buffer, '}');
238               mfe.style = buffer.toString ();
239             }
240         }
241
242       // Advance past the last terminator.
243       if (index >= max || pat.charAt(index) != '}')
244         throw new IllegalArgumentException ();
245       ++index;
246
247       // Now fetch trailing string.
248       index = scanString (pat, index, buffer);
249       mfe.trailer = buffer.toString ();
250
251       mfe.setLocale(locale);
252
253       return index;
254     }
255
256   public void applyPattern (String newPattern)
257     {
258       pattern = newPattern;
259
260       StringBuffer tempBuffer = new StringBuffer ();
261
262       int index = scanString (newPattern, 0, tempBuffer);
263       leader = tempBuffer.toString();
264
265       Vector elts = new Vector ();
266       while (index < newPattern.length())
267         index = scanFormat (newPattern, index, tempBuffer, elts, locale);
268
269       elements = new MessageFormatElement[elts.size()];
270       elts.copyInto(elements);
271     }
272
273   public Object clone ()
274     {
275       MessageFormat c = new MessageFormat ();
276       c.setLocale(locale);
277       c.applyPattern(pattern);
278       return (Object) c;
279     }
280
281   public boolean equals (Object obj)
282     {
283       if (! (obj instanceof MessageFormat))
284         return false;
285       MessageFormat mf = (MessageFormat) obj;
286       return (pattern.equals(mf.pattern)
287               && locale.equals(mf.locale));
288     }
289
290   public static String format (String pattern, Object arguments[])
291     {
292       MessageFormat mf = new MessageFormat (pattern);
293       StringBuffer sb = new StringBuffer ();
294       FieldPosition fp = new FieldPosition (NumberFormat.INTEGER_FIELD);
295       return mf.format(arguments, sb, fp).toString();
296     }
297
298   public final StringBuffer format (Object arguments[], StringBuffer appendBuf,
299                                     FieldPosition ignore)
300     {
301       appendBuf.append(leader);
302
303       for (int i = 0; i < elements.length; ++i)
304         {
305           if (elements[i].argNumber >= arguments.length)
306             throw new IllegalArgumentException ();
307           Object thisArg = arguments[elements[i].argNumber];
308
309           Format formatter = null;
310           if (elements[i].setFormat != null)
311             formatter = elements[i].setFormat;
312           else if (elements[i].format != null)
313             {
314               if (elements[i].formatClass != null
315                   && ! elements[i].formatClass.isInstance(thisArg))
316                 throw new IllegalArgumentException ();
317               formatter = elements[i].format;
318             }
319           else if (thisArg instanceof Number)
320             formatter = NumberFormat.getInstance(locale);
321           else if (thisArg instanceof Date)
322             formatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
323           else
324             appendBuf.append(thisArg);
325
326           if (formatter != null)
327             {
328               // Special-case ChoiceFormat.
329               if (formatter instanceof ChoiceFormat)
330                 {
331                   StringBuffer buf = new StringBuffer ();
332                   // FIXME: don't actually know what is correct here.
333                   // Can a sub-format refer to any argument, or just
334                   // the single argument passed to it?  Must test
335                   // against JDK.
336                   formatter.format(thisArg, buf, ignore);
337                   MessageFormat mf = new MessageFormat ();
338                   mf.setLocale(locale);
339                   mf.applyPattern(buf.toString());
340                   formatter = mf;
341                 }
342               formatter.format(thisArg, appendBuf, ignore);
343             }
344
345           appendBuf.append(elements[i].trailer);
346         }
347
348       return appendBuf;
349     }
350
351   public final StringBuffer format (Object singleArg, StringBuffer appendBuf,
352                                     FieldPosition ignore)
353     {
354       Object[] args;
355
356       if (singleArg instanceof Object[])
357         {
358           // This isn't specified in any manual, but it follows the
359           // JDK implementation.
360           args = (Object[]) singleArg;
361         }
362       else
363         {
364           args = new Object[1];
365           args[0] = singleArg;
366         }
367       return format (args, appendBuf, ignore);
368     }
369
370   public Format[] getFormats ()
371     {
372       Format[] f = new Format[elements.length];
373       for (int i = elements.length - 1; i >= 0; --i)
374         f[i] = elements[i].setFormat;
375       return f;
376     }
377
378   public Locale getLocale ()
379     {
380       return locale;
381     }
382
383   public int hashCode ()
384     {
385       // FIXME: not a very good hash.
386       return pattern.hashCode() + locale.hashCode();
387     }
388
389   private MessageFormat ()
390     {
391     }
392
393   public MessageFormat (String pattern)
394     {
395       locale = Locale.getDefault();
396       applyPattern (pattern);
397     }
398
399   public Object[] parse (String sourceStr, ParsePosition pos)
400     {
401       // Check initial text.
402       int index = pos.getIndex();
403       if (! sourceStr.startsWith(leader, index))
404         {
405           pos.setErrorIndex(index);
406           return null;
407         }
408       index += leader.length();
409
410       Vector results = new Vector (elements.length, 1);
411       // Now check each format.
412       for (int i = 0; i < elements.length; ++i)
413         {
414           Format formatter = null;
415           if (elements[i].setFormat != null)
416             formatter = elements[i].setFormat;
417           else if (elements[i].format != null)
418             formatter = elements[i].format;
419
420           Object value = null;
421           if (formatter instanceof ChoiceFormat)
422             {
423               // We must special-case a ChoiceFormat because it might
424               // have recursive formatting.
425               ChoiceFormat cf = (ChoiceFormat) formatter;
426               String[] formats = (String[]) cf.getFormats();
427               double[] limits = (double[]) cf.getLimits();
428               MessageFormat subfmt = new MessageFormat ();
429               subfmt.setLocale(locale);
430               ParsePosition subpos = new ParsePosition (index);
431
432               int j;
433               for (j = 0; value == null && j < limits.length; ++j)
434                 {
435                   subfmt.applyPattern(formats[j]);
436                   subpos.setIndex(index);
437                   value = subfmt.parse(sourceStr, subpos);
438                 }
439               if (value != null)
440                 {
441                   index = subpos.getIndex();
442                   value = new Double (limits[j]);
443                 }
444             }
445           else if (formatter != null)
446             {
447               pos.setIndex(index);
448               value = formatter.parseObject(sourceStr, pos);
449               if (value != null)
450                 index = pos.getIndex();
451             }
452           else
453             {
454               // We have a String format.  This can lose in a number
455               // of ways, but we give it a shot.
456               int next_index = sourceStr.indexOf(elements[i].trailer, index);
457               if (next_index == -1)
458                 {
459                   pos.setErrorIndex(index);
460                   return null;
461                 }
462               value = sourceStr.substring(index, next_index);
463               index = next_index;
464             }
465
466           if (value == null
467               || ! sourceStr.startsWith(elements[i].trailer, index))
468             {
469               pos.setErrorIndex(index);
470               return null;
471             }
472
473           if (elements[i].argNumber >= results.size())
474             results.setSize(elements[i].argNumber + 1);
475           results.setElementAt(value, elements[i].argNumber);
476
477           index += elements[i].trailer.length();
478         }
479
480       Object[] r = new Object[results.size()];
481       results.copyInto(r);
482       return r;
483     }
484
485   public Object[] parse (String sourceStr) throws ParseException
486     {
487       ParsePosition pp = new ParsePosition (0);
488       Object[] r = parse (sourceStr, pp);
489       if (r == null)
490         throw new ParseException ("couldn't parse string", pp.getErrorIndex());
491       return r;
492     }
493
494   public Object parseObject (String sourceStr, ParsePosition pos)
495     {
496       return parse (sourceStr, pos);
497     }
498
499   public void setFormat (int variableNum, Format newFormat)
500     {
501       elements[variableNum].setFormat = newFormat;
502     }
503
504   public void setFormats (Format[] newFormats)
505     {
506       if (newFormats.length < elements.length)
507         throw new IllegalArgumentException ();
508       int len = Math.min(newFormats.length, elements.length);
509       for (int i = 0; i < len; ++i)
510         elements[i].setFormat = newFormats[i];
511     }
512
513   public void setLocale (Locale loc)
514     {
515       locale = loc;
516       if (elements != null)
517         {
518           for (int i = 0; i < elements.length; ++i)
519             elements[i].setLocale(loc);
520         }
521     }
522
523   public String toPattern ()
524     {
525       return pattern;
526     }
527
528   // The pattern string.
529   private String pattern;
530   // The locale.
531   private Locale locale;
532   // Variables.
533   private MessageFormatElement[] elements;
534   // Leader text.
535   private String leader;
536 }