// ChoiceFormat.java - Formatter for `switch'-like string substitution. /* Copyright (C) 1999 Cygnus Solutions This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ package java.text; import java.util.Vector; /** * @author Tom Tromey * @date March 9, 1999 */ /* Written using "Java Class Libraries", 2nd edition, plus online * API docs for JDK 1.2 from http://www.javasoft.com. * Status: Believed complete and correct to 1.1. */ public class ChoiceFormat extends NumberFormat { // Note: we assume the same kind of quoting rules apply here. // This isn't explicitly documented. But for instance we accept // '#' as a literal hash in a format string. public void applyPattern (String newPattern) { int index = 0, max = newPattern.length(); Vector stringVec = new Vector (); Vector limitVec = new Vector (); StringBuffer buf = new StringBuffer (); while (true) { // Find end of double. int dstart = index; while (index < max) { char c = newPattern.charAt(index); if (c == '#' || c == '\u2064' || c == '<') break; ++index; } if (index == max) throw new IllegalArgumentException ("unexpected end of text"); Double d = new Double (newPattern.substring(dstart, index)); if (newPattern.charAt(index) == '<') d = new Double (nextDouble (d.doubleValue())); limitVec.addElement(d); // Scan text. ++index; buf.setLength(0); while (index < max) { char c = newPattern.charAt(index); if (c == '\'' && index < max + 1 && newPattern.charAt(index + 1) == '\'') { buf.append(c); ++index; } else if (c == '\'' && index < max + 2) { buf.append(newPattern.charAt(index + 1)); index += 2; } else if (c == '|') break; else buf.append(c); ++index; } stringVec.addElement(buf.toString()); if (index == max) break; ++index; } strings = new String[stringVec.size()]; stringVec.copyInto(strings); limits = new double[limitVec.size()]; for (int i = 0; i < limits.length; ++i) { Double d = (Double) limitVec.elementAt(i); limits[i] = d.doubleValue(); } } public ChoiceFormat (String newPattern) { super (); applyPattern (newPattern); } public ChoiceFormat (double[] limits, String[] strings) { super (); setChoices (limits, strings); } public Object clone () { return new ChoiceFormat (limits, strings); } public boolean equals (Object obj) { if (! (obj instanceof ChoiceFormat)) return false; ChoiceFormat cf = (ChoiceFormat) obj; if (limits.length != cf.limits.length) return false; for (int i = limits.length - 1; i >= 0; --i) { if (limits[i] != cf.limits[i] || !strings[i].equals(cf.strings[i])) return false; } return true; } public StringBuffer format (long num, StringBuffer appendBuf, FieldPosition pos) { return format ((double) num, appendBuf, pos); } public StringBuffer format (double num, StringBuffer appendBuf, FieldPosition pos) { if (limits.length == 0) return appendBuf; int index = 0; if (! Double.isNaN(num) && num >= limits[0]) { for (; index < limits.length - 1; ++index) { if (limits[index] <= num && index != limits.length - 2 && num < limits[index + 1]) break; } } return appendBuf.append(strings[index]); } public Object[] getFormats () { return (Object[]) strings.clone(); } public double[] getLimits () { return (double[]) limits.clone(); } public int hashCode () { int hash = 0; for (int i = 0; i < limits.length; ++i) { long v = Double.doubleToLongBits(limits[i]); hash ^= (v ^ (v >>> 32)); hash ^= strings[i].hashCode(); } return hash; } public static final double nextDouble (double d) { return nextDouble (d, true); } public static final double nextDouble (double d, boolean next) { if (Double.isInfinite(d) || Double.isNaN(d)) return d; long bits = Double.doubleToLongBits(d); long mantMask = (1L << mantissaBits) - 1; long mantissa = bits & mantMask; long expMask = (1L << exponentBits) - 1; long exponent = (bits >>> mantissaBits) & expMask; if (next ^ (bits < 0)) // Increment magnitude { if (mantissa == (1L << mantissaBits) - 1) { mantissa = 0L; exponent++; // Check for absolute overflow. if (exponent >= (1L << mantissaBits)) return (bits > 0) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY; } else mantissa++; } else // Decrement magnitude { if (exponent == 0L && mantissa == 0L) { // The only case where there is a change of sign return next ? Double.MIN_VALUE : -Double.MIN_VALUE; } else { if (mantissa == 0L) { mantissa = (1L << mantissaBits) - 1; exponent--; } else mantissa--; } } long result = bits < 0 ? 1 : 0; result = (result << exponentBits) | exponent; result = (result << mantissaBits) | mantissa; return Double.longBitsToDouble(result); } public Number parse (String sourceStr, ParsePosition pos) { int index = pos.getIndex(); for (int i = 0; i < limits.length; ++i) { if (sourceStr.startsWith(strings[i], index)) { pos.setIndex(index + strings[i].length()); return new Double (limits[i]); } } pos.setErrorIndex(index); return new Double (Double.NaN); } public static final double previousDouble (double d) { return nextDouble (d, false); } public void setChoices (double[] limits, String[] strings) { if (limits == null || strings == null) throw new NullPointerException (); if (limits.length != strings.length) throw new IllegalArgumentException (); this.strings = (String[]) strings.clone(); this.limits = (double[]) limits.clone(); } private final void quoteString (StringBuffer dest, String text) { int max = text.length(); for (int i = 0; i < max; ++i) { char c = text.charAt(i); if (c == '\'') { dest.append(c); dest.append(c); } else if (c == '#' || c == '|' || c == '\u2064' || c == '<') { dest.append('\''); dest.append(c); dest.append('\''); } else dest.append(c); } } public String toPattern () { StringBuffer result = new StringBuffer (); for (int i = 0; i < limits.length; ++i) { result.append(limits[i]); result.append('#'); quoteString (result, strings[i]); } return result.toString(); } // Formats and limits. private String[] strings; private double[] limits; // Number of mantissa bits in double. private static final int mantissaBits = 52; // Number of exponent bits in a double. private static final int exponentBits = 11; }