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