1 /* 2 * Copyright 2007 Kasper B. Graversen 3 * 4 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 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 package org.supercsv.cellprocessor; 17 18 import java.math.BigDecimal; 19 import java.text.DecimalFormatSymbols; 20 21 import org.supercsv.cellprocessor.ift.CellProcessor; 22 import org.supercsv.cellprocessor.ift.StringCellProcessor; 23 import org.supercsv.exception.SuperCsvCellProcessorException; 24 import org.supercsv.util.CsvContext; 25 26 /** 27 * Convert a String to a BigDecimal. It uses the String constructor of BigDecimal (<tt>new BigDecimal("0.1")</tt>) as it 28 * yields predictable results (see {@link BigDecimal}). 29 * <p> 30 * If the data uses a character other than "." as a decimal separator (Germany uses "," for example), then use the 31 * constructor that accepts a <tt>DecimalFormatSymbols</tt> object, as it will convert the character to a "." before 32 * creating the BigDecimal. Likewise if the data contains a grouping separator (Germany uses "." for example) then 33 * supplying a <tt>DecimalFormatSymbols</tt> object will allow grouping separators to be removed before parsing. 34 * 35 * @since 1.30 36 * @author Kasper B. Graversen 37 * @author James Bassett 38 */ 39 public class ParseBigDecimal extends CellProcessorAdaptor implements StringCellProcessor { 40 41 private static final char DEFAULT_DECIMAL_SEPARATOR = '.'; 42 43 private final DecimalFormatSymbols symbols; 44 45 /** 46 * Constructs a new <tt>ParseBigDecimal</tt> processor, which converts a String to a BigDecimal. 47 */ 48 public ParseBigDecimal() { 49 this.symbols = null; 50 } 51 52 /** 53 * Constructs a new <tt>ParseBigDecimal</tt> processor, which converts a String to a BigDecimal using the supplied 54 * <tt>DecimalFormatSymbols</tt> object to convert any decimal separator to a "." before creating the BigDecimal. 55 * 56 * @param symbols 57 * the decimal format symbols, containing the decimal separator 58 * @throws NullPointerException 59 * if symbols is null 60 */ 61 public ParseBigDecimal(final DecimalFormatSymbols symbols) { 62 super(); 63 checkPreconditions(symbols); 64 this.symbols = symbols; 65 } 66 67 /** 68 * Constructs a new <tt>ParseBigDecimal</tt> processor, which converts a String to a BigDecimal then calls the next 69 * processor in the chain. 70 * 71 * @param next 72 * the next processor in the chain 73 * @throws NullPointerException 74 * if next is null 75 */ 76 public ParseBigDecimal(final CellProcessor next) { 77 super(next); 78 this.symbols = null; 79 } 80 81 /** 82 * Constructs a new <tt>ParseBigDecimal</tt> processor, which converts a String to a BigDecimal using the supplied 83 * <tt>DecimalFormatSymbols</tt> object to convert any decimal separator to a "." before creating the BigDecimal, 84 * then calls the next processor in the chain. 85 * 86 * @param symbols 87 * the decimal format symbols, containing the decimal separator 88 * @param next 89 * the next processor in the chain 90 * @throws NullPointerException 91 * if symbols or next is null 92 */ 93 public ParseBigDecimal(final DecimalFormatSymbols symbols, final CellProcessor next) { 94 super(next); 95 checkPreconditions(symbols); 96 this.symbols = symbols; 97 } 98 99 /** 100 * Checks the preconditions for creating a new ParseBigDecimal processor. 101 * 102 * @param symbols 103 * the decimal format symbols, containing the decimal separator 104 * @throws NullPointerException 105 * if symbols is null 106 */ 107 private static void checkPreconditions(final DecimalFormatSymbols symbols) { 108 if( symbols == null ) { 109 throw new NullPointerException("symbols should not be null"); 110 } 111 } 112 113 /** 114 * {@inheritDoc} 115 * 116 * @throws SuperCsvCellProcessorException 117 * if value is null, isn't a String, or can't be parsed as a BigDecimal 118 */ 119 public Object execute(final Object value, final CsvContext context) { 120 validateInputNotNull(value, context); 121 122 final BigDecimal result; 123 if( value instanceof String ) { 124 final String s = (String) value; 125 try { 126 if( symbols == null ) { 127 result = new BigDecimal(s); 128 } else { 129 result = new BigDecimal(fixSymbols(s, symbols)); 130 } 131 } 132 catch(final NumberFormatException e) { 133 throw new SuperCsvCellProcessorException(String.format("'%s' could not be parsed as a BigDecimal", 134 value), context, this, e); 135 } 136 } else { 137 throw new SuperCsvCellProcessorException(String.class, value, context, this); 138 } 139 140 return next.execute(result, context); 141 } 142 143 /** 144 * Fixes the symbols in the input String (currently only decimal separator and grouping separator) so that the 145 * String can be parsed as a BigDecimal. 146 * 147 * @param s 148 * the String to fix 149 * @param symbols 150 * the decimal format symbols 151 * @return the fixed String 152 */ 153 private static String fixSymbols(final String s, final DecimalFormatSymbols symbols) { 154 final char groupingSeparator = symbols.getGroupingSeparator(); 155 final char decimalSeparator = symbols.getDecimalSeparator(); 156 return s.replace(String.valueOf(groupingSeparator), "").replace(decimalSeparator, DEFAULT_DECIMAL_SEPARATOR); 157 } 158 }