GNU Classpath (0.95) | |
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 newPattern 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 newPattern 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 num The number used for determine (based on the range 233: * terminators) which format string to append. 234: * @param appendBuf The <code>StringBuffer</code> to append the format string 235: * to. 236: * @param pos Unused. 237: * 238: * @return The <code>StringBuffer</code> with the format string appended. 239: */ 240: public StringBuffer format (long num, StringBuffer appendBuf, 241: FieldPosition pos) 242: { 243: return format ((double) num, appendBuf, pos); 244: } 245: 246: /** 247: * This method appends the appropriate format string to the specified 248: * <code>StringBuffer</code> based on the supplied <code>double</code> 249: * argument. 250: * 251: * @param num The number used for determine (based on the range 252: * terminators) which format string to append. 253: * @param appendBuf The <code>StringBuffer</code> to append the format string to. 254: * @param pos Unused. 255: * 256: * @return The <code>StringBuffer</code> with the format string appended. 257: */ 258: public StringBuffer format (double num, StringBuffer appendBuf, 259: FieldPosition pos) 260: { 261: if (choiceLimits.length == 0) 262: return appendBuf; 263: 264: int index = 0; 265: if (! Double.isNaN(num) && num >= choiceLimits[0]) 266: { 267: for (; index < choiceLimits.length - 1; ++index) 268: { 269: if (choiceLimits[index] <= num && num < choiceLimits[index + 1]) 270: break; 271: } 272: } 273: 274: return appendBuf.append(choiceFormats[index]); 275: } 276: 277: /** 278: * This method returns the list of format strings in use. 279: * 280: * @return The list of format objects. 281: */ 282: public Object[] getFormats () 283: { 284: return (Object[]) choiceFormats.clone(); 285: } 286: 287: /** 288: * This method returns the list of range terminators in use. 289: * 290: * @return The list of range terminators. 291: */ 292: public double[] getLimits () 293: { 294: return (double[]) choiceLimits.clone(); 295: } 296: 297: /** 298: * This method returns a hash value for this object 299: * 300: * @return A hash value for this object. 301: */ 302: public int hashCode () 303: { 304: int hash = 0; 305: for (int i = 0; i < choiceLimits.length; ++i) 306: { 307: long v = Double.doubleToLongBits(choiceLimits[i]); 308: hash ^= (v ^ (v >>> 32)); 309: hash ^= choiceFormats[i].hashCode(); 310: } 311: return hash; 312: } 313: 314: /** 315: * This method returns the lowest possible double greater than the 316: * specified double. If the specified double value is equal to 317: * <code>Double.NaN</code> then that is the value returned. 318: * 319: * @param d The specified double 320: * 321: * @return The lowest double value greater than the specified double. 322: */ 323: public static final double nextDouble (double d) 324: { 325: return nextDouble (d, true); 326: } 327: 328: /** 329: * This method returns a double that is either the next highest double 330: * or next lowest double compared to the specified double depending on the 331: * value of the passed boolean parameter. If the boolean parameter is 332: * <code>true</code>, then the lowest possible double greater than the 333: * specified double will be returned. Otherwise the highest possible 334: * double less than the specified double will be returned. 335: * 336: * @param d The specified double 337: * @param next <code>true</code> to return the next highest 338: * double, <code>false</code> otherwise. 339: * 340: * @return The next highest or lowest double value. 341: */ 342: public static double nextDouble (double d, boolean next) 343: { 344: if (Double.isInfinite(d) || Double.isNaN(d)) 345: return d; 346: 347: long bits = Double.doubleToLongBits(d); 348: 349: long mantMask = (1L << mantissaBits) - 1; 350: long mantissa = bits & mantMask; 351: 352: long expMask = (1L << exponentBits) - 1; 353: long exponent = (bits >>> mantissaBits) & expMask; 354: 355: if (next ^ (bits < 0)) // Increment magnitude 356: { 357: if (mantissa == (1L << mantissaBits) - 1) 358: { 359: mantissa = 0L; 360: exponent++; 361: 362: // Check for absolute overflow. 363: if (exponent >= (1L << mantissaBits)) 364: return (bits > 0) ? Double.POSITIVE_INFINITY 365: : Double.NEGATIVE_INFINITY; 366: } 367: else 368: mantissa++; 369: } 370: else // Decrement magnitude 371: { 372: if (exponent == 0L && mantissa == 0L) 373: { 374: // The only case where there is a change of sign 375: return next ? Double.MIN_VALUE : -Double.MIN_VALUE; 376: } 377: else 378: { 379: if (mantissa == 0L) 380: { 381: mantissa = (1L << mantissaBits) - 1; 382: exponent--; 383: } 384: else 385: mantissa--; 386: } 387: } 388: 389: long result = bits < 0 ? 1 : 0; 390: result = (result << exponentBits) | exponent; 391: result = (result << mantissaBits) | mantissa; 392: return Double.longBitsToDouble(result); 393: } 394: 395: /** 396: * I'm not sure what this method is really supposed to do, as it is 397: * not documented. 398: */ 399: public Number parse (String sourceStr, ParsePosition pos) 400: { 401: int index = pos.getIndex(); 402: for (int i = 0; i < choiceLimits.length; ++i) 403: { 404: if (sourceStr.startsWith(choiceFormats[i], index)) 405: { 406: pos.setIndex(index + choiceFormats[i].length()); 407: return new Double (choiceLimits[i]); 408: } 409: } 410: pos.setErrorIndex(index); 411: return new Double (Double.NaN); 412: } 413: 414: /** 415: * This method returns the highest possible double less than the 416: * specified double. If the specified double value is equal to 417: * <code>Double.NaN</code> then that is the value returned. 418: * 419: * @param d The specified double 420: * 421: * @return The highest double value less than the specified double. 422: */ 423: public static final double previousDouble (double d) 424: { 425: return nextDouble (d, false); 426: } 427: 428: /** 429: * This method sets new range terminators and format strings for this 430: * object. 431: * 432: * @param choiceLimits The new range terminators 433: * @param choiceFormats The new choice formats 434: */ 435: public void setChoices (double[] choiceLimits, String[] choiceFormats) 436: { 437: if (choiceLimits == null || choiceFormats == null) 438: throw new NullPointerException (); 439: if (choiceLimits.length != choiceFormats.length) 440: throw new IllegalArgumentException (); 441: this.choiceFormats = (String[]) choiceFormats.clone(); 442: this.choiceLimits = (double[]) choiceLimits.clone(); 443: } 444: 445: private void quoteString (StringBuffer dest, String text) 446: { 447: int max = text.length(); 448: for (int i = 0; i < max; ++i) 449: { 450: char c = text.charAt(i); 451: if (c == '\'') 452: { 453: dest.append(c); 454: dest.append(c); 455: } 456: else if (c == '#' || c == '|' || c == '\u2064' || c == '<') 457: { 458: dest.append('\''); 459: dest.append(c); 460: dest.append('\''); 461: } 462: else 463: dest.append(c); 464: } 465: } 466: 467: /** 468: * This method returns the range terminator list and format string list 469: * as a <code>String</code> suitable for using with the 470: * <code>applyPattern</code> method. 471: * 472: * @return A pattern string for this object 473: */ 474: public String toPattern () 475: { 476: StringBuffer result = new StringBuffer (); 477: for (int i = 0; i < choiceLimits.length; ++i) 478: { 479: result.append(choiceLimits[i]); 480: result.append('#'); 481: quoteString (result, choiceFormats[i]); 482: } 483: return result.toString(); 484: } 485: 486: /** 487: * This is the list of format strings. Note that this variable is 488: * specified by the serialization spec of this class. 489: */ 490: private String[] choiceFormats; 491: 492: /** 493: * This is the list of range terminator values. Note that this variable is 494: * specified by the serialization spec of this class. 495: */ 496: private double[] choiceLimits; 497: 498: // Number of mantissa bits in double. 499: private static final int mantissaBits = 52; 500: // Number of exponent bits in a double. 501: private static final int exponentBits = 11; 502: 503: private static final long serialVersionUID = 1795184449645032964L; 504: }
GNU Classpath (0.95) |