Mega Code Archive

 
Categories / Java / GWT
 

A simple number formatting parsing class

/*  * Copyright 2006 Robert Hanson <iamroberthanson AT gmail.com>  *   * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *   *    http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ package org.gwtwidgets.client.util; /**  * <dl>  * <dt><b>Title: </b><dd>Decimal Format</dd>  * <p>  * <dt><b>Description: </b><dd>This is a simple number formatting/ parsing class. Besides the simple number formatting  * it also interprets shortcuts for thousand (k) million (m) and billion (b).<p/>  * This Number Format class was adapted from the public domain javascript class found at   * http://www.mredkj.com/javascript/nfdocs.html </dd>  * <p>  * </dl>  * @author <a href="mailto:jasone@greenrivercomputing.com">Jason Essington</a>  * @version $Revision: 0.0 $  */ public class NumberFormat {     public static final String COMMA = ",";     public static final String PERIOD = ".";     public static final char DASH = '-';     public static final char LEFT_PAREN = '(';     public static final char RIGHT_PAREN = ')';     // k/m/b Shortcuts     public static final String THOUSAND = "k";     public static final String MILLION = "m";     public static final String BILLION = "b";          // currency position constants     public static final int CUR_POS_LEFT_OUTSIDE = 0;     public static final int CUR_POS_LEFT_INSIDE = 1;     public static final int CUR_POS_RIGHT_INSIDE = 2;     public static final int CUR_POS_RIGHT_OUTSIDE = 3;     // negative format constants     public static final int NEG_LEFT_DASH = 0;     public static final int NEG_RIGHT_DASH = 1;     public static final int NEG_PARENTHESIS = 2;     // constant to signal that fixed precision is not to be used     public static final int ARBITRARY_PRECISION = -1;     private String inputDecimalSeparator = PERIOD; // decimal character used on the original string     private boolean showGrouping = true;     private String groupingSeparator = COMMA; // thousands grouping character     private String decimalSeparator = PERIOD; // decimal point character     private boolean showCurrencySymbol = false;     private String currencySymbol = "$";     private int currencySymbolPosition = CUR_POS_LEFT_OUTSIDE;     private int negativeFormat = NEG_LEFT_DASH;     private boolean isNegativeRed = false; // wrap the output in html that will display red?     private int decimalPrecision = 0;     private boolean useFixedPrecision = false;     private boolean truncate = false; // truncate to decimalPrecision rather than rounding?      private boolean isPercentage = false; // should the result be displayed as a percentage?     private NumberFormat()     {     }     /**      * returns the default instance of NumberFormat      * @return      */     public static NumberFormat getInstance ()     {         NumberFormat nf = new NumberFormat();         return nf;     }     /**      * Returns a currency instance of number format      * @return      */     public static NumberFormat getCurrencyInstance ()     {         return getCurrencyInstance("$", true);     }     /**      * Returns a currency instance of number format that uses curSymbol as the currency symbol      * @param curSymbol      * @return      */     public static NumberFormat getCurrencyInstance (String curSymbol)     {         return getCurrencyInstance(curSymbol, true);     }     /**      * Returns a currency instance of number format that uses curSymbol as the currency symbol       * and either commas or periods as the thousands separator.      * @param curSymbol Currency Symbol      * @param useCommas true, uses commas as the thousands separator, false uses periods      * @return      */     public static NumberFormat getCurrencyInstance (String curSymbol, boolean useCommas)     {         NumberFormat nf = new NumberFormat();         nf.isCurrency(true);         nf.setCurrencySymbol(curSymbol);         if (!useCommas) {             nf.setDecimalSeparator(COMMA);             nf.setGroupingSeparator(PERIOD);         }         nf.setFixedPrecision(2);         return nf;     }     /**      * Returns an instance that formats numbers as integers.      * @return      */     public static NumberFormat getIntegerInstance ()     {         NumberFormat nf = new NumberFormat();         nf.setShowGrouping(false);         nf.setFixedPrecision(0);         return nf;     }     public static NumberFormat getPercentInstance ()     {         NumberFormat nf = new NumberFormat();         nf.isPercentage(true);         nf.setFixedPrecision(2);         nf.setShowGrouping(false);         return nf;     }     public String format (String num)     {         return toFormatted(parse(num));     }     public double parse (String num)     {         return asNumber(num, inputDecimalSeparator);     }     /**      * Static routine that attempts to create a double out of the      * supplied text. This routine is a bit smarter than Double.parseDouble()      * @param num      * @return      */     public static double parseDouble (String num, String decimalChar)     {         return asNumber(num, decimalChar);     }     public static double parseDouble (String num)     {         return parseDouble(num, PERIOD);     }     public void setInputDecimalSeparator (String val)     {         inputDecimalSeparator = val == null ? PERIOD : val;     }     public void setNegativeFormat (int format)     {         negativeFormat = format;     }     public void setNegativeRed (boolean isRed)     {         isNegativeRed = isRed;     }     public void setShowGrouping (boolean show)     {         showGrouping = show;     }     public void setDecimalSeparator (String separator)     {         decimalSeparator = separator;     }     public void setGroupingSeparator (String separator)     {         groupingSeparator = separator;     }     public void isCurrency (boolean isC)     {         showCurrencySymbol = isC;     }     public void setCurrencySymbol (String symbol)     {         currencySymbol = symbol;     }     public void setCurrencyPosition (int cp)     {         currencySymbolPosition = cp;     }     public void isPercentage (boolean pct)     {         isPercentage = pct;     }     /**      * Sets the number of fixed precision decimal places should be displayed.      * To use arbitrary precision, setFixedPrecision(NumberFormat.ARBITRARY_PRECISION)      * @param places       */     public void setFixedPrecision (int places)     {         useFixedPrecision = places != ARBITRARY_PRECISION;         this.decimalPrecision = places < 0 ? 0 : places;     }     /**      * Causes the number to be truncated rather than rounded to its fixed precision.      * @param trunc      */     public void setTruncate (boolean trunc)     {         truncate = trunc;     }     /**      *       * @param preSep raw number as text      * @param PERIOD incoming decimal point      * @param decimalSeparator outgoing decimal point      * @param groupingSeparator thousands separator      * @return      */     private String addSeparators (String preSep)     {         String nStr = preSep;         int dpos = nStr.indexOf(PERIOD);         String nStrEnd = "";         if (dpos != -1) {             nStrEnd = decimalSeparator + nStr.substring(dpos + 1, nStr.length());             nStr = nStr.substring(0, dpos);         }         int l = nStr.length();         for (int i = l; i > 0; i--) {             nStrEnd = nStr.charAt(i - 1) + nStrEnd;             if (i != 1 && ((l - i + 1) % 3) == 0) nStrEnd = groupingSeparator + nStrEnd;         }         return nStrEnd;     }     protected String toFormatted(double num)     {         String nStr;         if (isPercentage) num = num * 100;         nStr = useFixedPrecision ? toFixed(Math.abs(getRounded(num)), decimalPrecision) : Double.toString(num);         nStr = showGrouping ? addSeparators(nStr) : nStr.replaceAll("\\" + PERIOD, decimalSeparator);         String c0 = "";         String n0 = "";         String c1 = "";         String n1 = "";         String n2 = "";         String c2 = "";         String n3 = "";         String c3 = "";         String negSignL = "" + ((negativeFormat == NEG_PARENTHESIS) ? LEFT_PAREN : DASH);         String negSignR = "" + ((negativeFormat == NEG_PARENTHESIS) ? RIGHT_PAREN : DASH);                  if (currencySymbolPosition == CUR_POS_LEFT_OUTSIDE) {             if (num < 0) {                 if (negativeFormat == NEG_LEFT_DASH || negativeFormat == NEG_PARENTHESIS)                     n1 = negSignL;                 if (negativeFormat == NEG_RIGHT_DASH || negativeFormat == NEG_PARENTHESIS)                     n2 = negSignR;             }             if (showCurrencySymbol) c0 = currencySymbol;         }         else if (currencySymbolPosition == CUR_POS_LEFT_INSIDE) {             if (num < 0) {                 if (negativeFormat == NEG_LEFT_DASH || negativeFormat == NEG_PARENTHESIS)                     n0 = negSignL;                 if (negativeFormat == NEG_RIGHT_DASH || negativeFormat == NEG_PARENTHESIS)                     n3 = negSignR;             }             if (showCurrencySymbol) c1 = currencySymbol;         }         else if (currencySymbolPosition == CUR_POS_RIGHT_INSIDE) {             if (num < 0) {                 if (negativeFormat == NEG_LEFT_DASH || negativeFormat == NEG_PARENTHESIS)                     n0 = negSignL;                 if (negativeFormat == NEG_RIGHT_DASH || negativeFormat == NEG_PARENTHESIS)                     n3 = negSignR;             }             if (showCurrencySymbol) c2 = currencySymbol;         }         else if (currencySymbolPosition == CUR_POS_RIGHT_OUTSIDE) {             if (num < 0) {                 if (negativeFormat == NEG_LEFT_DASH || negativeFormat == NEG_PARENTHESIS)                     n1 = negSignL;                 if (negativeFormat == NEG_RIGHT_DASH || negativeFormat == NEG_PARENTHESIS)                     n2 = negSignR;             }             if (showCurrencySymbol) c3 = currencySymbol;         }         nStr = c0 + n0 + c1 + n1 + nStr + n2 + c2 + n3 + c3 + (isPercentage ? "%" : "");         if (isNegativeRed && num < 0) {             nStr = "<font color='red'>" + nStr + "</font>";         }         return nStr;    }     /**      * javascript only rounds to whole numbers, so we need to shift our decimal right,       * then round, then shift the decimal back left.      * @param val      * @return      */     private double getRounded (double val)     {         double exp = Math.pow(10, decimalPrecision);         double rounded = val * exp;         if (truncate)             rounded = rounded >= 0 ? Math.floor(rounded) : Math.ceil(rounded);         else             rounded = Math.round(rounded);         return rounded / exp;     }     private static native String toFixed(double val, int places) /*-{         return val.toFixed(places);     }-*/;     private static double asNumber(String val, String inputDecimalValue)     {         String newVal = val;         boolean isPercentage = false;         // remove % if there is one         if (newVal.indexOf('%') != -1) {             newVal = newVal.replaceAll("\\%", "");             isPercentage = true;         }         // convert abbreviations for thousand, million and billion         newVal = newVal.toLowerCase().replaceAll(BILLION, "000000000");         newVal = newVal.replaceAll(MILLION, "000000");         newVal = newVal.replaceAll(THOUSAND, "000");         // remove any characters that are not digit, decimal separator, +, -, (, ), e, or E               String re = "[^\\" + inputDecimalValue + "\\d\\-\\+\\(\\)eE]";         newVal = newVal.replaceAll(re, "");         // ensure that the first decimal separator is a . and remove the rest.         int index = newVal.indexOf(inputDecimalValue);         if (index != -1) {             newVal = newVal.substring(0, index)                     + PERIOD                     + (newVal.substring(index + inputDecimalValue.length()).replaceAll("\\"                             + inputDecimalValue, ""));         }         // convert right dash and paren negatives to left dash negative         if (newVal.charAt(newVal.length() - 1) == DASH) {             newVal = newVal.substring(0, newVal.length() - 1);             newVal = DASH + newVal;         }         else if (newVal.charAt(0) == LEFT_PAREN                 && newVal.charAt(newVal.length() - 1) == RIGHT_PAREN) {             newVal = newVal.substring(1, newVal.length() - 1);             newVal = DASH + newVal;         }         Double parsed;         try {             parsed = new Double(newVal);             if (parsed.isInfinite() || parsed.isNaN()) parsed = new Double(0);         }         catch (NumberFormatException e) {             parsed = new Double(0);         }         return isPercentage ? parsed.doubleValue() / 100 : parsed.doubleValue();     } }