OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / text / ChoiceFormat.java
1 /* ChoiceFormat.java -- Format over a range of numbers
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
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.
11  
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.
16
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., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
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.
26
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. */
38
39
40 package java.text;
41
42 import gnu.java.lang.CPStringBuilder;
43
44 import java.util.Vector;
45
46 /**
47  * This class allows a format to be specified based on a range of numbers.
48  * To use this class, first specify two lists of formats and range terminators.
49  * These lists must be arrays of equal length.  The format of index 
50  * <code>i</code> will be selected for value <code>X</code> if 
51  * <code>terminator[i] &lt;= X &lt; limit[i + 1]</code>.  If the value X is not
52  * included in any range, then either the first or last format will be 
53  * used depending on whether the value X falls outside the range.
54  * <p>
55  * This sounds complicated, but that is because I did a poor job of
56  * explaining it.  Consider the following example:
57  * <p>
58  *
59 <pre>terminators = { 1, ChoiceFormat.nextDouble(1) }
60 formats = { "file", "files" }</pre>
61  *
62  * <p>
63  * In this case if the actual number tested is one or less, then the word
64  * "file" is used as the format value.  If the number tested is greater than
65  * one, then "files" is used.  This allows plurals to be handled
66  * gracefully.  Note the use of the method <code>nextDouble</code>.  This
67  * method selects the next highest double number than its argument.  This
68  * effectively makes any double greater than 1.0 cause the "files" string
69  * to be selected.  (Note that all terminator values are specified as
70  * doubles.
71  * <p>
72  * Note that in order for this class to work properly, the range terminator
73  * array must be sorted in ascending order and the format string array
74  * must be the same length as the terminator array.
75  *
76  * @author Tom Tromey (tromey@cygnus.com)
77  * @author Aaron M. Renn (arenn@urbanophile.com)
78  * @date March 9, 1999
79  */
80 /* Written using "Java Class Libraries", 2nd edition, plus online
81  * API docs for JDK 1.2 from http://www.javasoft.com.
82  * Status:  Believed complete and correct to 1.1.
83  */
84 public class ChoiceFormat extends NumberFormat
85 {
86   /**
87    * This method sets new range terminators and format strings for this
88    * object based on the specified pattern. This pattern is of the form 
89    * "term#string|term#string...".  For example "1#Sunday|2#Monday|#Tuesday".
90    *
91    * @param newPattern The pattern of terminators and format strings.
92    *
93    * @exception IllegalArgumentException If the pattern is not valid
94    */
95   public void applyPattern (String newPattern)
96   {
97     // Note: we assume the same kind of quoting rules apply here.
98     // This isn't explicitly documented.  But for instance we accept
99     // '#' as a literal hash in a format string.
100     int index = 0, max = newPattern.length();
101     Vector stringVec = new Vector ();
102     Vector limitVec = new Vector ();
103     final CPStringBuilder buf = new CPStringBuilder ();
104
105     while (true)
106       {
107         // Find end of double.
108         int dstart = index;
109         while (index < max)
110           {
111             char c = newPattern.charAt(index);
112             if (c == '#' || c == '\u2064' || c == '<')
113               break;
114             ++index;
115           }
116         
117         if (index == max)
118           throw new IllegalArgumentException ("unexpected end of text");
119         Double d = Double.valueOf (newPattern.substring(dstart, index));
120
121         if (newPattern.charAt(index) == '<')
122           d = Double.valueOf (nextDouble (d.doubleValue()));
123
124         limitVec.addElement(d);
125
126         // Scan text.
127         ++index;
128         buf.setLength(0);
129         while (index < max)
130           {
131             char c = newPattern.charAt(index);
132             if (c == '\'' && index < max + 1
133                 && newPattern.charAt(index + 1) == '\'')
134               {
135                 buf.append(c);
136                 ++index;
137               }
138             else if (c == '\'' && index < max + 2)
139               {
140                 buf.append(newPattern.charAt(index + 1));
141                 index += 2;
142               }
143             else if (c == '|')
144               break;
145             else
146               buf.append(c);
147             ++index;
148           }
149
150         stringVec.addElement(buf.toString());
151         if (index == max)
152           break;
153         ++index;
154       }
155
156     choiceFormats = new String[stringVec.size()];
157     stringVec.copyInto(choiceFormats);
158
159     choiceLimits = new double[limitVec.size()];
160     for (int i = 0; i < choiceLimits.length; ++i)
161       {
162         Double d = (Double) limitVec.elementAt(i);
163         choiceLimits[i] = d.doubleValue();
164       }
165   }
166
167   /**
168    * This method initializes a new instance of <code>ChoiceFormat</code> that
169    * generates its range terminator and format string arrays from the
170    * specified pattern.  This pattern is of the form 
171    * "term#string|term#string...".  For example "1#Sunday|2#Monday|#Tuesday".
172    * This is the same pattern type used by the <code>applyPattern</code>
173    * method.
174    *
175    * @param newPattern The pattern of terminators and format strings.
176    *
177    * @exception IllegalArgumentException If the pattern is not valid
178    */
179   public ChoiceFormat (String newPattern)
180   {
181     super ();
182     applyPattern (newPattern);
183   }
184
185   /**
186    * This method initializes a new instance of <code>ChoiceFormat</code> that
187    * will use the specified range terminators and format strings.
188    *
189    * @param choiceLimits The array of range terminators
190    * @param choiceFormats The array of format strings
191    */
192   public ChoiceFormat (double[] choiceLimits, String[] choiceFormats)
193   {
194     super ();
195     setChoices (choiceLimits, choiceFormats);
196   }
197
198   /**
199    * This method tests this object for equality with the specified 
200    * object.  This will be true if and only if:
201    * <ul>
202    * <li>The specified object is not <code>null</code>.</li>
203    * <li>The specified object is an instance of <code>ChoiceFormat</code>.</li>
204    * <li>The termination ranges and format strings are identical to
205    *     this object's. </li>
206    * </ul>
207    *
208    * @param obj The object to test for equality against.
209    *
210    * @return <code>true</code> if the specified object is equal to
211    * this one, <code>false</code> otherwise. 
212    */
213   public boolean equals (Object obj)
214   {
215     if (! (obj instanceof ChoiceFormat))
216       return false;
217     ChoiceFormat cf = (ChoiceFormat) obj;
218     if (choiceLimits.length != cf.choiceLimits.length)
219       return false;
220     for (int i = choiceLimits.length - 1; i >= 0; --i)
221       {
222         if (choiceLimits[i] != cf.choiceLimits[i]
223             || !choiceFormats[i].equals(cf.choiceFormats[i]))
224           return false;
225       }
226     return true;
227   }
228
229   /**
230    * This method appends the appropriate format string to the specified
231    * <code>StringBuffer</code> based on the supplied <code>long</code>
232    * argument.
233    *
234    * @param num The number used for determine (based on the range
235    *               terminators) which format string to append. 
236    * @param appendBuf The <code>StringBuffer</code> to append the format string 
237    *                  to.
238    * @param pos Unused.
239    *
240    * @return The <code>StringBuffer</code> with the format string appended.
241    */
242   public StringBuffer format (long num, StringBuffer appendBuf,
243                               FieldPosition pos)
244   {
245     return format ((double) num, appendBuf, pos);
246   }
247
248   /**
249    * This method appends the appropriate format string to the specified
250    * <code>StringBuffer</code> based on the supplied <code>double</code>
251    * argument.
252    *
253    * @param num The number used for determine (based on the range
254    *               terminators) which format string to append. 
255    * @param appendBuf The <code>StringBuffer</code> to append the format string to.
256    * @param pos Unused.
257    *
258    * @return The <code>StringBuffer</code> with the format string appended.
259    */
260   public StringBuffer format (double num, StringBuffer appendBuf,
261                               FieldPosition pos)
262   {
263     if (choiceLimits.length == 0)
264       return appendBuf;
265
266     int index = 0;
267     if (! Double.isNaN(num) && num >= choiceLimits[0])
268       {
269         for (; index < choiceLimits.length - 1; ++index)
270           {
271             if (choiceLimits[index] <= num && num < choiceLimits[index + 1])
272               break;
273           }
274       }
275
276     return appendBuf.append(choiceFormats[index]);
277   }
278
279   /**
280    * This method returns the list of format strings in use.
281    *
282    * @return The list of format objects.
283    */
284   public Object[] getFormats ()
285   {
286     return (Object[]) choiceFormats.clone();
287   }
288
289   /**
290    * This method returns the list of range terminators in use.
291    *
292    * @return The list of range terminators.
293    */
294   public double[] getLimits ()
295   {
296     return (double[]) choiceLimits.clone();
297   }
298
299   /**
300    * This method returns a hash value for this object
301    * 
302    * @return A hash value for this object.
303    */
304   public int hashCode ()
305   {
306     int hash = 0;
307     for (int i = 0; i < choiceLimits.length; ++i)
308       {
309         long v = Double.doubleToLongBits(choiceLimits[i]);
310         hash ^= (v ^ (v >>> 32));
311         hash ^= choiceFormats[i].hashCode();
312       }
313     return hash;
314   }
315
316   /**
317    * This method returns the lowest possible double greater than the 
318    * specified double.  If the specified double value is equal to
319    * <code>Double.NaN</code> then that is the value returned.
320    *
321    * @param d The specified double
322    *
323    * @return The lowest double value greater than the specified double.
324    */
325   public static final double nextDouble (double d)
326   {
327     return nextDouble (d, true);
328   }
329
330   /**
331    * This method returns a double that is either the next highest double
332    * or next lowest double compared to the specified double depending on the
333    * value of the passed boolean parameter.  If the boolean parameter is
334    * <code>true</code>, then the lowest possible double greater than the 
335    * specified double will be returned.  Otherwise the highest possible
336    * double less than the specified double will be returned.
337    *
338    * @param d The specified double
339    * @param next <code>true</code> to return the next highest
340    *                 double, <code>false</code> otherwise. 
341    *
342    * @return The next highest or lowest double value.
343    */
344   public static double nextDouble (double d, boolean next)
345   {
346     if (Double.isInfinite(d) || Double.isNaN(d))
347       return d;
348
349     long bits = Double.doubleToLongBits(d);
350
351     long mantMask = (1L << mantissaBits) - 1;
352     long mantissa = bits & mantMask;
353
354     long expMask = (1L << exponentBits) - 1;
355     long exponent = (bits >>> mantissaBits) & expMask;
356
357     if (next ^ (bits < 0)) // Increment magnitude
358       {
359         if (mantissa == (1L << mantissaBits) - 1)
360           {
361             mantissa = 0L;
362             exponent++;
363              
364             // Check for absolute overflow.
365             if (exponent >= (1L << mantissaBits))
366               return (bits > 0) ? Double.POSITIVE_INFINITY 
367                 : Double.NEGATIVE_INFINITY;                   
368           }
369         else
370           mantissa++;
371       }
372     else // Decrement magnitude
373       {
374         if (exponent == 0L && mantissa == 0L)
375           {
376             // The only case where there is a change of sign
377             return next ? Double.MIN_VALUE : -Double.MIN_VALUE;
378           }
379         else
380           {
381             if (mantissa == 0L)
382               {
383                 mantissa = (1L << mantissaBits) - 1;
384                 exponent--;
385               }
386             else
387               mantissa--;
388           }
389       }
390
391     long result = bits < 0 ? 1 : 0;
392     result = (result << exponentBits) | exponent;
393     result = (result << mantissaBits) | mantissa;
394     return Double.longBitsToDouble(result);
395   }
396
397   /**
398    * I'm not sure what this method is really supposed to do, as it is
399    * not documented.
400    */
401   public Number parse (String sourceStr, ParsePosition pos)
402   {
403     int index = pos.getIndex();
404     for (int i = 0; i < choiceLimits.length; ++i)
405       {
406         if (sourceStr.startsWith(choiceFormats[i], index))
407           {
408             pos.setIndex(index + choiceFormats[i].length());
409             return Double.valueOf (choiceLimits[i]);
410           }
411       }
412     pos.setErrorIndex(index);
413     return Double.valueOf (Double.NaN);
414   }
415
416   /**
417    * This method returns the highest possible double less than the 
418    * specified double.  If the specified double value is equal to
419    * <code>Double.NaN</code> then that is the value returned.
420    *
421    * @param d The specified double
422    *
423    * @return The highest double value less than the specified double.
424    */
425   public static final double previousDouble (double d)
426   {
427     return nextDouble (d, false);
428   }
429
430   /**
431    * This method sets new range terminators and format strings for this
432    * object.
433    *
434    * @param choiceLimits The new range terminators
435    * @param choiceFormats The new choice formats
436    */
437   public void setChoices (double[] choiceLimits, String[] choiceFormats)
438   {
439     if (choiceLimits == null || choiceFormats == null)
440       throw new NullPointerException ();
441     if (choiceLimits.length != choiceFormats.length)
442       throw new IllegalArgumentException ();
443     this.choiceFormats = (String[]) choiceFormats.clone();
444     this.choiceLimits = (double[]) choiceLimits.clone();
445   }
446
447   private void quoteString (CPStringBuilder dest, String text)
448   {
449     int max = text.length();
450     for (int i = 0; i < max; ++i)
451       {
452         char c = text.charAt(i);
453         if (c == '\'')
454           {
455             dest.append(c);
456             dest.append(c);
457           }
458         else if (c == '#' || c == '|' || c == '\u2064' || c == '<')
459           {
460             dest.append('\'');
461             dest.append(c);
462             dest.append('\'');
463           }
464         else
465           dest.append(c);
466       }
467   }
468
469   /**
470    * This method returns the range terminator list and format string list
471    * as a <code>String</code> suitable for using with the 
472    * <code>applyPattern</code> method.
473    *
474    * @return A pattern string for this object
475    */
476   public String toPattern ()
477   {
478     CPStringBuilder result = new CPStringBuilder ();
479     for (int i = 0; i < choiceLimits.length; ++i)
480       {
481         result.append(choiceLimits[i]);
482         result.append('#');
483         quoteString (result, choiceFormats[i]);
484       }
485     return result.toString();
486   }
487
488   /**
489    * This is the list of format strings.  Note that this variable is
490    * specified by the serialization spec of this class.
491    */
492   private String[] choiceFormats;
493
494   /**
495    * This is the list of range terminator values.  Note that this variable is
496    * specified by the serialization spec of this class.
497    */
498   private double[] choiceLimits;
499
500   // Number of mantissa bits in double.
501   private static final int mantissaBits = 52;
502   // Number of exponent bits in a double.
503   private static final int exponentBits = 11;
504
505   private static final long serialVersionUID = 1795184449645032964L;
506 }