View Javadoc
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 }