source: trunk/gcc/libjava/java/text/MessageFormat.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 15.7 KB
Line 
1/* MessageFormat.java - Localized message formatting.
2 Copyright (C) 1999, 2001, 2002 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38
39package java.text;
40
41import java.util.Date;
42import java.util.Locale;
43import java.util.Vector;
44
45/**
46 * @author Tom Tromey <tromey@cygnus.com>
47 * @author Jorge Aliss <jaliss@hotmail.com>
48 * @date March 3, 1999
49 */
50/* Written using "Java Class Libraries", 2nd edition, plus online
51 * API docs for JDK 1.2 from http://www.javasoft.com.
52 * Status: Believed complete and correct to 1.2, except serialization.
53 * and parsing.
54 */
55final class MessageFormatElement
56{
57 // Argument number.
58 int argNumber;
59 // Formatter to be used. This is the format set by setFormat.
60 Format setFormat;
61 // Formatter to be used based on the type.
62 Format format;
63
64 // Argument will be checked to make sure it is an instance of this
65 // class.
66 Class formatClass;
67
68 // Formatter type.
69 String type;
70 // Formatter style.
71 String style;
72
73 // Text to follow this element.
74 String trailer;
75
76 // Recompute the locale-based formatter.
77 void setLocale (Locale loc)
78 {
79 if (type == null)
80 ;
81 else if (type.equals("number"))
82 {
83 formatClass = java.lang.Number.class;
84
85 if (style == null)
86 format = NumberFormat.getInstance(loc);
87 else if (style.equals("currency"))
88 format = NumberFormat.getCurrencyInstance(loc);
89 else if (style.equals("percent"))
90 format = NumberFormat.getPercentInstance(loc);
91 else if (style.equals("integer"))
92 {
93 NumberFormat nf = NumberFormat.getNumberInstance(loc);
94 nf.setMaximumFractionDigits(0);
95 nf.setGroupingUsed(false);
96 format = nf;
97 }
98 else
99 {
100 format = NumberFormat.getNumberInstance(loc);
101 DecimalFormat df = (DecimalFormat) format;
102 df.applyPattern(style);
103 }
104 }
105 else if (type.equals("time") || type.equals("date"))
106 {
107 formatClass = java.util.Date.class;
108
109 int val = DateFormat.DEFAULT;
110 if (style == null)
111 ;
112 else if (style.equals("short"))
113 val = DateFormat.SHORT;
114 else if (style.equals("medium"))
115 val = DateFormat.MEDIUM;
116 else if (style.equals("long"))
117 val = DateFormat.LONG;
118 else if (style.equals("full"))
119 val = DateFormat.FULL;
120
121 if (type.equals("time"))
122 format = DateFormat.getTimeInstance(val, loc);
123 else
124 format = DateFormat.getDateInstance(val, loc);
125
126 if (style != null && val == DateFormat.DEFAULT)
127 {
128 SimpleDateFormat sdf = (SimpleDateFormat) format;
129 sdf.applyPattern(style);
130 }
131 }
132 else if (type.equals("choice"))
133 {
134 formatClass = java.lang.Number.class;
135
136 if (style == null)
137 throw new
138 IllegalArgumentException ("style required for choice format");
139 format = new ChoiceFormat (style);
140 }
141 }
142}
143
144public class MessageFormat extends Format
145{
146 // Helper that returns the text up to the next format opener. The
147 // text is put into BUFFER. Returns index of character after end of
148 // string. Throws IllegalArgumentException on error.
149 private static final int scanString (String pat, int index,
150 StringBuffer buffer)
151 {
152 int max = pat.length();
153 buffer.setLength(0);
154 for (; index < max; ++index)
155 {
156 char c = pat.charAt(index);
157 if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'')
158 {
159 buffer.append(pat.charAt(index + 1));
160 index += 2;
161 }
162 else if (c == '\'' && index + 1 < max
163 && pat.charAt(index + 1) == '\'')
164 {
165 buffer.append(c);
166 ++index;
167 }
168 else if (c == '{')
169 break;
170 else if (c == '}')
171 throw new IllegalArgumentException ();
172 else
173 buffer.append(c);
174 }
175 return index;
176 }
177
178 // This helper retrieves a single part of a format element. Returns
179 // the index of the terminating character.
180 private static final int scanFormatElement (String pat, int index,
181 StringBuffer buffer,
182 char term)
183 {
184 int max = pat.length();
185 buffer.setLength(0);
186 int brace_depth = 1;
187
188 for (; index < max; ++index)
189 {
190 char c = pat.charAt(index);
191 if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'')
192 {
193 buffer.append(c);
194 buffer.append(pat.charAt(index + 1));
195 buffer.append(c);
196 index += 2;
197 }
198 else if (c == '\'' && index + 1 < max
199 && pat.charAt(index + 1) == '\'')
200 {
201 buffer.append(c);
202 ++index;
203 }
204 else if (c == '{')
205 {
206 buffer.append(c);
207 ++brace_depth;
208 }
209 else if (c == '}')
210 {
211 if (--brace_depth == 0)
212 break;
213 buffer.append(c);
214 }
215 // Check for TERM after braces, because TERM might be `}'.
216 else if (c == term)
217 break;
218 else
219 buffer.append(c);
220 }
221 return index;
222 }
223
224 // This is used to parse a format element and whatever non-format
225 // text might trail it.
226 private static final int scanFormat (String pat, int index,
227 StringBuffer buffer, Vector elts,
228 Locale locale)
229 {
230 MessageFormatElement mfe = new MessageFormatElement ();
231 elts.addElement(mfe);
232
233 int max = pat.length();
234
235 // Skip the opening `{'.
236 ++index;
237
238 // Fetch the argument number.
239 index = scanFormatElement (pat, index, buffer, ',');
240 try
241 {
242 mfe.argNumber = Integer.parseInt(buffer.toString());
243 }
244 catch (NumberFormatException nfx)
245 {
246 throw new IllegalArgumentException ();
247 }
248
249 // Extract the element format.
250 if (index < max && pat.charAt(index) == ',')
251 {
252 index = scanFormatElement (pat, index + 1, buffer, ',');
253 mfe.type = buffer.toString();
254
255 // Extract the style.
256 if (index < max && pat.charAt(index) == ',')
257 {
258 index = scanFormatElement (pat, index + 1, buffer, '}');
259 mfe.style = buffer.toString ();
260 }
261 }
262
263 // Advance past the last terminator.
264 if (index >= max || pat.charAt(index) != '}')
265 throw new IllegalArgumentException ();
266 ++index;
267
268 // Now fetch trailing string.
269 index = scanString (pat, index, buffer);
270 mfe.trailer = buffer.toString ();
271
272 mfe.setLocale(locale);
273
274 return index;
275 }
276
277 /**
278 * Applies the specified pattern to this MessageFormat.
279 *
280 * @param aPattern The Pattern
281 */
282 public void applyPattern (String newPattern)
283 {
284 pattern = newPattern;
285
286 StringBuffer tempBuffer = new StringBuffer ();
287
288 int index = scanString (newPattern, 0, tempBuffer);
289 leader = tempBuffer.toString();
290
291 Vector elts = new Vector ();
292 while (index < newPattern.length())
293 index = scanFormat (newPattern, index, tempBuffer, elts, locale);
294
295 elements = new MessageFormatElement[elts.size()];
296 elts.copyInto(elements);
297 }
298
299 /**
300 * Overrides Format.clone()
301 */
302 public Object clone ()
303 {
304 MessageFormat c = (MessageFormat) super.clone ();
305 c.elements = (MessageFormatElement[]) elements.clone ();
306 return c;
307 }
308
309 /**
310 * Overrides Format.equals(Object obj)
311 */
312 public boolean equals (Object obj)
313 {
314 if (! (obj instanceof MessageFormat))
315 return false;
316 MessageFormat mf = (MessageFormat) obj;
317 return (pattern.equals(mf.pattern)
318 && locale.equals(mf.locale));
319 }
320
321 /**
322 * A convinience method to format patterns.
323 *
324 * @param aPattern The pattern used when formatting.
325 * @param arguments The array containing the objects to be formatted.
326 */
327 public static String format (String pattern, Object arguments[])
328 {
329 MessageFormat mf = new MessageFormat (pattern);
330 StringBuffer sb = new StringBuffer ();
331 FieldPosition fp = new FieldPosition (NumberFormat.INTEGER_FIELD);
332 return mf.format(arguments, sb, fp).toString();
333 }
334
335 /**
336 * Returns the pattern with the formatted objects.
337 *
338 * @param source The array containing the objects to be formatted.
339 * @param result The StringBuffer where the text is appened.
340 * @param fp A FieldPosition object (it is ignored).
341 */
342 public final StringBuffer format (Object arguments[], StringBuffer appendBuf,
343 FieldPosition ignore)
344 {
345 appendBuf.append(leader);
346
347 for (int i = 0; i < elements.length; ++i)
348 {
349 if (elements[i].argNumber >= arguments.length)
350 throw new IllegalArgumentException ();
351 Object thisArg = arguments[elements[i].argNumber];
352
353 Format formatter = null;
354 if (elements[i].setFormat != null)
355 formatter = elements[i].setFormat;
356 else if (elements[i].format != null)
357 {
358 if (elements[i].formatClass != null
359 && ! elements[i].formatClass.isInstance(thisArg))
360 throw new IllegalArgumentException ();
361 formatter = elements[i].format;
362 }
363 else if (thisArg instanceof Number)
364 formatter = NumberFormat.getInstance(locale);
365 else if (thisArg instanceof Date)
366 formatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
367 else
368 appendBuf.append(thisArg);
369
370 if (formatter != null)
371 {
372 // Special-case ChoiceFormat.
373 if (formatter instanceof ChoiceFormat)
374 {
375 StringBuffer buf = new StringBuffer ();
376 formatter.format(thisArg, buf, ignore);
377 MessageFormat mf = new MessageFormat ();
378 mf.setLocale(locale);
379 mf.applyPattern(buf.toString());
380 mf.format(arguments, appendBuf, ignore);
381 }
382 else
383 formatter.format(thisArg, appendBuf, ignore);
384 }
385
386 appendBuf.append(elements[i].trailer);
387 }
388
389 return appendBuf;
390 }
391
392 /**
393 * Returns the pattern with the formatted objects.
394 *
395 * @param source The object to be formatted.
396 * @param result The StringBuffer where the text is appened.
397 * @param fp A FieldPosition object (it is ignored).
398 */
399 public final StringBuffer format (Object singleArg, StringBuffer appendBuf,
400 FieldPosition ignore)
401 {
402 Object[] args;
403
404 if (singleArg instanceof Object[])
405 {
406 // This isn't specified in any manual, but it follows the
407 // JDK implementation.
408 args = (Object[]) singleArg;
409 }
410 else
411 {
412 args = new Object[1];
413 args[0] = singleArg;
414 }
415 return format (args, appendBuf, ignore);
416 }
417
418 /**
419 * Returns an array with the Formats for
420 * the arguments.
421 */
422 public Format[] getFormats ()
423 {
424 Format[] f = new Format[elements.length];
425 for (int i = elements.length - 1; i >= 0; --i)
426 f[i] = elements[i].setFormat;
427 return f;
428 }
429
430 /**
431 * Returns the locale.
432 */
433 public Locale getLocale ()
434 {
435 return locale;
436 }
437
438 /**
439 * Overrides Format.hashCode()
440 */
441 public int hashCode ()
442 {
443 // FIXME: not a very good hash.
444 return pattern.hashCode() + locale.hashCode();
445 }
446
447 private MessageFormat ()
448 {
449 }
450
451 /**
452 * Creates a new MessageFormat object with
453 * the specified pattern
454 *
455 * @param aPattern The Pattern
456 */
457 public MessageFormat (String pattern)
458 {
459 locale = Locale.getDefault();
460 applyPattern (pattern);
461 }
462
463 public Object[] parse (String sourceStr, ParsePosition pos)
464 {
465 // Check initial text.
466 int index = pos.getIndex();
467 if (! sourceStr.startsWith(leader, index))
468 {
469 pos.setErrorIndex(index);
470 return null;
471 }
472 index += leader.length();
473
474 Vector results = new Vector (elements.length, 1);
475 // Now check each format.
476 for (int i = 0; i < elements.length; ++i)
477 {
478 Format formatter = null;
479 if (elements[i].setFormat != null)
480 formatter = elements[i].setFormat;
481 else if (elements[i].format != null)
482 formatter = elements[i].format;
483
484 Object value = null;
485 if (formatter instanceof ChoiceFormat)
486 {
487 // We must special-case a ChoiceFormat because it might
488 // have recursive formatting.
489 ChoiceFormat cf = (ChoiceFormat) formatter;
490 String[] formats = (String[]) cf.getFormats();
491 double[] limits = (double[]) cf.getLimits();
492 MessageFormat subfmt = new MessageFormat ();
493 subfmt.setLocale(locale);
494 ParsePosition subpos = new ParsePosition (index);
495
496 int j;
497 for (j = 0; value == null && j < limits.length; ++j)
498 {
499 subfmt.applyPattern(formats[j]);
500 subpos.setIndex(index);
501 value = subfmt.parse(sourceStr, subpos);
502 }
503 if (value != null)
504 {
505 index = subpos.getIndex();
506 value = new Double (limits[j]);
507 }
508 }
509 else if (formatter != null)
510 {
511 pos.setIndex(index);
512 value = formatter.parseObject(sourceStr, pos);
513 if (value != null)
514 index = pos.getIndex();
515 }
516 else
517 {
518 // We have a String format. This can lose in a number
519 // of ways, but we give it a shot.
520 int next_index = sourceStr.indexOf(elements[i].trailer, index);
521 if (next_index == -1)
522 {
523 pos.setErrorIndex(index);
524 return null;
525 }
526 value = sourceStr.substring(index, next_index);
527 index = next_index;
528 }
529
530 if (value == null
531 || ! sourceStr.startsWith(elements[i].trailer, index))
532 {
533 pos.setErrorIndex(index);
534 return null;
535 }
536
537 if (elements[i].argNumber >= results.size())
538 results.setSize(elements[i].argNumber + 1);
539 results.setElementAt(value, elements[i].argNumber);
540
541 index += elements[i].trailer.length();
542 }
543
544 Object[] r = new Object[results.size()];
545 results.copyInto(r);
546 return r;
547 }
548
549 public Object[] parse (String sourceStr) throws ParseException
550 {
551 ParsePosition pp = new ParsePosition (0);
552 Object[] r = parse (sourceStr, pp);
553 if (r == null)
554 throw new ParseException ("couldn't parse string", pp.getErrorIndex());
555 return r;
556 }
557
558 public Object parseObject (String sourceStr, ParsePosition pos)
559 {
560 return parse (sourceStr, pos);
561 }
562
563 /**
564 * Sets the format for the argument at an specified
565 * index.
566 *
567 * @param index The index.
568 * @format The Format object.
569 */
570 public void setFormat (int variableNum, Format newFormat)
571 {
572 elements[variableNum].setFormat = newFormat;
573 }
574
575 /**
576 * Sets the formats for the arguments.
577 *
578 * @param formats An array of Format objects.
579 */
580 public void setFormats (Format[] newFormats)
581 {
582 if (newFormats.length < elements.length)
583 throw new IllegalArgumentException ();
584 int len = Math.min(newFormats.length, elements.length);
585 for (int i = 0; i < len; ++i)
586 elements[i].setFormat = newFormats[i];
587 }
588
589 /**
590 * Sets the locale.
591 *
592 * @param locale A Locale
593 */
594 public void setLocale (Locale loc)
595 {
596 locale = loc;
597 if (elements != null)
598 {
599 for (int i = 0; i < elements.length; ++i)
600 elements[i].setLocale(loc);
601 }
602 }
603
604 /**
605 * Returns the pattern.
606 */
607 public String toPattern ()
608 {
609 return pattern;
610 }
611
612 // The pattern string.
613 private String pattern;
614 // The locale.
615 private Locale locale;
616 // Variables.
617 private MessageFormatElement[] elements;
618 // Leader text.
619 private String leader;
620}
Note: See TracBrowser for help on using the repository browser.