GNU Classpath (0.17) | ||
Frames | No Frames |
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 java.util.Vector; 43: 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] <= X < 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> 56: * 57: <pre>terminators = { 1, ChoiceFormat.nextDouble(1) } 58: formats = { "file", "files" }</pre> 59: * 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. 73: * 74: * @author Tom Tromey (tromey@cygnus.com) 75: * @author Aaron M. Renn (arenn@urbanophile.com) 76: * @date March 9, 1999 77: */ 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. 81: */ 82: public class ChoiceFormat extends NumberFormat 83: { 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". 88: * 89: * @param pattern The pattern of terminators and format strings. 90: * 91: * @exception IllegalArgumentException If the pattern is not valid 92: */ 93: public void applyPattern (String newPattern) 94: { 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 (); 102: 103: while (true) 104: { 105: // Find end of double. 106: int dstart = index; 107: while (index < max) 108: { 109: char c = newPattern.charAt(index); 110: if (c == '#' || c == '\u2064' || c == '<') 111: break; 112: ++index; 113: } 114: 115: if (index == max) 116: throw new IllegalArgumentException ("unexpected end of text"); 117: Double d = new Double (newPattern.substring(dstart, index)); 118: 119: if (newPattern.charAt(index) == '<') 120: d = new Double (nextDouble (d.doubleValue())); 121: 122: limitVec.addElement(d); 123: 124: // Scan text. 125: ++index; 126: buf.setLength(0); 127: while (index < max) 128: { 129: char c = newPattern.charAt(index); 130: if (c == '\'' && index < max + 1 131: && newPattern.charAt(index + 1) == '\'') 132: { 133: buf.append(c); 134: ++index; 135: } 136: else if (c == '\'' && index < max + 2) 137: { 138: buf.append(newPattern.charAt(index + 1)); 139: index += 2; 140: } 141: else if (c == '|') 142: break; 143: else 144: buf.append(c); 145: ++index; 146: } 147: 148: stringVec.addElement(buf.toString()); 149: if (index == max) 150: break; 151: ++index; 152: } 153: 154: choiceFormats = new String[stringVec.size()]; 155: stringVec.copyInto(choiceFormats); 156: 157: choiceLimits = new double[limitVec.size()]; 158: for (int i = 0; i < choiceLimits.length; ++i) 159: { 160: Double d = (Double) limitVec.elementAt(i); 161: choiceLimits[i] = d.doubleValue(); 162: } 163: } 164: 165: /** 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. 172: * 173: * @param pattern The pattern of terminators and format strings. 174: * 175: * @exception IllegalArgumentException If the pattern is not valid 176: */ 177: public ChoiceFormat (String newPattern) 178: { 179: super (); 180: applyPattern (newPattern); 181: } 182: 183: /** 184: * This method initializes a new instance of <code>ChoiceFormat</code> that 185: * will use the specified range terminators and format strings. 186: * 187: * @param choiceLimits The array of range terminators 188: * @param choiceFormats The array of format strings 189: */ 190: public ChoiceFormat (double[] choiceLimits, String[] choiceFormats) 191: { 192: super (); 193: setChoices (choiceLimits, choiceFormats); 194: } 195: 196: /** 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> 205: * 206: * @param obj The object to test for equality against. 207: * 208: * @return <code>true</code> if the specified object is equal to 209: * this one, <code>false</code> otherwise. 210: */ 211: public boolean equals (Object obj) 212: { 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) 219: { 220: if (choiceLimits[i] != cf.choiceLimits[i] 221: || !choiceFormats[i].equals(cf.choiceFormats[i])) 222: return false; 223: } 224: return true; 225: } 226: 227: /** 228: * This method appends the appropriate format string to the specified 229: * <code>StringBuffer</code> based on the supplied <code>long</code> 230: * argument. 231: * 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. 236: * 237: * @return The <code>StringBuffer</code> with the format string appended. 238: */ 239: public StringBuffer format (long num, StringBuffer appendBuf, 240: FieldPosition pos) 241: { 242: return format ((double) num, appendBuf, pos); 243: } 244: 245: /** 246: * This method appends the appropriate format string to the specified 247: * <code>StringBuffer</code> based on the supplied <code>double</code> 248: * argument. 249: * 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. 254: * 255: * @return The <code>StringBuffer</code> with the format string appended. 256: */ 257: public StringBuffer format (double num, StringBuffer appendBuf, 258: FieldPosition pos) 259: { 260: if (choiceLimits.length == 0) 261: return appendBuf; 262: 263: int index = 0; 264: if (! Double.isNaN(num) && num >= choiceLimits[0]) 265: { 266: for (; index < choiceLimits.length - 1; ++index) 267: { 268: if (choiceLimits[index] <= num && num < choiceLimits[index + 1]) 269: break; 270: } 271: } 272: 273: return appendBuf.append(choiceFormats[index]); 274: } 275: 276: /** 277: * This method returns the list of format strings in use. 278: * 279: * @return The list of format objects. 280: */ 281: public Object[] getFormats () 282: { 283: return (Object[]) choiceFormats.clone(); 284: } 285: 286: /** 287: * This method returns the list of range terminators in use. 288: * 289: * @return The list of range terminators. 290: */ 291: public double[] getLimits () 292: { 293: return (double[]) choiceLimits.clone(); 294: } 295: 296: /** 297: * This method returns a hash value for this object 298: * 299: * @return A hash value for this object. 300: */ 301: public int hashCode () 302: { 303: int hash = 0; 304: for (int i = 0; i < choiceLimits.length; ++i) 305: { 306: long v = Double.doubleToLongBits(choiceLimits[i]); 307: hash ^= (v ^ (v >>> 32)); 308: hash ^= choiceFormats[i].hashCode(); 309: } 310: return hash; 311: } 312: 313: /** 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. 317: * 318: * @param d The specified double 319: * 320: * @return The lowest double value greater than the specified double. 321: */ 322: public static final double nextDouble (double d) 323: { 324: return nextDouble (d, true); 325: } 326: 327: /** 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. 334: * 335: * @param d The specified double 336: * @param positive <code>true</code> to return the next highest 337: * double, <code>false</code> otherwise. 338: * 339: * @return The next highest or lowest double value. 340: */ 341: public static double nextDouble (double d, boolean next) 342: { 343: if (Double.isInfinite(d) || Double.isNaN(d)) 344: return d; 345: 346: long bits = Double.doubleToLongBits(d); 347: 348: long mantMask = (1L << mantissaBits) - 1; 349: long mantissa = bits & mantMask; 350: 351: long expMask = (1L << exponentBits) - 1; 352: long exponent = (bits >>> mantissaBits) & expMask; 353: 354: if (next ^ (bits < 0)) // Increment magnitude 355: { 356: if (mantissa == (1L << mantissaBits) - 1) 357: { 358: mantissa = 0L; 359: exponent++; 360: 361: // Check for absolute overflow. 362: if (exponent >= (1L << mantissaBits)) 363: return (bits > 0) ? Double.POSITIVE_INFINITY 364: : Double.NEGATIVE_INFINITY; 365: } 366: else 367: mantissa++; 368: } 369: else // Decrement magnitude 370: { 371: if (exponent == 0L && mantissa == 0L) 372: { 373: // The only case where there is a change of sign 374: return next ? Double.MIN_VALUE : -Double.MIN_VALUE; 375: } 376: else 377: { 378: if (mantissa == 0L) 379: { 380: mantissa = (1L << mantissaBits) - 1; 381: exponent--; 382: } 383: else 384: mantissa--; 385: } 386: } 387: 388: long result = bits < 0 ? 1 : 0; 389: result = (result << exponentBits) | exponent; 390: result = (result << mantissaBits) | mantissa; 391: return Double.longBitsToDouble(result); 392: } 393: 394: /** 395: * I'm not sure what this method is really supposed to do, as it is 396: * not documented. 397: */ 398: public Number parse (String sourceStr, ParsePosition pos) 399: { 400: int index = pos.getIndex(); 401: for (int i = 0; i < choiceLimits.length; ++i) 402: { 403: if (sourceStr.startsWith(choiceFormats[i], index)) 404: { 405: pos.setIndex(index + choiceFormats[i].length()); 406: return new Double (choiceLimits[i]); 407: } 408: } 409: pos.setErrorIndex(index); 410: return new Double (Double.NaN); 411: } 412: 413: /** 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. 417: * 418: * @param d The specified double 419: * 420: * @return The highest double value less than the specified double. 421: */ 422: public static final double previousDouble (double d) 423: { 424: return nextDouble (d, false); 425: } 426: 427: /** 428: * This method sets new range terminators and format strings for this 429: * object. 430: * 431: * @param choiceLimits The new range terminators 432: * @param choiceFormats The new choice formats 433: */ 434: public void setChoices (double[] choiceLimits, String[] choiceFormats) 435: { 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(); 442: } 443: 444: private void quoteString (StringBuffer dest, String text) 445: { 446: int max = text.length(); 447: for (int i = 0; i < max; ++i) 448: { 449: char c = text.charAt(i); 450: if (c == '\'') 451: { 452: dest.append(c); 453: dest.append(c); 454: } 455: else if (c == '#' || c == '|' || c == '\u2064' || c == '<') 456: { 457: dest.append('\''); 458: dest.append(c); 459: dest.append('\''); 460: } 461: else 462: dest.append(c); 463: } 464: } 465: 466: /** 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. 470: * 471: * @return A pattern string for this object 472: */ 473: public String toPattern () 474: { 475: StringBuffer result = new StringBuffer (); 476: for (int i = 0; i < choiceLimits.length; ++i) 477: { 478: result.append(choiceLimits[i]); 479: result.append('#'); 480: quoteString (result, choiceFormats[i]); 481: } 482: return result.toString(); 483: } 484: 485: /** 486: * This is the list of format strings. Note that this variable is 487: * specified by the serialization spec of this class. 488: */ 489: private String[] choiceFormats; 490: 491: /** 492: * This is the list of range terminator values. Note that this variable is 493: * specified by the serialization spec of this class. 494: */ 495: private double[] choiceLimits; 496: 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; 501: 502: private static final long serialVersionUID = 1795184449645032964L; 503: }
GNU Classpath (0.17) |