ChoiceFormat.java 16 KB

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