Coverage Report - org.kuali.rice.core.web.format.CurrencyFormatter
 
Classes in this File Line Coverage Branch Coverage Complexity
CurrencyFormatter
0%
0/66
0%
0/36
4.75
 
 1  
 /*
 2  
  * Copyright 2004 Jonathan M. Lehr
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
 5  
  * You may obtain a copy of the License at
 6  
  *
 7  
  * http://www.apache.org/licenses/LICENSE-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
 10  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 11  
  * governing permissions and limitations under the License.
 12  
  * 
 13  
  * MODIFIED BY THE KUALI FOUNDATION
 14  
  */
 15  
 // begin Kuali Foundation modification
 16  
 package org.kuali.rice.core.web.format;
 17  
 // end Kuali Foundation modification
 18  
 
 19  
 // begin Kuali Foundation modification
 20  
 import java.text.DecimalFormat;
 21  
 import java.text.NumberFormat;
 22  
 import java.text.ParseException;
 23  
 import java.util.regex.Pattern;
 24  
 
 25  
 import org.apache.commons.lang.StringUtils;
 26  
 import org.apache.log4j.Logger;
 27  
 import org.kuali.rice.core.util.RiceKeyConstants;
 28  
 import org.kuali.rice.core.util.type.KualiDecimal;
 29  
 import org.kuali.rice.core.util.type.KualiInteger;
 30  
 
 31  
 /**
 32  
  * begin Kuali Foundation modification
 33  
  * This class is used to format Currency objects.
 34  
  * end Kuali Foundation modification
 35  
  */
 36  0
 public class CurrencyFormatter extends Formatter {
 37  
         
 38  
     private static final long serialVersionUID = -7986953503122821012L;
 39  
     
 40  
         // begin Kuali Foundation modification
 41  
     // original code
 42  
     /*
 43  
     /** The default scale for currency values * /
 44  
     public final static int SCALE = 2;
 45  
 
 46  
     /** The key used to look up the currency error string * /
 47  
     public final static String CURRENCY_ERROR_KEY = "error.currency";
 48  
 
 49  
     public static final String SHOW_SYMBOL = "showCurrencySymbol";
 50  
 
 51  
     static final String PARSE_MSG = "Unable to parse a currency value from ";
 52  
 
 53  
     static final String FORMAT_MSG = "Unable to format a currency value from ";
 54  
     */
 55  0
     private static Logger LOG = Logger.getLogger(CurrencyFormatter.class);
 56  
     public static final String SHOW_SYMBOL = "showCurrencySymbol";
 57  0
     private static final Pattern CURRENCY_PATTERN = Pattern.compile("[-\\(\\)\\$\\.,0-9]*");
 58  0
     private static final Pattern TRAILING_DECIMAL_PATTERN = Pattern.compile("^(\\.[0-9]{0,2}){0,1}\\)?$");
 59  
     // end Kuali Foundation modification
 60  
 
 61  
     /**
 62  
      * begin Kuali Foundation modification
 63  
      * Unformats its argument and returns a KualiDecimal instance initialized with the resulting string value
 64  
      * 
 65  
      * @see org.kuali.rice.core.web.format.Formatter#convertToObject(java.lang.String)
 66  
      * end Kuali Foundation modification
 67  
      */
 68  
     @Override
 69  
     protected Object convertToObject(String target) {
 70  
         // begin Kuali Foundation modification
 71  0
         KualiDecimal value = null;
 72  
 
 73  0
         LOG.debug("convertToObject '" + target + "'");
 74  
 
 75  0
         if (target != null) {
 76  0
             target = target.trim();
 77  
 
 78  0
             String rawString = target;
 79  
 
 80  
             // parseable values are $1.23 and ($1.23), not (1.23)
 81  0
             if (target.startsWith("(")) {
 82  0
                 if (!target.startsWith("($")) {
 83  0
                     target = "($" + StringUtils.substringAfter(target, "(");
 84  
                 }
 85  
             }
 86  
 
 87  
             // Insert currency symbol if absent
 88  0
             if (!(target.startsWith("(") || target.startsWith(getSymbol()))) {
 89  0
                 target = interpolateSymbol(target);
 90  
             }
 91  
 
 92  
             // preemptively detect non-numeric-related symbols, since NumberFormat.parse seems to be silently deleting them
 93  
             // (i.e. 9aaaaaaaaaaaaaaa is silently converted into 9)
 94  0
             if (!CURRENCY_PATTERN.matcher(target).matches()) {
 95  0
                 throw new FormatException("parsing", RiceKeyConstants.ERROR_CURRENCY, rawString);
 96  
             }
 97  
 
 98  
             // preemptively detect String with excessive digits after the decimal, to prevent them from being silently rounded
 99  0
             if (rawString.contains(".") && !TRAILING_DECIMAL_PATTERN.matcher(target.substring(target.indexOf("."), target.length())).matches()) {
 100  0
                 throw new FormatException("parsing", RiceKeyConstants.ERROR_CURRENCY_DECIMAL, rawString);
 101  
             }
 102  
 
 103  
             // actually reformat the numeric value
 104  0
             NumberFormat formatter = getCurrencyInstanceUsingParseBigDecimal();
 105  
             try {
 106  0
                 Number parsedNumber = formatter.parse(target);
 107  0
                 value = new KualiDecimal(parsedNumber.toString());
 108  
             }
 109  0
             catch (NumberFormatException e) {
 110  0
                 throw new FormatException("parsing", RiceKeyConstants.ERROR_CURRENCY, rawString, e);
 111  
             }
 112  0
             catch (ParseException e) {
 113  0
                 throw new FormatException("parsing", RiceKeyConstants.ERROR_CURRENCY, rawString, e);
 114  0
             }
 115  
         }
 116  
 
 117  0
         return value;
 118  
         // end Kuali Foundation modification
 119  
     }
 120  
 
 121  
     protected String interpolateSymbol(String target) {
 122  0
         if (target.startsWith("-")) {
 123  0
             int dashPos = target.indexOf('-');
 124  0
             int symbolPos = target.indexOf(getSymbol());
 125  0
             int index = (dashPos > symbolPos ? dashPos : symbolPos);
 126  0
             return "($" + target.substring(index + 1) + ")";
 127  
         }
 128  0
         return target.startsWith("(") ? "($" + target.indexOf("(" + 1) : "$" + target;
 129  
     }
 130  
 
 131  
     protected String removeSymbol(String target) {
 132  0
         int index = target.indexOf(getSymbol());
 133  0
         String prefix = (index > 0 ? target.substring(0, index) : "");
 134  0
         return prefix + target.substring(index + 1);
 135  
     }
 136  
 
 137  
     protected String getSymbol() {
 138  0
         return "$";
 139  
     }
 140  
 
 141  
     protected boolean showSymbol() {
 142  0
         String showSymbol = (settings == null ? null : (String) settings.get(SHOW_SYMBOL));
 143  0
         return Boolean.valueOf(showSymbol).booleanValue();
 144  
     }
 145  
 
 146  
     /**
 147  
      * Returns a string representation of its argument formatted as a currency value.
 148  
      * 
 149  
      * begin Kuali Foundation modification
 150  
      * @see org.kuali.rice.core.web.format.Formatter#format(java.lang.Object)
 151  
      * end Kuali Foundation modification
 152  
      */
 153  
     // begin Kuali Foundation modification
 154  
     @Override
 155  
     // end Kuali Foundation modification
 156  
     public Object format(Object obj) {
 157  
         // begin Kuali Foundation modification
 158  
         // major code rewrite, original code commented
 159  
         /*
 160  
         if (obj == null)
 161  
             return null;
 162  
 
 163  
         NumberFormat formatter = NumberFormat.getCurrencyInstance();
 164  
         String string = null;
 165  
 
 166  
         try {
 167  
             BigDecimal number = (BigDecimal) obj;
 168  
             number = number.setScale(SCALE, BigDecimal.ROUND_HALF_UP);
 169  
             string = formatter.format(number.doubleValue());
 170  
         } catch (IllegalArgumentException e) {
 171  
             throw new FormatException(FORMAT_MSG + obj, e);
 172  
         } catch (ClassCastException e) {
 173  
             throw new FormatException(FORMAT_MSG + obj, e);
 174  
         }
 175  
 
 176  
         return showSymbol() ? string : removeSymbol(string);
 177  
         */
 178  0
         LOG.debug("format '" + obj + "'");
 179  0
         if (obj == null)
 180  0
             return null;
 181  
 
 182  0
         NumberFormat formatter = getCurrencyInstanceUsingParseBigDecimal();
 183  0
         String string = null;
 184  
 
 185  
         try {
 186  
             Number number;
 187  
 
 188  0
             if (obj instanceof KualiInteger) {
 189  0
                 formatter.setMaximumFractionDigits(0);
 190  0
                 number = (KualiInteger) obj;
 191  
 
 192  
                 // Note that treating the number as a KualiDecimal below is obsolete. But it doesn't do any harm either since
 193  
                 // we already set maximumFractionDigits above.
 194  
             }
 195  
             else {
 196  0
                 number = (KualiDecimal) obj;
 197  
             }
 198  
 
 199  
             // run the incoming KualiDecimal's string representation through convertToObject, so that KualiDecimal objects
 200  
             // containing ill-formatted incoming values will cause the same errors here that ill-formatted Strings do in
 201  
             // convertToObject
 202  0
             KualiDecimal convertedNumber = (KualiDecimal) convertToObject(number.toString());
 203  
 
 204  0
             string = formatter.format(convertedNumber.bigDecimalValue());
 205  
         }
 206  0
         catch (IllegalArgumentException e) {
 207  0
             throw new FormatException("formatting", RiceKeyConstants.ERROR_CURRENCY, obj.toString(), e);
 208  
         }
 209  0
         catch (ClassCastException e) {
 210  0
             throw new FormatException("formatting", RiceKeyConstants.ERROR_CURRENCY, obj.toString(), e);
 211  0
         }
 212  
 
 213  0
         return showSymbol() ? string : removeSymbol(string);
 214  
         // end Kuali Foundation modification
 215  
     }
 216  
 
 217  
     // begin Kuali Foundation modification
 218  
     /**
 219  
      * retrieves a currency formatter instance and sets ParseBigDecimal to true to fix [KULEDOCS-742]
 220  
      * 
 221  
      * @return CurrencyInstance
 222  
      */
 223  
     static final NumberFormat getCurrencyInstanceUsingParseBigDecimal() {
 224  0
         NumberFormat formatter = NumberFormat.getCurrencyInstance();
 225  0
         if (formatter instanceof DecimalFormat) {
 226  0
             ((DecimalFormat) formatter).setParseBigDecimal(true);
 227  
         }
 228  0
         return formatter;
 229  
     }
 230  
     // end Kuali Foundation modification
 231  
     
 232  
     // begin Kuali Foundation modification
 233  
     /**
 234  
      * Validates a currency string by passing it into the convertToObject method and determining if conversion succeeded.
 235  
      * 
 236  
      * @param currencyString The string to attempt to format.
 237  
      * @return True if no exceptions occurred when parsing and the conversion returned a non-null value; false otherwise.
 238  
      * @see org.kuali.rice.core.web.format.CurrencyFormatter#convertToObject(java.lang.String)
 239  
      */
 240  
     public boolean validate(String currencyString) {
 241  0
             Object currencyObject = null;
 242  
             try {
 243  0
                     currencyObject = convertToObject(currencyString);
 244  0
             } catch (Exception e) {
 245  0
                     currencyObject = null;
 246  0
             }
 247  0
             return (currencyObject != null);
 248  
     }
 249  
     // end Kuali Foundation modification
 250  
 }