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 }