History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/text/ChoiceFormat.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/text/ChoiceFormat.java+619−0
@@ -0,0 +1,619 @@
1+/*
2+ * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+/*
27+ * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28+ * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29+ *
30+ * The original version of this source code and documentation is copyrighted
31+ * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32+ * materials are provided under terms of a License Agreement between Taligent
33+ * and Sun. This technology is protected by multiple US and International
34+ * patents. This notice and attribution to Taligent may not be removed.
35+ * Taligent is a registered trademark of Taligent, Inc.
36+ *
37+ */
38+
39+package java.text;
40+
41+import java.io.InvalidObjectException;
42+import java.io.IOException;
43+import java.io.ObjectInputStream;
44+import java.util.Arrays;
45+
46+/**
47+ * A <code>ChoiceFormat</code> allows you to attach a format to a range of numbers.
48+ * It is generally used in a <code>MessageFormat</code> for handling plurals.
49+ * The choice is specified with an ascending list of doubles, where each item
50+ * specifies a half-open interval up to the next item:
51+ * <blockquote>
52+ * <pre>
53+ * X matches j if and only if limit[j] &lt;= X &lt; limit[j+1]
54+ * </pre>
55+ * </blockquote>
56+ * If there is no match, then either the first or last index is used, depending
57+ * on whether the number (X) is too low or too high. If the limit array is not
58+ * in ascending order, the results of formatting will be incorrect. ChoiceFormat
59+ * also accepts <code>&#92;u221E</code> as equivalent to infinity(INF).
60+ *
61+ * <p>
62+ * <strong>Note:</strong>
63+ * <code>ChoiceFormat</code> differs from the other <code>Format</code>
64+ * classes in that you create a <code>ChoiceFormat</code> object with a
65+ * constructor (not with a <code>getInstance</code> style factory
66+ * method). The factory methods aren't necessary because <code>ChoiceFormat</code>
67+ * doesn't require any complex setup for a given locale. In fact,
68+ * <code>ChoiceFormat</code> doesn't implement any locale specific behavior.
69+ *
70+ * <p>
71+ * When creating a <code>ChoiceFormat</code>, you must specify an array of formats
72+ * and an array of limits. The length of these arrays must be the same.
73+ * For example,
74+ * <ul>
75+ * <li>
76+ * <em>limits</em> = {1,2,3,4,5,6,7}<br>
77+ * <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
78+ * <li>
79+ * <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
80+ * <em>formats</em> = {"no files", "one file", "many files"}<br>
81+ * (<code>nextDouble</code> can be used to get the next higher double, to
82+ * make the half-open interval.)
83+ * </ul>
84+ *
85+ * <p>
86+ * Here is a simple example that shows formatting and parsing:
87+ * <blockquote>
88+ * <pre>
89+ * double[] limits = {1,2,3,4,5,6,7};
90+ * String[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
91+ * ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
92+ * ParsePosition status = new ParsePosition(0);
93+ * for (double i = 0.0; i &lt;= 8.0; ++i) {
94+ * status.setIndex(0);
95+ * System.out.println(i + " -&gt; " + form.format(i) + " -&gt; "
96+ * + form.parse(form.format(i),status));
97+ * }
98+ * </pre>
99+ * </blockquote>
100+ * Here is a more complex example, with a pattern format:
101+ * <blockquote>
102+ * <pre>
103+ * double[] filelimits = {0,1,2};
104+ * String[] filepart = {"are no files","is one file","are {2} files"};
105+ * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
106+ * Format[] testFormats = {fileform, null, NumberFormat.getInstance()};
107+ * MessageFormat pattform = new MessageFormat("There {0} on {1}");
108+ * pattform.setFormats(testFormats);
109+ * Object[] testArgs = {null, "ADisk", null};
110+ * for (int i = 0; i &lt; 4; ++i) {
111+ * testArgs[0] = new Integer(i);
112+ * testArgs[2] = testArgs[0];
113+ * System.out.println(pattform.format(testArgs));
114+ * }
115+ * </pre>
116+ * </blockquote>
117+ * <p>
118+ * Specifying a pattern for ChoiceFormat objects is fairly straightforward.
119+ * For example:
120+ * <blockquote>
121+ * <pre>
122+ * ChoiceFormat fmt = new ChoiceFormat(
123+ * "-1#is negative| 0#is zero or fraction | 1#is one |1.0&lt;is 1+ |2#is two |2&lt;is more than 2.");
124+ * System.out.println("Formatter Pattern : " + fmt.toPattern());
125+ *
126+ * System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
127+ * System.out.println("Format with -1.0 : " + fmt.format(-1.0));
128+ * System.out.println("Format with 0 : " + fmt.format(0));
129+ * System.out.println("Format with 0.9 : " + fmt.format(0.9));
130+ * System.out.println("Format with 1.0 : " + fmt.format(1));
131+ * System.out.println("Format with 1.5 : " + fmt.format(1.5));
132+ * System.out.println("Format with 2 : " + fmt.format(2));
133+ * System.out.println("Format with 2.1 : " + fmt.format(2.1));
134+ * System.out.println("Format with NaN : " + fmt.format(Double.NaN));
135+ * System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
136+ * </pre>
137+ * </blockquote>
138+ * And the output result would be like the following:
139+ * <blockquote>
140+ * <pre>
141+ * Format with -INF : is negative
142+ * Format with -1.0 : is negative
143+ * Format with 0 : is zero or fraction
144+ * Format with 0.9 : is zero or fraction
145+ * Format with 1.0 : is one
146+ * Format with 1.5 : is 1+
147+ * Format with 2 : is two
148+ * Format with 2.1 : is more than 2.
149+ * Format with NaN : is negative
150+ * Format with +INF : is more than 2.
151+ * </pre>
152+ * </blockquote>
153+ *
154+ * <h4><a name="synchronization">Synchronization</a></h4>
155+ *
156+ * <p>
157+ * Choice formats are not synchronized.
158+ * It is recommended to create separate format instances for each thread.
159+ * If multiple threads access a format concurrently, it must be synchronized
160+ * externally.
161+ *
162+ *
163+ * @see DecimalFormat
164+ * @see MessageFormat
165+ * @author Mark Davis
166+ */
167+public class ChoiceFormat extends NumberFormat {
168+
169+ // Proclaim serial compatibility with 1.1 FCS
170+ private static final long serialVersionUID = 1795184449645032964L;
171+
172+ /**
173+ * Sets the pattern.
174+ * @param newPattern See the class description.
175+ */
176+ public void applyPattern(String newPattern) {
177+ StringBuffer[] segments = new StringBuffer[2];
178+ for (int i = 0; i < segments.length; ++i) {
179+ segments[i] = new StringBuffer();
180+ }
181+ double[] newChoiceLimits = new double[30];
182+ String[] newChoiceFormats = new String[30];
183+ int count = 0;
184+ int part = 0;
185+ double startValue = 0;
186+ double oldStartValue = Double.NaN;
187+ boolean inQuote = false;
188+ for (int i = 0; i < newPattern.length(); ++i) {
189+ char ch = newPattern.charAt(i);
190+ if (ch=='\'') {
191+ // Check for "''" indicating a literal quote
192+ if ((i+1)<newPattern.length() && newPattern.charAt(i+1)==ch) {
193+ segments[part].append(ch);
194+ ++i;
195+ } else {
196+ inQuote = !inQuote;
197+ }
198+ } else if (inQuote) {
199+ segments[part].append(ch);
200+ } else if (ch == '<' || ch == '#' || ch == '\u2264') {
201+ if (segments[0].length() == 0) {
202+ throw new IllegalArgumentException();
203+ }
204+ try {
205+ String tempBuffer = segments[0].toString();
206+ if (tempBuffer.equals("\u221E")) {
207+ startValue = Double.POSITIVE_INFINITY;
208+ } else if (tempBuffer.equals("-\u221E")) {
209+ startValue = Double.NEGATIVE_INFINITY;
210+ } else {
211+ startValue = Double.valueOf(segments[0].toString()).doubleValue();
212+ }
213+ } catch (Exception e) {
214+ throw new IllegalArgumentException();
215+ }
216+ if (ch == '<' && startValue != Double.POSITIVE_INFINITY &&
217+ startValue != Double.NEGATIVE_INFINITY) {
218+ startValue = nextDouble(startValue);
219+ }
220+ if (startValue <= oldStartValue) {
221+ throw new IllegalArgumentException();
222+ }
223+ segments[0].setLength(0);
224+ part = 1;
225+ } else if (ch == '|') {
226+ if (count == newChoiceLimits.length) {
227+ newChoiceLimits = doubleArraySize(newChoiceLimits);
228+ newChoiceFormats = doubleArraySize(newChoiceFormats);
229+ }
230+ newChoiceLimits[count] = startValue;
231+ newChoiceFormats[count] = segments[1].toString();
232+ ++count;
233+ oldStartValue = startValue;
234+ segments[1].setLength(0);
235+ part = 0;
236+ } else {
237+ segments[part].append(ch);
238+ }
239+ }
240+ // clean up last one
241+ if (part == 1) {
242+ if (count == newChoiceLimits.length) {
243+ newChoiceLimits = doubleArraySize(newChoiceLimits);
244+ newChoiceFormats = doubleArraySize(newChoiceFormats);
245+ }
246+ newChoiceLimits[count] = startValue;
247+ newChoiceFormats[count] = segments[1].toString();
248+ ++count;
249+ }
250+ choiceLimits = new double[count];
251+ System.arraycopy(newChoiceLimits, 0, choiceLimits, 0, count);
252+ choiceFormats = new String[count];
253+ System.arraycopy(newChoiceFormats, 0, choiceFormats, 0, count);
254+ }
255+
256+ /**
257+ * Gets the pattern.
258+ */
259+ public String toPattern() {
260+ StringBuffer result = new StringBuffer();
261+ for (int i = 0; i < choiceLimits.length; ++i) {
262+ if (i != 0) {
263+ result.append('|');
264+ }
265+ // choose based upon which has less precision
266+ // approximate that by choosing the closest one to an integer.
267+ // could do better, but it's not worth it.
268+ double less = previousDouble(choiceLimits[i]);
269+ double tryLessOrEqual = Math.abs(Math.IEEEremainder(choiceLimits[i], 1.0d));
270+ double tryLess = Math.abs(Math.IEEEremainder(less, 1.0d));
271+
272+ if (tryLessOrEqual < tryLess) {
273+ result.append(""+choiceLimits[i]);
274+ result.append('#');
275+ } else {
276+ if (choiceLimits[i] == Double.POSITIVE_INFINITY) {
277+ result.append("\u221E");
278+ } else if (choiceLimits[i] == Double.NEGATIVE_INFINITY) {
279+ result.append("-\u221E");
280+ } else {
281+ result.append(""+less);
282+ }
283+ result.append('<');
284+ }
285+ // Append choiceFormats[i], using quotes if there are special characters.
286+ // Single quotes themselves must be escaped in either case.
287+ String text = choiceFormats[i];
288+ boolean needQuote = text.indexOf('<') >= 0
289+ || text.indexOf('#') >= 0
290+ || text.indexOf('\u2264') >= 0
291+ || text.indexOf('|') >= 0;
292+ if (needQuote) result.append('\'');
293+ if (text.indexOf('\'') < 0) result.append(text);
294+ else {
295+ for (int j=0; j<text.length(); ++j) {
296+ char c = text.charAt(j);
297+ result.append(c);
298+ if (c == '\'') result.append(c);
299+ }
300+ }
301+ if (needQuote) result.append('\'');
302+ }
303+ return result.toString();
304+ }
305+
306+ /**
307+ * Constructs with limits and corresponding formats based on the pattern.
308+ * @see #applyPattern
309+ */
310+ public ChoiceFormat(String newPattern) {
311+ applyPattern(newPattern);
312+ }
313+
314+ /**
315+ * Constructs with the limits and the corresponding formats.
316+ * @see #setChoices
317+ */
318+ public ChoiceFormat(double[] limits, String[] formats) {
319+ setChoices(limits, formats);
320+ }
321+
322+ /**
323+ * Set the choices to be used in formatting.
324+ * @param limits contains the top value that you want
325+ * parsed with that format,and should be in ascending sorted order. When
326+ * formatting X, the choice will be the i, where
327+ * limit[i] &lt;= X &lt; limit[i+1].
328+ * If the limit array is not in ascending order, the results of formatting
329+ * will be incorrect.
330+ * @param formats are the formats you want to use for each limit.
331+ * They can be either Format objects or Strings.
332+ * When formatting with object Y,
333+ * if the object is a NumberFormat, then ((NumberFormat) Y).format(X)
334+ * is called. Otherwise Y.toString() is called.
335+ */
336+ public void setChoices(double[] limits, String formats[]) {
337+ if (limits.length != formats.length) {
338+ throw new IllegalArgumentException(
339+ "Array and limit arrays must be of the same length.");
340+ }
341+ choiceLimits = limits;
342+ choiceFormats = formats;
343+ }
344+
345+ /**
346+ * Get the limits passed in the constructor.
347+ * @return the limits.
348+ */
349+ public double[] getLimits() {
350+ return choiceLimits;
351+ }
352+
353+ /**
354+ * Get the formats passed in the constructor.
355+ * @return the formats.
356+ */
357+ public Object[] getFormats() {
358+ return choiceFormats;
359+ }
360+
361+ // Overrides
362+
363+ /**
364+ * Specialization of format. This method really calls
365+ * <code>format(double, StringBuffer, FieldPosition)</code>
366+ * thus the range of longs that are supported is only equal to
367+ * the range that can be stored by double. This will never be
368+ * a practical limitation.
369+ */
370+ public StringBuffer format(long number, StringBuffer toAppendTo,
371+ FieldPosition status) {
372+ return format((double)number, toAppendTo, status);
373+ }
374+
375+ /**
376+ * Returns pattern with formatted double.
377+ * @param number number to be formatted & substituted.
378+ * @param toAppendTo where text is appended.
379+ * @param status ignore no useful status is returned.
380+ */
381+ public StringBuffer format(double number, StringBuffer toAppendTo,
382+ FieldPosition status) {
383+ // find the number
384+ int i;
385+ for (i = 0; i < choiceLimits.length; ++i) {
386+ if (!(number >= choiceLimits[i])) {
387+ // same as number < choiceLimits, except catchs NaN
388+ break;
389+ }
390+ }
391+ --i;
392+ if (i < 0) i = 0;
393+ // return either a formatted number, or a string
394+ return toAppendTo.append(choiceFormats[i]);
395+ }
396+
397+ /**
398+ * Parses a Number from the input text.
399+ * @param text the source text.
400+ * @param status an input-output parameter. On input, the
401+ * status.index field indicates the first character of the
402+ * source text that should be parsed. On exit, if no error
403+ * occured, status.index is set to the first unparsed character
404+ * in the source text. On exit, if an error did occur,
405+ * status.index is unchanged and status.errorIndex is set to the
406+ * first index of the character that caused the parse to fail.
407+ * @return A Number representing the value of the number parsed.
408+ */
409+ public Number parse(String text, ParsePosition status) {
410+ // find the best number (defined as the one with the longest parse)
411+ int start = status.index;
412+ int furthest = start;
413+ double bestNumber = Double.NaN;
414+ double tempNumber = 0.0;
415+ for (int i = 0; i < choiceFormats.length; ++i) {
416+ String tempString = choiceFormats[i];
417+ if (text.regionMatches(start, tempString, 0, tempString.length())) {
418+ status.index = start + tempString.length();
419+ tempNumber = choiceLimits[i];
420+ if (status.index > furthest) {
421+ furthest = status.index;
422+ bestNumber = tempNumber;
423+ if (furthest == text.length()) break;
424+ }
425+ }
426+ }
427+ status.index = furthest;
428+ if (status.index == start) {
429+ status.errorIndex = furthest;
430+ }
431+ return new Double(bestNumber);
432+ }
433+
434+ /**
435+ * Finds the least double greater than d.
436+ * If NaN, returns same value.
437+ * <p>Used to make half-open intervals.
438+ * @see #previousDouble
439+ */
440+ public static final double nextDouble (double d) {
441+ return nextDouble(d,true);
442+ }
443+
444+ /**
445+ * Finds the greatest double less than d.
446+ * If NaN, returns same value.
447+ * @see #nextDouble
448+ */
449+ public static final double previousDouble (double d) {
450+ return nextDouble(d,false);
451+ }
452+
453+ /**
454+ * Overrides Cloneable
455+ */
456+ public Object clone()
457+ {
458+ ChoiceFormat other = (ChoiceFormat) super.clone();
459+ // for primitives or immutables, shallow clone is enough
460+ other.choiceLimits = (double[]) choiceLimits.clone();
461+ other.choiceFormats = (String[]) choiceFormats.clone();
462+ return other;
463+ }
464+
465+ /**
466+ * Generates a hash code for the message format object.
467+ */
468+ public int hashCode() {
469+ int result = choiceLimits.length;
470+ if (choiceFormats.length > 0) {
471+ // enough for reasonable distribution
472+ result ^= choiceFormats[choiceFormats.length-1].hashCode();
473+ }
474+ return result;
475+ }
476+
477+ /**
478+ * Equality comparision between two
479+ */
480+ public boolean equals(Object obj) {
481+ if (obj == null) return false;
482+ if (this == obj) // quick check
483+ return true;
484+ if (getClass() != obj.getClass())
485+ return false;
486+ ChoiceFormat other = (ChoiceFormat) obj;
487+ return (Arrays.equals(choiceLimits, other.choiceLimits)
488+ && Arrays.equals(choiceFormats, other.choiceFormats));
489+ }
490+
491+ /**
492+ * After reading an object from the input stream, do a simple verification
493+ * to maintain class invariants.
494+ * @throws InvalidObjectException if the objects read from the stream is invalid.
495+ */
496+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
497+ in.defaultReadObject();
498+ if (choiceLimits.length != choiceFormats.length) {
499+ throw new InvalidObjectException(
500+ "limits and format arrays of different length.");
501+ }
502+ }
503+
504+ // ===============privates===========================
505+
506+ /**
507+ * A list of lower bounds for the choices. The formatter will return
508+ * <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
509+ * <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
510+ * @serial
511+ */
512+ private double[] choiceLimits;
513+
514+ /**
515+ * A list of choice strings. The formatter will return
516+ * <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
517+ * <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
518+ * @serial
519+ */
520+ private String[] choiceFormats;
521+
522+ /*
523+ static final long SIGN = 0x8000000000000000L;
524+ static final long EXPONENT = 0x7FF0000000000000L;
525+ static final long SIGNIFICAND = 0x000FFFFFFFFFFFFFL;
526+
527+ private static double nextDouble (double d, boolean positive) {
528+ if (Double.isNaN(d) || Double.isInfinite(d)) {
529+ return d;
530+ }
531+ long bits = Double.doubleToLongBits(d);
532+ long significand = bits & SIGNIFICAND;
533+ if (bits < 0) {
534+ significand |= (SIGN | EXPONENT);
535+ }
536+ long exponent = bits & EXPONENT;
537+ if (positive) {
538+ significand += 1;
539+ // FIXME fix overflow & underflow
540+ } else {
541+ significand -= 1;
542+ // FIXME fix overflow & underflow
543+ }
544+ bits = exponent | (significand & ~EXPONENT);
545+ return Double.longBitsToDouble(bits);
546+ }
547+ */
548+
549+ static final long SIGN = 0x8000000000000000L;
550+ static final long EXPONENT = 0x7FF0000000000000L;
551+ static final long POSITIVEINFINITY = 0x7FF0000000000000L;
552+
553+ /**
554+ * Finds the least double greater than d (if positive == true),
555+ * or the greatest double less than d (if positive == false).
556+ * If NaN, returns same value.
557+ *
558+ * Does not affect floating-point flags,
559+ * provided these member functions do not:
560+ * Double.longBitsToDouble(long)
561+ * Double.doubleToLongBits(double)
562+ * Double.isNaN(double)
563+ */
564+ public static double nextDouble (double d, boolean positive) {
565+
566+ /* filter out NaN's */
567+ if (Double.isNaN(d)) {
568+ return d;
569+ }
570+
571+ /* zero's are also a special case */
572+ if (d == 0.0) {
573+ double smallestPositiveDouble = Double.longBitsToDouble(1L);
574+ if (positive) {
575+ return smallestPositiveDouble;
576+ } else {
577+ return -smallestPositiveDouble;
578+ }
579+ }
580+
581+ /* if entering here, d is a nonzero value */
582+
583+ /* hold all bits in a long for later use */
584+ long bits = Double.doubleToLongBits(d);
585+
586+ /* strip off the sign bit */
587+ long magnitude = bits & ~SIGN;
588+
589+ /* if next double away from zero, increase magnitude */
590+ if ((bits > 0) == positive) {
591+ if (magnitude != POSITIVEINFINITY) {
592+ magnitude += 1;
593+ }
594+ }
595+ /* else decrease magnitude */
596+ else {
597+ magnitude -= 1;
598+ }
599+
600+ /* restore sign bit and return */
601+ long signbit = bits & SIGN;
602+ return Double.longBitsToDouble (magnitude | signbit);
603+ }
604+
605+ private static double[] doubleArraySize(double[] array) {
606+ int oldSize = array.length;
607+ double[] newArray = new double[oldSize * 2];
608+ System.arraycopy(array, 0, newArray, 0, oldSize);
609+ return newArray;
610+ }
611+
612+ private String[] doubleArraySize(String[] array) {
613+ int oldSize = array.length;
614+ String[] newArray = new String[oldSize * 2];
615+ System.arraycopy(array, 0, newArray, 0, oldSize);
616+ return newArray;
617+ }
618+
619+}