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.text.DecimalFormat;
19  
20  import org.supercsv.cellprocessor.ift.DoubleCellProcessor;
21  import org.supercsv.cellprocessor.ift.LongCellProcessor;
22  import org.supercsv.cellprocessor.ift.StringCellProcessor;
23  import org.supercsv.exception.SuperCsvCellProcessorException;
24  import org.supercsv.util.CsvContext;
25  
26  /**
27   * Converts a double into a formatted string using the {@link DecimalFormat} class and the default locale. This is
28   * useful, when you need to show numbers with a specific number of digits.
29   * <p>
30   * Please be aware that the constructors that use <tt>DecimalFormat</tt> are not thread-safe, so it is generally better
31   * to use the constructors that accept a date format String.
32   * <p>
33   * In the format string, the following characters are defined as : <br>
34   * 
35   * <pre>
36   * 0   - means Digit
37   * #   - means Digit, zero shows as absent (works only as zero padding on the right hand side of the number)
38   * .   - means Decimal separator or monetary decimal separator
39   * -   - means Minus sign
40   * ,   - means Grouping separator
41   * </pre>
42   * 
43   * <br>
44   * If you want to convert from a String to a decimal, use the {@link ParseDouble} or {@link ParseBigDecimal} processor.
45   * 
46   * @since 1.50
47   * @author Kasper B. Graversen
48   * @author James Bassett
49   */
50  public class FmtNumber extends CellProcessorAdaptor implements DoubleCellProcessor, LongCellProcessor {
51  	
52  	/** the decimal format string */
53  	private final String decimalFormat;
54  	
55  	/** the decimal format object - not thread safe */
56  	private final DecimalFormat formatter;
57  	
58  	/**
59  	 * Constructs a new <tt>FmtNumber</tt> processor, which converts a double into a formatted string using the supplied
60  	 * decimal format String. This constructor is thread-safe.
61  	 * 
62  	 * @param decimalFormat
63  	 *            the decimal format String (see {@link DecimalFormat})
64  	 * @throws NullPointerException
65  	 *             if decimalFormat is null
66  	 */
67  	public FmtNumber(final String decimalFormat) {
68  		super();
69  		checkPreconditions(decimalFormat);
70  		this.decimalFormat = decimalFormat;
71  		this.formatter = null;
72  	}
73  	
74  	/**
75  	 * Constructs a new <tt>FmtNumber</tt> processor, which converts a double into a formatted string using the supplied
76  	 * decimal format String, then calls the next processor in the chain. This constructor is thread-safe.
77  	 * 
78  	 * @param decimalFormat
79  	 *            the decimal format String (see {@link DecimalFormat})
80  	 * @param next
81  	 *            the next processor in the chain
82  	 * @throws NullPointerException
83  	 *             if decimalFormat or next is null
84  	 */
85  	public FmtNumber(final String decimalFormat, final StringCellProcessor next) {
86  		super(next);
87  		checkPreconditions(decimalFormat);
88  		this.decimalFormat = decimalFormat;
89  		this.formatter = null;
90  	}
91  	
92  	/**
93  	 * Constructs a new <tt>FmtNumber</tt> processor, which converts a double into a formatted string using the supplied
94  	 * decimal format. This constructor is not thread-safe.
95  	 * 
96  	 * @param formatter
97  	 *            the DecimalFormat
98  	 * @throws NullPointerException
99  	 *             if formatter is null
100 	 */
101 	public FmtNumber(final DecimalFormat formatter) {
102 		super();
103 		checkPreconditions(formatter);
104 		this.formatter = formatter;
105 		this.decimalFormat = null;
106 	}
107 	
108 	/**
109 	 * Constructs a new <tt>FmtNumber</tt> processor, which converts a double into a formatted string using the supplied
110 	 * decimal format, then calls the next processor in the chain. This constructor is not thread-safe.
111 	 * 
112 	 * @param formatter
113 	 *            the DecimalFormat
114 	 * @param next
115 	 *            the next processor in the chain
116 	 * @throws NullPointerException
117 	 *             if formatter or next is null
118 	 */
119 	public FmtNumber(final DecimalFormat formatter, final StringCellProcessor next) {
120 		super(next);
121 		checkPreconditions(formatter);
122 		this.formatter = formatter;
123 		this.decimalFormat = null;
124 	}
125 	
126 	/**
127 	 * Checks the preconditions for creating a new FmtNumber processor with a date format String.
128 	 * 
129 	 * @param dateFormat
130 	 *            the date format String
131 	 * @throws NullPointerException
132 	 *             if dateFormat is null
133 	 */
134 	private static void checkPreconditions(final String dateFormat) {
135 		if( dateFormat == null ) {
136 			throw new NullPointerException("dateFormat should not be null");
137 		}
138 	}
139 	
140 	/**
141 	 * Checks the preconditions for creating a new FmtNumber processor with a DecimalFormat.
142 	 * 
143 	 * @param formatter
144 	 *            the DecimalFormat
145 	 * @throws NullPointerException
146 	 *             if formatter is null
147 	 */
148 	private static void checkPreconditions(final DecimalFormat formatter) {
149 		if( formatter == null ) {
150 			throw new NullPointerException("formatter should not be null");
151 		}
152 	}
153 	
154 	/**
155 	 * {@inheritDoc}
156 	 * 
157 	 * @throws SuperCsvCellProcessorException
158 	 *             if value is null or not a Number, or if an invalid decimalFormat String was supplied
159 	 */
160 	public Object execute(final Object value, final CsvContext context) {
161 		validateInputNotNull(value, context);
162 		
163 		if( !(value instanceof Number) ) {
164 			throw new SuperCsvCellProcessorException(Number.class, value, context, this);
165 		}
166 		
167 		// create a new DecimalFormat if one is not supplied
168 		final DecimalFormat decimalFormatter;
169 		try {
170 			decimalFormatter = formatter != null ? formatter : new DecimalFormat(decimalFormat);
171 		}
172 		catch(IllegalArgumentException e) {
173 			throw new SuperCsvCellProcessorException(
174 				String.format("'%s' is not a valid decimal format", decimalFormat), context, this, e);
175 		}
176 		
177 		final String result = decimalFormatter.format(value);
178 		return next.execute(result, context);
179 	}
180 }