View Javadoc
1   package com.github.mygreen.supercsv.cellprocessor.format;
2   
3   import java.math.BigDecimal;
4   import java.math.BigInteger;
5   import java.text.DecimalFormat;
6   import java.text.NumberFormat;
7   import java.text.ParseException;
8   import java.text.ParsePosition;
9   import java.util.HashMap;
10  import java.util.Map;
11  import java.util.Objects;
12  import java.util.Optional;
13  
14  /**
15   * 書式を指定した数値のフォーマッタ。
16   * 
17   * @since 1.2
18   * @author T.TSUCHIE
19   *
20   */
21  public class NumberFormatWrapper<T extends Number> extends AbstractTextFormatter<T> {
22      
23      private final NumberFormat formatter;
24      
25      private final Class<T> type;
26      
27      private final boolean lenient;
28      
29      public NumberFormatWrapper(final NumberFormat formatter, final Class<T> type) {
30          this(formatter, type, false);
31      }
32      
33      public NumberFormatWrapper(final NumberFormat formatter, final Class<T> type, final boolean lenient) {
34          Objects.requireNonNull(formatter);
35          Objects.requireNonNull(type);
36          
37          this.formatter = (NumberFormat) formatter.clone();
38          this.type = type;
39          this.lenient = lenient;
40          
41      }
42      
43      @Override
44      public synchronized String print(final Number number) {
45          return formatter.format(number);
46      }
47      
48      @Override
49      public T parse(final String text) {
50          return parse(type, text);
51      }
52      
53      /**
54       * 指定した数値のクラスに文字列をパースする。
55       * <p>Java標準のクラスタイプをサポートします。</p>
56       * 
57       * @param type 変換する数値のクラス。
58       * @param text パース対象の文字列。
59       * @return パースした数値のオブジェクト。
60       * @throws IllegalArgumentException サポートしていないクラスタイプが指定された場合。
61       * @throws TextParseException fail convert Number or BigDecimal.
62       */
63      @SuppressWarnings("unchecked")
64      synchronized <N extends Number> N parse(final Class<N> type, final String text) {
65          
66          final Number result;
67          if(lenient) {
68              try {
69                  result = formatter.parse(text);
70              } catch(ParseException e) {
71                  throw new TextParseException(text, type, e);
72              }
73          } else {
74              ParsePosition position = new ParsePosition(0);
75              result = formatter.parse(text, position);
76              
77              if(position.getIndex() != text.length()) {
78                  throw new TextParseException(text, type, String.format("Cannot parse '%s' using fromat %s", text, getPattern()));
79              }
80          }
81          
82          try {
83              if(result instanceof BigDecimal) {
84                  // if set DecimalFormat#setParseBigDecimal(true)
85                  return (N) convertWithBigDecimal(type, (BigDecimal) result, text);
86                  
87              } else {
88                  return (N) convertWithNumber(type, result, text);
89              }
90          } catch(NumberFormatException | ArithmeticException e) {
91              throw new TextParseException(text, type, e);
92          }
93          
94      }
95      
96      private Number convertWithNumber(final Class<? extends Number> type, final Number number, final String str) {
97          
98          if(Byte.class.isAssignableFrom(type) || byte.class.isAssignableFrom(type)) {
99              return number.byteValue();
100             
101         } else if(Short.class.isAssignableFrom(type) || short.class.isAssignableFrom(type)) {
102             return number.shortValue() ;
103             
104         } else if(Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) {
105             return number.intValue();
106             
107         } else if(Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) {
108             return number.longValue();
109             
110         } else if(Float.class.isAssignableFrom(type) || float.class.isAssignableFrom(type)) {
111             return number.floatValue();
112             
113         } else if(Double.class.isAssignableFrom(type) || double.class.isAssignableFrom(type)) {
114             return number.doubleValue();
115             
116         } else if(type.isAssignableFrom(BigInteger.class)) {
117             return new BigInteger(str);
118             
119         } else if(type.isAssignableFrom(BigDecimal.class)) {
120             return new BigDecimal(str);
121             
122         }
123         
124         throw new IllegalArgumentException(String.format("not support class type : %s", type.getCanonicalName()));
125     }
126     
127     private Number convertWithBigDecimal(final Class<? extends Number> type, final BigDecimal number, final String str) {
128         
129         if(Byte.class.isAssignableFrom(type) || byte.class.isAssignableFrom(type)) {
130             return lenient ? number.byteValue() : number.byteValueExact();
131             
132         } else if(Short.class.isAssignableFrom(type) || short.class.isAssignableFrom(type)) {
133             return lenient ? number.shortValue() : number.shortValueExact();
134             
135         } else if(Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) {
136             return lenient ? number.intValue() : number.intValueExact();
137             
138         } else if(Long.class.isAssignableFrom(type) || long.class.isAssignableFrom(type)) {
139             return lenient ? number.longValue() : number.longValueExact();
140             
141         } else if(Float.class.isAssignableFrom(type) || float.class.isAssignableFrom(type)) {
142             return number.floatValue();
143             
144         } else if(Double.class.isAssignableFrom(type) || double.class.isAssignableFrom(type)) {
145             return number.doubleValue();
146             
147         } else if(type.isAssignableFrom(BigInteger.class)) {
148             return lenient ? number.toBigInteger() : number.toBigIntegerExact();
149             
150         } else if(type.isAssignableFrom(BigDecimal.class)) {
151             return number;
152             
153         }
154         
155         throw new IllegalArgumentException(String.format("not support class type : %s", type.getCanonicalName()));
156         
157     }
158     
159     @Override
160     public Optional<String> getPattern() {
161         
162         if(formatter instanceof DecimalFormat) {
163             DecimalFormat df = (DecimalFormat) formatter;
164             return Optional.of(df.toPattern());
165         }
166         
167         return Optional.empty();
168         
169     }
170     
171     /**
172      * パースする際に、数値に変換可能な部分のみ変換するかどうか。
173      * <p>例えば、trueのときは、{@literal 123abc} をパースする際に{@literal 123}を数値としてパースします。
174      *   <br>falseの場合は、例外{@link TextParseException}をスローします。
175      * </p>
176      * @return trueの場合、曖昧にパースします。
177      */
178     public boolean isLenient() {
179         return lenient;
180     }
181     
182     @Override
183     public Map<String, Object> getMessageVariables() {
184         
185         final Map<String, Object> vars = new HashMap<>();
186         getPattern().ifPresent(p -> vars.put("pattern", p));
187         
188         return vars;
189     }
190     
191 }