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