001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.text;
019:
020: import java.util.ArrayList;
021: import java.util.Arrays;
022: import java.util.List;
023: import java.util.Locale;
024:
025: /**
026: * ChoiceFormat is used to associate strings with ranges of double values. The
027: * strings and ranges are either specified using arrays or with a pattern which
028: * is parsed to determine the Strings and ranges.
029: */
030:
031: public class ChoiceFormat extends NumberFormat {
032:
033: private static final long serialVersionUID = 1795184449645032964L;
034:
035: private double[] choiceLimits;
036:
037: private String[] choiceFormats;
038:
039: /**
040: * Constructs a new ChoiceFormat with the specified ranges and associated
041: * strings.
042: *
043: * @param limits
044: * an array of double, the ranges are greater or equal to the
045: * value in lower index up to less than the value in the next
046: * higher index. The bounds of the lowest and highest indexes are
047: * negative and positive infinity.
048: * @param formats
049: * the strings associated with the ranges. The lower bound of the
050: * associated range is at the same index as the string.
051: */
052: public ChoiceFormat(double[] limits, String[] formats) {
053: setChoices(limits, formats);
054: }
055:
056: /**
057: * Constructs a new ChoiceFormat with the strings and ranges parsed from the
058: * specified pattern.
059: *
060: * @param template
061: * the pattern of strings and ranges
062: *
063: * @exception IllegalArgumentException
064: * then an error occurs parsing the pattern
065: */
066: public ChoiceFormat(String template) {
067: applyPattern(template);
068: }
069:
070: /**
071: * Parses the pattern to determine new strings and ranges for this
072: * ChoiceFormat.
073: *
074: * @param template
075: * the pattern of strings and ranges
076: *
077: * @exception IllegalArgumentException
078: * then an error occurs parsing the pattern
079: */
080: public void applyPattern(String template) {
081: double[] limits = new double[5];
082: List<String> formats = new ArrayList<String>();
083: int length = template.length(), limitCount = 0, index = 0;
084: StringBuffer buffer = new StringBuffer();
085: NumberFormat format = NumberFormat.getInstance(Locale.US);
086: ParsePosition position = new ParsePosition(0);
087: while (true) {
088: index = skipWhitespace(template, index);
089: if (index >= length) {
090: if (limitCount == limits.length) {
091: choiceLimits = limits;
092: } else {
093: choiceLimits = new double[limitCount];
094: System.arraycopy(limits, 0, choiceLimits, 0,
095: limitCount);
096: }
097: choiceFormats = new String[formats.size()];
098: for (int i = 0; i < formats.size(); i++) {
099: choiceFormats[i] = formats.get(i);
100: }
101: return;
102: }
103:
104: position.setIndex(index);
105: Number value = format.parse(template, position);
106: index = skipWhitespace(template, position.getIndex());
107: if (position.getErrorIndex() != -1 || index >= length) {
108: // Fix Harmony 540
109: choiceLimits = new double[0];
110: choiceFormats = new String[0];
111: return;
112: }
113: char ch = template.charAt(index++);
114: if (limitCount == limits.length) {
115: double[] newLimits = new double[limitCount * 2];
116: System.arraycopy(limits, 0, newLimits, 0, limitCount);
117: limits = newLimits;
118: }
119: double next;
120: switch (ch) {
121: case '#':
122: case '\u2264':
123: next = value.doubleValue();
124: break;
125: case '<':
126: next = nextDouble(value.doubleValue());
127: break;
128: default:
129: throw new IllegalArgumentException();
130: }
131: if (limitCount > 0 && next <= limits[limitCount - 1]) {
132: throw new IllegalArgumentException();
133: }
134: buffer.setLength(0);
135: position.setIndex(index);
136: upTo(template, position, buffer, '|');
137: index = position.getIndex();
138: limits[limitCount++] = next;
139: formats.add(buffer.toString());
140: }
141: }
142:
143: /**
144: * Answers a new instance of ChoiceFormat with the same ranges and strings
145: * as this ChoiceFormat.
146: *
147: * @return a shallow copy of this ChoiceFormat
148: *
149: * @see java.lang.Cloneable
150: */
151: @Override
152: public Object clone() {
153: ChoiceFormat clone = (ChoiceFormat) super .clone();
154: clone.choiceLimits = choiceLimits.clone();
155: clone.choiceFormats = choiceFormats.clone();
156: return clone;
157: }
158:
159: /**
160: * Compares the specified object to this ChoiceFormat and answer if they are
161: * equal. The object must be an instance of ChoiceFormat and have the same
162: * limits and formats.
163: *
164: * @param object
165: * the object to compare with this object
166: * @return true if the specified object is equal to this ChoiceFormat, false
167: * otherwise
168: *
169: * @see #hashCode
170: */
171: @Override
172: public boolean equals(Object object) {
173: if (this == object) {
174: return true;
175: }
176: if (!(object instanceof ChoiceFormat)) {
177: return false;
178: }
179: ChoiceFormat choice = (ChoiceFormat) object;
180: return Arrays.equals(choiceLimits, choice.choiceLimits)
181: && Arrays.equals(choiceFormats, choice.choiceFormats);
182: }
183:
184: /**
185: * Appends to the specified StringBuffer the string associated with the
186: * range in which the specified double value fits.
187: *
188: * @param value
189: * the double to format
190: * @param buffer
191: * the StringBuffer
192: * @param field
193: * a FieldPosition which is ignored
194: * @return the StringBuffer parameter <code>buffer</code>
195: */
196: @Override
197: public StringBuffer format(double value, StringBuffer buffer,
198: FieldPosition field) {
199: for (int i = choiceLimits.length - 1; i >= 0; i--) {
200: if (choiceLimits[i] <= value) {
201: return buffer.append(choiceFormats[i]);
202: }
203: }
204: return choiceFormats.length == 0 ? buffer : buffer
205: .append(choiceFormats[0]);
206: }
207:
208: /**
209: * Appends to the specified StringBuffer the string associated with the
210: * range in which the specified long value fits.
211: *
212: * @param value
213: * the long to format
214: * @param buffer
215: * the StringBuffer
216: * @param field
217: * a FieldPosition which is ignored
218: * @return the StringBuffer parameter <code>buffer</code>
219: */
220: @Override
221: public StringBuffer format(long value, StringBuffer buffer,
222: FieldPosition field) {
223: return format((double) value, buffer, field);
224: }
225:
226: /**
227: * Answers the Strings associated with the ranges of this ChoiceFormat.
228: *
229: * @return an array of String
230: */
231: public Object[] getFormats() {
232: return choiceFormats;
233: }
234:
235: /**
236: * Answers the ranges of this ChoiceFormat.
237: *
238: * @return an array of double, the ranges are greater or equal to the value
239: * in lower index up to less than the value in the next higher
240: * index. The bounds of the lowest and highest indexes are negative
241: * and positive infinity.
242: */
243: public double[] getLimits() {
244: return choiceLimits;
245: }
246:
247: /**
248: * Answers an integer hash code for the receiver. Objects which are equal
249: * answer the same value for this method.
250: *
251: * @return the receiver's hash
252: *
253: * @see #equals
254: */
255: @Override
256: public int hashCode() {
257: int hashCode = 0;
258: for (int i = 0; i < choiceLimits.length; i++) {
259: long v = Double.doubleToLongBits(choiceLimits[i]);
260: hashCode += (int) (v ^ (v >>> 32))
261: + choiceFormats[i].hashCode();
262: }
263: return hashCode;
264: }
265:
266: /**
267: * Answers the double value which is closest to the specified double but
268: * larger.
269: *
270: * @param value
271: * a double value
272: * @return the next larger double value
273: */
274: public static final double nextDouble(double value) {
275: if (value == Double.POSITIVE_INFINITY) {
276: return value;
277: }
278: long bits;
279: // Handle -0.0
280: if (value == 0) {
281: bits = 0;
282: } else {
283: bits = Double.doubleToLongBits(value);
284: }
285: return Double.longBitsToDouble(value < 0 ? bits - 1 : bits + 1);
286: }
287:
288: /**
289: * Answers the double value which is closest to the specified double but
290: * either larger or smaller as specified.
291: *
292: * @param value
293: * a double value
294: * @param increment
295: * true to get a larger value, false to get a smaller value
296: * @return the next larger or smaller double value
297: */
298: public static double nextDouble(double value, boolean increment) {
299: return increment ? nextDouble(value) : previousDouble(value);
300: }
301:
302: /**
303: * Parse a Double from the specified String starting at the index specified
304: * by the ParsePosition. The String is compared to the strings of this
305: * ChoiceFormat and if a match occurs, the answer is the lower bound of the
306: * corresponding range. If the string is successfully parsed, the index of
307: * the ParsePosition is updated to the index following the parsed text.
308: *
309: * @param string
310: * the String to parse
311: * @param position
312: * the ParsePosition, updated on return with the index following
313: * the parsed text, or on error the index is unchanged and the
314: * error index is set to the index where the error occurred
315: * @return a Double resulting from the parse, or Double.NaN if there is an
316: * error
317: */
318: @Override
319: public Number parse(String string, ParsePosition position) {
320: int offset = position.getIndex();
321: for (int i = 0; i < choiceFormats.length; i++) {
322: if (string.startsWith(choiceFormats[i], offset)) {
323: position.setIndex(offset + choiceFormats[i].length());
324: return new Double(choiceLimits[i]);
325: }
326: }
327: position.setErrorIndex(offset);
328: return new Double(Double.NaN);
329: }
330:
331: /**
332: * Answers the double value which is closest to the specified double but
333: * smaller.
334: *
335: * @param value
336: * a double value
337: * @return the next smaller double value
338: */
339: public static final double previousDouble(double value) {
340: if (value == Double.NEGATIVE_INFINITY) {
341: return value;
342: }
343: long bits;
344: // Handle 0.0
345: if (value == 0) {
346: bits = 0x8000000000000000L;
347: } else {
348: bits = Double.doubleToLongBits(value);
349: }
350: return Double
351: .longBitsToDouble(value <= 0 ? bits + 1 : bits - 1);
352: }
353:
354: /**
355: * Sets the ranges and associated strings of this ChoiceFormat.
356: *
357: * @param limits
358: * an array of double, the ranges are greater or equal to the
359: * value in lower index up to less than the value in the next
360: * higher index. The bounds of the lowest and highest indexes are
361: * negative and positive infinity.
362: * @param formats
363: * the strings associated with the ranges. The lower bound of the
364: * range is at the same index as the string.
365: */
366: public void setChoices(double[] limits, String[] formats) {
367: if (limits.length != formats.length) {
368: throw new IllegalArgumentException();
369: }
370: choiceLimits = limits;
371: choiceFormats = formats;
372: }
373:
374: private int skipWhitespace(String string, int index) {
375: int length = string.length();
376: while (index < length
377: && Character.isWhitespace(string.charAt(index))) {
378: index++;
379: }
380: return index;
381: }
382:
383: /**
384: * Answers the pattern of this ChoiceFormat which specified the ranges and
385: * their associated strings.
386: *
387: * @return the pattern
388: */
389: public String toPattern() {
390: StringBuffer buffer = new StringBuffer();
391: for (int i = 0; i < choiceLimits.length; i++) {
392: if (i != 0) {
393: buffer.append('|');
394: }
395: String previous = String
396: .valueOf(previousDouble(choiceLimits[i]));
397: String limit = String.valueOf(choiceLimits[i]);
398: if (previous.length() < limit.length()) {
399: buffer.append(previous);
400: buffer.append('<');
401: } else {
402: buffer.append(limit);
403: buffer.append('#');
404: }
405: boolean quote = (choiceFormats[i].indexOf('|') != -1);
406: if (quote) {
407: buffer.append('\'');
408: }
409: buffer.append(choiceFormats[i]);
410: if (quote) {
411: buffer.append('\'');
412: }
413: }
414: return buffer.toString();
415: }
416: }
|