source: trunk/src/gcc/libjava/java/text/MessageFormat.java@ 154

Last change on this file since 154 was 2, checked in by bird, 23 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 15.8 KB
Line 
1/* MessageFormat.java - Localized message formatting.
2 Copyright (C) 1999, 2001 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 <[email protected]>
47 * @author Jorge Aliss <[email protected]>
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 // FIXME: don't actually know what is correct here.
377 // Can a sub-format refer to any argument, or just
378 // the single argument passed to it? Must test
379 // against JDK.
380 formatter.format(thisArg, buf, ignore);
381 MessageFormat mf = new MessageFormat ();
382 mf.setLocale(locale);
383 mf.applyPattern(buf.toString());
384 formatter = mf;
385 }
386 formatter.format(thisArg, appendBuf, ignore);
387 }
388
389 appendBuf.append(elements[i].trailer);
390 }
391
392 return appendBuf;
393 }
394
395 /**
396 * Returns the pattern with the formatted objects.
397 *
398 * @param source The object to be formatted.
399 * @param result The StringBuffer where the text is appened.
400 * @param fp A FieldPosition object (it is ignored).
401 */
402 public final StringBuffer format (Object singleArg, StringBuffer appendBuf,
403 FieldPosition ignore)
404 {
405 Object[] args;
406
407 if (singleArg instanceof Object[])
408 {
409 // This isn't specified in any manual, but it follows the
410 // JDK implementation.
411 args = (Object[]) singleArg;
412 }
413 else
414 {
415 args = new Object[1];
416 args[0] = singleArg;
417 }
418 return format (args, appendBuf, ignore);
419 }
420
421 /**
422 * Returns an array with the Formats for
423 * the arguments.
424 */
425 public Format[] getFormats ()
426 {
427 Format[] f = new Format[elements.length];
428 for (int i = elements.length - 1; i >= 0; --i)
429 f[i] = elements[i].setFormat;
430 return f;
431 }
432
433 /**
434 * Returns the locale.
435 */
436 public Locale getLocale ()
437 {
438 return locale;
439 }
440
441 /**
442 * Overrides Format.hashCode()
443 */
444 public int hashCode ()
445 {
446 // FIXME: not a very good hash.
447 return pattern.hashCode() + locale.hashCode();
448 }
449
450 private MessageFormat ()
451 {
452 }
453
454 /**
455 * Creates a new MessageFormat object with
456 * the specified pattern
457 *
458 * @param aPattern The Pattern
459 */
460 public MessageFormat (String pattern)
461 {
462 locale = Locale.getDefault();
463 applyPattern (pattern);
464 }
465
466 public Object[] parse (String sourceStr, ParsePosition pos)
467 {
468 // Check initial text.
469 int index = pos.getIndex();
470 if (! sourceStr.startsWith(leader, index))
471 {
472 pos.setErrorIndex(index);
473 return null;
474 }
475 index += leader.length();
476
477 Vector results = new Vector (elements.length, 1);
478 // Now check each format.
479 for (int i = 0; i < elements.length; ++i)
480 {
481 Format formatter = null;
482 if (elements[i].setFormat != null)
483 formatter = elements[i].setFormat;
484 else if (elements[i].format != null)
485 formatter = elements[i].format;
486
487 Object value = null;
488 if (formatter instanceof ChoiceFormat)
489 {
490 // We must special-case a ChoiceFormat because it might
491 // have recursive formatting.
492 ChoiceFormat cf = (ChoiceFormat) formatter;
493 String[] formats = (String[]) cf.getFormats();
494 double[] limits = (double[]) cf.getLimits();
495 MessageFormat subfmt = new MessageFormat ();
496 subfmt.setLocale(locale);
497 ParsePosition subpos = new ParsePosition (index);
498
499 int j;
500 for (j = 0; value == null && j < limits.length; ++j)
501 {
502 subfmt.applyPattern(formats[j]);
503 subpos.setIndex(index);
504 value = subfmt.parse(sourceStr, subpos);
505 }
506 if (value != null)
507 {
508 index = subpos.getIndex();
509 value = new Double (limits[j]);
510 }
511 }
512 else if (formatter != null)
513 {
514 pos.setIndex(index);
515 value = formatter.parseObject(sourceStr, pos);
516 if (value != null)
517 index = pos.getIndex();
518 }
519 else
520 {
521 // We have a String format. This can lose in a number
522 // of ways, but we give it a shot.
523 int next_index = sourceStr.indexOf(elements[i].trailer, index);
524 if (next_index == -1)
525 {
526 pos.setErrorIndex(index);
527 return null;
528 }
529 value = sourceStr.substring(index, next_index);
530 index = next_index;
531 }
532
533 if (value == null
534 || ! sourceStr.startsWith(elements[i].trailer, index))
535 {
536 pos.setErrorIndex(index);
537 return null;
538 }
539
540 if (elements[i].argNumber >= results.size())
541 results.setSize(elements[i].argNumber + 1);
542 results.setElementAt(value, elements[i].argNumber);
543
544 index += elements[i].trailer.length();
545 }
546
547 Object[] r = new Object[results.size()];
548 results.copyInto(r);
549 return r;
550 }
551
552 public Object[] parse (String sourceStr) throws ParseException
553 {
554 ParsePosition pp = new ParsePosition (0);
555 Object[] r = parse (sourceStr, pp);
556 if (r == null)
557 throw new ParseException ("couldn't parse string", pp.getErrorIndex());
558 return r;
559 }
560
561 public Object parseObject (String sourceStr, ParsePosition pos)
562 {
563 return parse (sourceStr, pos);
564 }
565
566 /**
567 * Sets the format for the argument at an specified
568 * index.
569 *
570 * @param index The index.
571 * @format The Format object.
572 */
573 public void setFormat (int variableNum, Format newFormat)
574 {
575 elements[variableNum].setFormat = newFormat;
576 }
577
578 /**
579 * Sets the formats for the arguments.
580 *
581 * @param formats An array of Format objects.
582 */
583 public void setFormats (Format[] newFormats)
584 {
585 if (newFormats.length < elements.length)
586 throw new IllegalArgumentException ();
587 int len = Math.min(newFormats.length, elements.length);
588 for (int i = 0; i < len; ++i)
589 elements[i].setFormat = newFormats[i];
590 }
591
592 /**
593 * Sets the locale.
594 *
595 * @param locale A Locale
596 */
597 public void setLocale (Locale loc)
598 {
599 locale = loc;
600 if (elements != null)
601 {
602 for (int i = 0; i < elements.length; ++i)
603 elements[i].setLocale(loc);
604 }
605 }
606
607 /**
608 * Returns the pattern.
609 */
610 public String toPattern ()
611 {
612 return pattern;
613 }
614
615 // The pattern string.
616 private String pattern;
617 // The locale.
618 private Locale locale;
619 // Variables.
620 private MessageFormatElement[] elements;
621 // Leader text.
622 private String leader;
623}
Note: See TracBrowser for help on using the repository browser.