Merge from the pain train
[official-gcc.git] / libjava / java / text / ChoiceFormat.java
blob3c390278b88e2ee2b0658f85ca753191b67f39a4
1 /* ChoiceFormat.java -- Format over a range of numbers
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING. If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA.
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library. Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module. An independent module is a module which is not derived from
34 or based on this library. If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so. If you do not wish to do so, delete this
37 exception statement from your version. */
40 package java.text;
42 import java.util.Vector;
44 /**
45 * This class allows a format to be specified based on a range of numbers.
46 * To use this class, first specify two lists of formats and range terminators.
47 * These lists must be arrays of equal length. The format of index
48 * <code>i</code> will be selected for value <code>X</code> if
49 * <code>terminator[i] &lt;= X &lt; limit[i + 1]</code>. If the value X is not
50 * included in any range, then either the first or last format will be
51 * used depending on whether the value X falls outside the range.
52 * <p>
53 * This sounds complicated, but that is because I did a poor job of
54 * explaining it. Consider the following example:
55 * <p>
57 <pre>terminators = { 1, ChoiceFormat.nextDouble(1) }
58 formats = { "file", "files" }</pre>
60 * <p>
61 * In this case if the actual number tested is one or less, then the word
62 * "file" is used as the format value. If the number tested is greater than
63 * one, then "files" is used. This allows plurals to be handled
64 * gracefully. Note the use of the method <code>nextDouble</code>. This
65 * method selects the next highest double number than its argument. This
66 * effectively makes any double greater than 1.0 cause the "files" string
67 * to be selected. (Note that all terminator values are specified as
68 * doubles.
69 * <p>
70 * Note that in order for this class to work properly, the range terminator
71 * array must be sorted in ascending order and the format string array
72 * must be the same length as the terminator array.
74 * @author Tom Tromey (tromey@cygnus.com)
75 * @author Aaron M. Renn (arenn@urbanophile.com)
76 * @date March 9, 1999
78 /* Written using "Java Class Libraries", 2nd edition, plus online
79 * API docs for JDK 1.2 from http://www.javasoft.com.
80 * Status: Believed complete and correct to 1.1.
82 public class ChoiceFormat extends NumberFormat
84 /**
85 * This method sets new range terminators and format strings for this
86 * object based on the specified pattern. This pattern is of the form
87 * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday".
89 * @param pattern The pattern of terminators and format strings.
91 * @exception IllegalArgumentException If the pattern is not valid
93 public void applyPattern (String newPattern)
95 // Note: we assume the same kind of quoting rules apply here.
96 // This isn't explicitly documented. But for instance we accept
97 // '#' as a literal hash in a format string.
98 int index = 0, max = newPattern.length();
99 Vector stringVec = new Vector ();
100 Vector limitVec = new Vector ();
101 StringBuffer buf = new StringBuffer ();
103 while (true)
105 // Find end of double.
106 int dstart = index;
107 while (index < max)
109 char c = newPattern.charAt(index);
110 if (c == '#' || c == '\u2064' || c == '<')
111 break;
112 ++index;
115 if (index == max)
116 throw new IllegalArgumentException ("unexpected end of text");
117 Double d = new Double (newPattern.substring(dstart, index));
119 if (newPattern.charAt(index) == '<')
120 d = new Double (nextDouble (d.doubleValue()));
122 limitVec.addElement(d);
124 // Scan text.
125 ++index;
126 buf.setLength(0);
127 while (index < max)
129 char c = newPattern.charAt(index);
130 if (c == '\'' && index < max + 1
131 && newPattern.charAt(index + 1) == '\'')
133 buf.append(c);
134 ++index;
136 else if (c == '\'' && index < max + 2)
138 buf.append(newPattern.charAt(index + 1));
139 index += 2;
141 else if (c == '|')
142 break;
143 else
144 buf.append(c);
145 ++index;
148 stringVec.addElement(buf.toString());
149 if (index == max)
150 break;
151 ++index;
154 choiceFormats = new String[stringVec.size()];
155 stringVec.copyInto(choiceFormats);
157 choiceLimits = new double[limitVec.size()];
158 for (int i = 0; i < choiceLimits.length; ++i)
160 Double d = (Double) limitVec.elementAt(i);
161 choiceLimits[i] = d.doubleValue();
166 * This method initializes a new instance of <code>ChoiceFormat</code> that
167 * generates its range terminator and format string arrays from the
168 * specified pattern. This pattern is of the form
169 * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday".
170 * This is the same pattern type used by the <code>applyPattern</code>
171 * method.
173 * @param pattern The pattern of terminators and format strings.
175 * @exception IllegalArgumentException If the pattern is not valid
177 public ChoiceFormat (String newPattern)
179 super ();
180 applyPattern (newPattern);
184 * This method initializes a new instance of <code>ChoiceFormat</code> that
185 * will use the specified range terminators and format strings.
187 * @param choiceLimits The array of range terminators
188 * @param choiceFormats The array of format strings
190 public ChoiceFormat (double[] choiceLimits, String[] choiceFormats)
192 super ();
193 setChoices (choiceLimits, choiceFormats);
197 * This method tests this object for equality with the specified
198 * object. This will be true if and only if:
199 * <ul>
200 * <li>The specified object is not <code>null</code>.</li>
201 * <li>The specified object is an instance of <code>ChoiceFormat</code>.</li>
202 * <li>The termination ranges and format strings are identical to
203 * this object's. </li>
204 * </ul>
206 * @param obj The object to test for equality against.
208 * @return <code>true</code> if the specified object is equal to
209 * this one, <code>false</code> otherwise.
211 public boolean equals (Object obj)
213 if (! (obj instanceof ChoiceFormat))
214 return false;
215 ChoiceFormat cf = (ChoiceFormat) obj;
216 if (choiceLimits.length != cf.choiceLimits.length)
217 return false;
218 for (int i = choiceLimits.length - 1; i >= 0; --i)
220 if (choiceLimits[i] != cf.choiceLimits[i]
221 || !choiceFormats[i].equals(cf.choiceFormats[i]))
222 return false;
224 return true;
228 * This method appends the appropriate format string to the specified
229 * <code>StringBuffer</code> based on the supplied <code>long</code>
230 * argument.
232 * @param number The number used for determine (based on the range
233 * terminators) which format string to append.
234 * @param sb The <code>StringBuffer</code> to append the format string to.
235 * @param status Unused.
237 * @return The <code>StringBuffer</code> with the format string appended.
239 public StringBuffer format (long num, StringBuffer appendBuf,
240 FieldPosition pos)
242 return format ((double) num, appendBuf, pos);
246 * This method appends the appropriate format string to the specified
247 * <code>StringBuffer</code> based on the supplied <code>double</code>
248 * argument.
250 * @param number The number used for determine (based on the range
251 * terminators) which format string to append.
252 * @param sb The <code>StringBuffer</code> to append the format string to.
253 * @param status Unused.
255 * @return The <code>StringBuffer</code> with the format string appended.
257 public StringBuffer format (double num, StringBuffer appendBuf,
258 FieldPosition pos)
260 if (choiceLimits.length == 0)
261 return appendBuf;
263 int index = 0;
264 if (! Double.isNaN(num) && num >= choiceLimits[0])
266 for (; index < choiceLimits.length - 1; ++index)
268 if (choiceLimits[index] <= num && num < choiceLimits[index + 1])
269 break;
273 return appendBuf.append(choiceFormats[index]);
277 * This method returns the list of format strings in use.
279 * @return The list of format objects.
281 public Object[] getFormats ()
283 return (Object[]) choiceFormats.clone();
287 * This method returns the list of range terminators in use.
289 * @return The list of range terminators.
291 public double[] getLimits ()
293 return (double[]) choiceLimits.clone();
297 * This method returns a hash value for this object
299 * @return A hash value for this object.
301 public int hashCode ()
303 int hash = 0;
304 for (int i = 0; i < choiceLimits.length; ++i)
306 long v = Double.doubleToLongBits(choiceLimits[i]);
307 hash ^= (v ^ (v >>> 32));
308 hash ^= choiceFormats[i].hashCode();
310 return hash;
314 * This method returns the lowest possible double greater than the
315 * specified double. If the specified double value is equal to
316 * <code>Double.NaN</code> then that is the value returned.
318 * @param d The specified double
320 * @return The lowest double value greater than the specified double.
322 public static final double nextDouble (double d)
324 return nextDouble (d, true);
328 * This method returns a double that is either the next highest double
329 * or next lowest double compared to the specified double depending on the
330 * value of the passed boolean parameter. If the boolean parameter is
331 * <code>true</code>, then the lowest possible double greater than the
332 * specified double will be returned. Otherwise the highest possible
333 * double less than the specified double will be returned.
335 * @param d The specified double
336 * @param positive <code>true</code> to return the next highest
337 * double, <code>false</code> otherwise.
339 * @return The next highest or lowest double value.
341 public static double nextDouble (double d, boolean next)
343 if (Double.isInfinite(d) || Double.isNaN(d))
344 return d;
346 long bits = Double.doubleToLongBits(d);
348 long mantMask = (1L << mantissaBits) - 1;
349 long mantissa = bits & mantMask;
351 long expMask = (1L << exponentBits) - 1;
352 long exponent = (bits >>> mantissaBits) & expMask;
354 if (next ^ (bits < 0)) // Increment magnitude
356 if (mantissa == (1L << mantissaBits) - 1)
358 mantissa = 0L;
359 exponent++;
361 // Check for absolute overflow.
362 if (exponent >= (1L << mantissaBits))
363 return (bits > 0) ? Double.POSITIVE_INFINITY
364 : Double.NEGATIVE_INFINITY;
366 else
367 mantissa++;
369 else // Decrement magnitude
371 if (exponent == 0L && mantissa == 0L)
373 // The only case where there is a change of sign
374 return next ? Double.MIN_VALUE : -Double.MIN_VALUE;
376 else
378 if (mantissa == 0L)
380 mantissa = (1L << mantissaBits) - 1;
381 exponent--;
383 else
384 mantissa--;
388 long result = bits < 0 ? 1 : 0;
389 result = (result << exponentBits) | exponent;
390 result = (result << mantissaBits) | mantissa;
391 return Double.longBitsToDouble(result);
395 * I'm not sure what this method is really supposed to do, as it is
396 * not documented.
398 public Number parse (String sourceStr, ParsePosition pos)
400 int index = pos.getIndex();
401 for (int i = 0; i < choiceLimits.length; ++i)
403 if (sourceStr.startsWith(choiceFormats[i], index))
405 pos.setIndex(index + choiceFormats[i].length());
406 return new Double (choiceLimits[i]);
409 pos.setErrorIndex(index);
410 return new Double (Double.NaN);
414 * This method returns the highest possible double less than the
415 * specified double. If the specified double value is equal to
416 * <code>Double.NaN</code> then that is the value returned.
418 * @param d The specified double
420 * @return The highest double value less than the specified double.
422 public static final double previousDouble (double d)
424 return nextDouble (d, false);
428 * This method sets new range terminators and format strings for this
429 * object.
431 * @param choiceLimits The new range terminators
432 * @param choiceFormats The new choice formats
434 public void setChoices (double[] choiceLimits, String[] choiceFormats)
436 if (choiceLimits == null || choiceFormats == null)
437 throw new NullPointerException ();
438 if (choiceLimits.length != choiceFormats.length)
439 throw new IllegalArgumentException ();
440 this.choiceFormats = (String[]) choiceFormats.clone();
441 this.choiceLimits = (double[]) choiceLimits.clone();
444 private void quoteString (StringBuffer dest, String text)
446 int max = text.length();
447 for (int i = 0; i < max; ++i)
449 char c = text.charAt(i);
450 if (c == '\'')
452 dest.append(c);
453 dest.append(c);
455 else if (c == '#' || c == '|' || c == '\u2064' || c == '<')
457 dest.append('\'');
458 dest.append(c);
459 dest.append('\'');
461 else
462 dest.append(c);
467 * This method returns the range terminator list and format string list
468 * as a <code>String</code> suitable for using with the
469 * <code>applyPattern</code> method.
471 * @return A pattern string for this object
473 public String toPattern ()
475 StringBuffer result = new StringBuffer ();
476 for (int i = 0; i < choiceLimits.length; ++i)
478 result.append(choiceLimits[i]);
479 result.append('#');
480 quoteString (result, choiceFormats[i]);
482 return result.toString();
486 * This is the list of format strings. Note that this variable is
487 * specified by the serialization spec of this class.
489 private String[] choiceFormats;
492 * This is the list of range terminator values. Note that this variable is
493 * specified by the serialization spec of this class.
495 private double[] choiceLimits;
497 // Number of mantissa bits in double.
498 private static final int mantissaBits = 52;
499 // Number of exponent bits in a double.
500 private static final int exponentBits = 11;
502 private static final long serialVersionUID = 1795184449645032964L;