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