Frames | No Frames |
1: /* ChoiceFormat.java -- Format over a range of numbers 2: Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2012 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] <= X < 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<String> stringVec = new Vector<String> (); 102: Vector<Double> limitVec = new Vector<Double> (); 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 = 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: }