home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / text / ChoiceFormat.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  16.1 KB  |  448 lines

  1. /*
  2.  * @(#)ChoiceFormat.java    1.17 98/03/18
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996-1997 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996-1997 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.text;
  32. import java.text.Utility;
  33.  
  34. /**
  35.  * A <code>ChoiceFormat</code> allows you to attach a format to a range of numbers.
  36.  * It is generally used in a <code>MessageFormat</code> for handling plurals.
  37.  * The choice is specified with an ascending list of doubles, where each item
  38.  * specifies a half-open interval up to the next item:
  39.  * <blockquote>
  40.  * <pre>
  41.  * X matches j if and only if limit[j] <= X < limit[j+1]
  42.  * </pre>
  43.  * </blockquote>
  44.  * If there is no match, then either the first or last index is used, depending
  45.  * on whether the number (X) is too low or too high.
  46.  *
  47.  * <p>
  48.  * <strong>Note:</strong>
  49.  * <code>ChoiceFormat</code> differs from the other <code>Format</code>
  50.  * classes in that you create a <code>ChoiceFormat</code> object with a
  51.  * constructor (not with a <code>getInstance</code> style factory
  52.  * method). The factory methods aren't necessary because <code>ChoiceFormat</code>
  53.  * doesn't require any complex setup for a given locale. In fact,
  54.  * <code>ChoiceFormat</code> doesn't implement any locale specific behavior.
  55.  *
  56.  * <p>
  57.  * When creating a <code>ChoiceFormat</code>, you must specify an array of formats
  58.  * and an array of limits. The length of these arrays must be the same.
  59.  * For example,
  60.  * <ul>
  61.  * <li>
  62.  *     <em>limits</em> = {1,2,3,4,5,6,7}<br>
  63.  *     <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
  64.  * <li>
  65.  *     <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
  66.  *     <em>formats</em> = {"no files", "one file", "many files"}<br>
  67.  *     (<code>nextDouble</code> can be used to get the next higher double, to
  68.  *     make the half-open interval.)
  69.  * </ul>
  70.  *
  71.  * <p>
  72.  * Here is a simple example that shows formatting and parsing:
  73.  * <blockquote>
  74.  * <pre>
  75.  * double[] limits = {1,2,3,4,5,6,7};
  76.  * String[] monthNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
  77.  * ChoiceFormat form = new ChoiceFormat(limits, monthNames);
  78.  * ParsePosition status = new ParsePosition(0);
  79.  * for (double i = 0.0; i <= 8.0; ++i) {
  80.  *     status.setIndex(0);
  81.  *     System.out.println(i + " -> " + form.format(i) + " -> "
  82.  *                              + form.parse(form.format(i),status));
  83.  * }
  84.  * </pre>
  85.  * </blockquote>
  86.  * Here is a more complex example, with a pattern format:
  87.  * <blockquote>
  88.  * <pre>
  89.  * double[] filelimits = {0,1,2};
  90.  * String[] filepart = {"are no files","is one file","are {2} files"};
  91.  * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
  92.  * Format[] testFormats = {fileform, null, NumberFormat.getInstance()};
  93.  * MessageFormat pattform = new MessageFormat("There {0} on {1}");
  94.  * pattform.setFormats(testFormats);
  95.  * Object[] testArgs = {null, "ADisk", null};
  96.  * for (int i = 0; i < 4; ++i) {
  97.  *     testArgs[0] = new Integer(i);
  98.  *     testArgs[2] = testArgs[0];
  99.  *     System.out.println(pattform.format(testArgs));
  100.  * }
  101.  * </pre>
  102.  * </blockquote>
  103.  * @see          DecimalFormat
  104.  * @see          MessageFormat
  105.  * @version      1.17 03/18/98
  106.  * @author       Mark Davis
  107.  */
  108. public class ChoiceFormat extends NumberFormat {
  109.     /**
  110.      * Sets the pattern.
  111.      * @param newPattern See the class description.
  112.      */
  113.     public void applyPattern(String newPattern) {
  114.             StringBuffer[] segments = new StringBuffer[2];
  115.             for (int i = 0; i < segments.length; ++i) {
  116.                 segments[i] = new StringBuffer();       // later, use single
  117.             }
  118.             double[] newChoiceLimits = new double[30];  // current limit
  119.             String[] newChoiceFormats = new String[30];   // later, use Vectors
  120.             int count = 0;
  121.             int part = 0;
  122.             double startValue = 0;
  123.             double oldStartValue = Double.NaN;
  124.             boolean inQuote = false;
  125.             for (int i = 0; i < newPattern.length(); ++i) {
  126.                 char ch = newPattern.charAt(i);
  127.                 if (ch == '<' || ch == '#' || ch == '\u2264') {
  128.                     if (segments[0].equals("")) {
  129.                         throw new IllegalArgumentException();
  130.                     }
  131.                     try {
  132.                         startValue = Double.valueOf(segments[0].toString()).doubleValue();
  133.                     } catch (Exception e) {
  134.                         throw new IllegalArgumentException();
  135.                     }
  136.                     if (ch == '<') {
  137.                         startValue = nextDouble(startValue);
  138.                     }
  139.                     if (startValue <= oldStartValue) {
  140.                         throw new IllegalArgumentException();
  141.                     }
  142.                     segments[0].setLength(0);
  143.                     part = 1;
  144.                 } else if (ch == '|') {
  145.                 //System.out.println("***" + startValue + "," + segments[1].toString());
  146.                     newChoiceLimits[count] = startValue;
  147.                     newChoiceFormats[count] = segments[1].toString();
  148.                     ++count;
  149.                     oldStartValue = startValue;
  150.                     segments[1].setLength(0);
  151.                     part = 0;
  152.                 } else {
  153.                     segments[part].append(ch);
  154.                 }
  155.             }
  156.             // clean up last one
  157.             if (part == 1) {
  158.                 newChoiceLimits[count] = startValue;
  159.             newChoiceFormats[count] = segments[1].toString();
  160.                 //System.out.println("***" + newChoiceLimits[count] + "," + newChoiceFormats[count]);
  161.                 ++count;
  162.             }
  163.             // compact arrays
  164.             //System.out.println("***" + count);
  165.             choiceLimits = new double[count];
  166.             System.arraycopy(newChoiceLimits, 0, choiceLimits, 0, count);
  167.             choiceFormats = new String[count];
  168.             System.arraycopy(newChoiceFormats, 0, choiceFormats, 0, count);
  169.             //for (int i = 0; i < choiceLimits.length; ++i) {
  170.             //System.out.println("&&<" + choiceLimits[i]);
  171.             //System.out.println("&&>" + choiceFormats[i]);
  172.             //}
  173.     }
  174.     /**
  175.      * Gets the pattern.
  176.      */
  177.  
  178.     public String toPattern() {
  179.         StringBuffer result = new StringBuffer();
  180.         //System.out.println("&&&" + choiceLimits.length);
  181.         for (int i = 0; i < choiceLimits.length; ++i) {
  182.             //System.out.println("&&<" + choiceLimits[i] + ";"
  183.             //  + Long.toString(Double.doubleToLongBits(choiceLimits[i]),16)
  184.             //  + ";" + choiceFormats[i]);
  185.             if (i != 0) {
  186.                 result.append('|');
  187.             }
  188.             // choose based upon which has less precision
  189.             // approximate that by choosing the closest one to an integer.
  190.             // could do better, but it's not worth it.
  191.             double less = previousDouble(choiceLimits[i]);
  192.             double tryLessOrEqual = Math.abs(Math.IEEEremainder(choiceLimits[i], 1.0d));
  193.             double tryLess = Math.abs(Math.IEEEremainder(less, 1.0d));
  194.             if (tryLessOrEqual < tryLess) {
  195.                 result.append(""+choiceLimits[i]);
  196.                 result.append('#');
  197.             } else {
  198.                 result.append(""+less);
  199.                 result.append('<');
  200.             }
  201.             result.append(choiceFormats[i].toString());
  202.         }
  203.         return result.toString();
  204.     }
  205.  
  206.     /**
  207.      * Constructs with limits and corresponding formats based on the pattern.
  208.      */
  209.     public ChoiceFormat(String newPattern)  {
  210.         applyPattern(newPattern);
  211.     }
  212.     /**
  213.      * Constructs with the limits and the corresponding formats.
  214.      * @see #setChoices
  215.      */
  216.     public ChoiceFormat(double[] limits, String[] formats) {
  217.         setChoices(limits, formats);
  218.     }
  219.     /**
  220.      * Set the choices to be used in formatting.
  221.      * @param limits contains the top value that you want
  222.      * parsed with that format,and should be in ascending sorted order. When
  223.      * formatting X, the choice will be the i, where limit[i] <= X < limit[i+1].
  224.      * @param formats are the formats you want to use for each limit.
  225.      * They can be either Format objects or Strings.
  226.      * When formatting with object Y,
  227.      * if the object is a NumberFormat, then ((NumberFormat) Y).format(X)
  228.      * is called. Otherwise Y.toString() is called.
  229.      */
  230.     public void setChoices(double[] limits, String formats[]) {
  231.         choiceLimits = limits;
  232.         choiceFormats = formats;
  233.     }
  234.     /**
  235.      * Get the limits passed in the constructor.
  236.      * @return the limits.
  237.      */
  238.     public double[] getLimits() {
  239.         return choiceLimits;
  240.     }
  241.     /**
  242.      * Get the formats passed in the constructor.
  243.      * @return the formats.
  244.      */
  245.     public Object[] getFormats() {
  246.         return choiceFormats;
  247.     }
  248.  
  249.     // Overrides
  250.  
  251.     /**
  252.      * Specialization of format. This method really calls
  253.      * <code>format(double, StringBuffer, FieldPosition)</code>
  254.      * thus the range of longs that are supported is only equal to
  255.      * the range that can be stored by double. This will never be
  256.      * a practical limitation.
  257.      */
  258.     public StringBuffer format(long number, StringBuffer toAppendTo,
  259.                                FieldPosition status) {
  260.         return format((double)number, toAppendTo, status);
  261.     }
  262.  
  263.     public StringBuffer format(double number, StringBuffer toAppendTo,
  264.                                FieldPosition status) {
  265.         // find the number
  266.         int i;
  267.         for (i = 0; i < choiceLimits.length; ++i) {
  268.             if (!(number >= choiceLimits[i])) {
  269.                 // same as number < choiceLimits, except catchs NaN
  270.                 break;
  271.             }
  272.         }
  273.         --i;
  274.         if (i < 0) i = 0;
  275.         // return either a formatted number, or a string
  276.         return toAppendTo.append(choiceFormats[i]);
  277.     }
  278.  
  279.     public Number parse(String text, ParsePosition status) {
  280.         // find the best number (defined as the one with the longest parse)
  281.         int start = status.index;
  282.         int furthest = start;
  283.         double bestNumber = Double.NaN;
  284.         double tempNumber = 0.0;
  285.         for (int i = 0; i < choiceFormats.length; ++i) {
  286.             String tempString = choiceFormats[i];
  287.             if (text.regionMatches(start, tempString, 0, tempString.length())) {
  288.                 status.index = tempString.length();
  289.                 tempNumber = choiceLimits[i];
  290.                 if (status.index > furthest) {
  291.                     furthest = status.index;
  292.                     bestNumber = tempNumber;
  293.                     if (furthest == text.length()) break;
  294.                 }
  295.             }
  296.         }
  297.         status.index = furthest;
  298.         if (status.index == start)
  299.             status.errorIndex = furthest;
  300.         return new Double(bestNumber);
  301.     }
  302.  
  303.     /**
  304.      * Finds the least double greater than d.
  305.      * If NaN, returns same value.
  306.      * <p>Used to make half-open intervals.
  307.      * @see #previousDouble
  308.      */
  309.     public static final double nextDouble (double d) {
  310.         return nextDouble(d,true);
  311.     }
  312.  
  313.     /**
  314.      * Finds the greatest double less than d.
  315.      * If NaN, returns same value.
  316.      * @see #nextDouble
  317.      */
  318.     public static final double previousDouble (double d) {
  319.         return nextDouble(d,false);
  320.     }
  321.  
  322.     /**
  323.      * Overrides Cloneable
  324.      */
  325.     public Object clone()
  326.     {
  327.         ChoiceFormat other = (ChoiceFormat) super.clone();
  328.         // for primitives or immutables, shallow clone is enough
  329.         other.choiceLimits = (double[]) choiceLimits.clone();
  330.         other.choiceFormats = (String[]) choiceFormats.clone();
  331.         return other;
  332.     }
  333.  
  334.     /**
  335.      * Generates a hash code for the message format object.
  336.      */
  337.     public int hashCode() {
  338.         int result = choiceLimits.length;
  339.         if (choiceFormats.length > 0) {
  340.             // enough for reasonable distribution
  341.             result ^= choiceFormats[choiceFormats.length-1].hashCode();
  342.         }
  343.         return result;
  344.     }
  345.  
  346.     /**
  347.      * Equality comparision between two
  348.      */
  349.     public boolean equals(Object obj) {
  350.         if (this == obj)                      // quick check
  351.             return true;
  352.         if (getClass() != obj.getClass())
  353.             return false;
  354.         ChoiceFormat other = (ChoiceFormat) obj;
  355.         return (Utility.arrayEquals(choiceLimits,other.choiceLimits)
  356.             && Utility.arrayEquals(choiceFormats,other.choiceFormats));
  357.     }
  358.     // ===============privates===========================
  359.     private double[] choiceLimits;
  360.     private String[] choiceFormats;
  361.  
  362.     /*
  363.     static final long SIGN          = 0x8000000000000000L;
  364.     static final long EXPONENT      = 0x7FF0000000000000L;
  365.     static final long SIGNIFICAND   = 0x000FFFFFFFFFFFFFL;
  366.  
  367.     private static double nextDouble (double d, boolean positive) {
  368.         if (Double.isNaN(d) || Double.isInfinite(d)) {
  369.                 return d;
  370.             }
  371.         long bits = Double.doubleToLongBits(d);
  372.         long significand = bits & SIGNIFICAND;
  373.         if (bits < 0) {
  374.             significand |= (SIGN | EXPONENT);
  375.         }
  376.         long exponent = bits & EXPONENT;
  377.         if (positive) {
  378.             significand += 1;
  379.             // FIXME fix overflow & underflow
  380.         } else {
  381.             significand -= 1;
  382.             // FIXME fix overflow & underflow
  383.         }
  384.         bits = exponent | (significand & ~EXPONENT);
  385.         return Double.longBitsToDouble(bits);
  386.     }
  387.     */
  388.  
  389.     /*
  390.      * Finds the least double greater than d (if positive == true),
  391.      * or the greatest double less than d (if positive == false).
  392.      * If NaN, returns same value.
  393.      *
  394.      * Does not affect floating-point flags,
  395.      *  provided these member functions do not:
  396.      *          Double.longBitsToDouble ()
  397.      *          Double.doubleToLongBits ()
  398.      *          Double.IsNaN ()
  399.      */
  400.  
  401.     static final long SIGN                = 0x8000000000000000L;
  402.     static final long EXPONENT            = 0x7FF0000000000000L;
  403.     static final long POSITIVEINFINITY    = 0x7FF0000000000000L;
  404.  
  405.     public static double nextDouble (double d, boolean positive) {
  406.  
  407.         /* filter out NaN's */
  408.         if (Double.isNaN(d)) {
  409.             return d;
  410.         }
  411.  
  412.         /* zero's are also a special case */
  413.         if (d == 0.0) {
  414.             double smallestPositiveDouble = Double.longBitsToDouble(1L);
  415.             if (positive) {
  416.                 return smallestPositiveDouble;
  417.             } else {
  418.                 return -smallestPositiveDouble;
  419.             }
  420.         }
  421.  
  422.         /* if entering here, d is a nonzero value */
  423.  
  424.         /* hold all bits in a long for later use */
  425.         long bits = Double.doubleToLongBits(d);
  426.  
  427.         /* strip off the sign bit */
  428.         long magnitude = bits & ~SIGN;
  429.  
  430.         /* if next double away from zero, increase magnitude */
  431.         if ((bits > 0) == positive) {
  432.             if (magnitude != POSITIVEINFINITY) {
  433.                 magnitude += 1;
  434.             }
  435.         }
  436.         /* else decrease magnitude */
  437.         else {
  438.             magnitude -= 1;
  439.         }
  440.  
  441.         /* restore sign bit and return */
  442.         long signbit = bits & SIGN;
  443.         return Double.longBitsToDouble (magnitude | signbit);
  444.     }
  445.  
  446. }
  447.  
  448.