View Javadoc
1   /*
2    * Copyright 2016 Andrew Rucker Jones.
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 com.opencsv.bean;
17  
18  import com.opencsv.ICSVParser;
19  import com.opencsv.exceptions.CsvDataTypeMismatchException;
20  import org.apache.commons.beanutils.BeanUtilsBean;
21  import org.apache.commons.beanutils.ConversionException;
22  import org.apache.commons.beanutils.ConvertUtilsBean;
23  import org.apache.commons.beanutils.locale.LocaleConvertUtilsBean;
24  import org.apache.commons.lang3.StringUtils;
25  
26  import java.util.Locale;
27  import java.util.ResourceBundle;
28  
29  /**
30   * This class wraps fields from the reflection API in order to handle
31   * translation of primitive types and to add a "required" flag.
32   *
33   * @author Andrew Rucker Jones
34   * @since 4.2 (previously BeanFieldPrimitiveTypes since 3.8)
35   */
36  public class ConverterPrimitiveTypes extends AbstractCsvConverter {
37  
38      /**
39       * The formatter for all inputs to wrapped and unwrapped primitive
40       * types when a specific locale is not required.
41       * <p>Either this or {@link #readLocaleConverter} should be used, and the
42       * other should always be {@code null}.</p>
43       * <p><em>It is absolutely critical that access to this member variable is
44       * always synchronized!</em></p>
45       */
46      protected final ConvertUtilsBean readConverter;
47  
48      /**
49       * The formatter for all inputs to wrapped and unwrapped primitive
50       * types when a specific locale is required.
51       * <p>Either this or {@link #readConverter} should be used, and the other
52       * should always be {@code null}.</p>
53       * <p><em>It is absolutely critical that access to this member variable is
54       * always synchronized!</em></p>
55       */
56      protected final LocaleConvertUtilsBean readLocaleConverter;
57  
58      /**
59       * The formatter for all inputs from wrapped and unwrapped primitive
60       * types when a specific locale is not required.
61       * <p>Either this or {@link #writeLocaleConverter} should be used, and the
62       * other should always be {@code null}.</p>
63       * <p><em>It is absolutely critical that access to this member variable is
64       * always synchronized!</em></p>
65       */
66      protected final ConvertUtilsBean writeConverter;
67  
68      /**
69       * The formatter for all inputs from wrapped and unwrapped primitive
70       * types when a specific locale is required.
71       * <p>Either this or {@link #writeConverter} should be used, and the other
72       * should always be {@code null}.</p>
73       * <p><em>It is absolutely critical that access to this member variable is
74       * always synchronized!</em></p>
75       */
76      protected final LocaleConvertUtilsBean writeLocaleConverter;
77  
78      /**
79       * @param type    The class of the type of the data being processed
80       * @param locale   If not null or empty, specifies the locale used for
81       *                 converting locale-specific data types
82       * @param writeLocale   If not null or empty, specifies the locale used for
83       *                 converting locale-specific data types for writing
84       * @param errorLocale The locale to use for error messages.
85       */
86      public ConverterPrimitiveTypes(Class<?> type, String locale, String writeLocale, Locale errorLocale) {
87          super(type, locale, writeLocale, errorLocale);
88          if(this.locale == null) {
89              readConverter = BeanUtilsBean.getInstance().getConvertUtils();
90              readConverter.register(true, false, 0);
91              readLocaleConverter = null;
92          }
93          else {
94              readLocaleConverter = new LocaleConvertUtilsBean();
95              readLocaleConverter.setDefaultLocale(this.locale);
96              readConverter = null;
97          }
98          if(this.writeLocale == null) {
99              writeConverter = BeanUtilsBean.getInstance().getConvertUtils();
100             writeConverter.register(true, false, 0);
101             writeLocaleConverter = null;
102         }
103         else {
104             writeLocaleConverter = new LocaleConvertUtilsBean();
105             writeLocaleConverter.setDefaultLocale(this.writeLocale);
106             writeConverter = null;
107         }
108     }
109 
110     @Override
111     public Object convertToRead(String value)
112             throws CsvDataTypeMismatchException {
113         Object o = null;
114 
115         if (StringUtils.isNotBlank(value) || (value != null && type.equals(String.class))) {
116             try {
117                 if(readConverter != null) {
118                     synchronized (readConverter) {
119                         o = readConverter.convert(value, type);
120                     }
121                 }
122                 else {
123                     synchronized (readLocaleConverter) {
124                         o = readLocaleConverter.convert(value, type);
125                     }
126                 }
127             } catch (ConversionException e) {
128                 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(
129                         value, type, String.format(
130                                 ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("conversion.impossible"),
131                                 value, type.getCanonicalName()));
132                 csve.initCause(e);
133                 throw csve;
134             }
135         }
136         return o;
137     }
138     
139     /**
140      * This method takes the current value of the field in question in the bean
141      * passed in and converts it to a string.
142      * It works for all of the primitives, wrapped primitives,
143      * {@link java.lang.String}, {@link java.math.BigDecimal}, and
144      * {@link java.math.BigInteger}.
145      * 
146      * @throws CsvDataTypeMismatchException If there is an error converting
147      *   value to a string
148      */
149     // The rest of the Javadoc is automatically inherited from the base class.
150     @Override
151     public String convertToWrite(Object value)
152             throws CsvDataTypeMismatchException {
153         String result = null;
154         if(value != null) {
155             try {
156                 if(writeConverter != null) {
157                     synchronized (writeConverter) {
158                         result = writeConverter.convert(value);
159                     }
160                 }
161                 else {
162                     synchronized (writeLocaleConverter) {
163                         result = writeLocaleConverter.convert(value);
164                     }
165                 }
166             }
167             catch(ConversionException e) {
168                 CsvDataTypeMismatchException csve = new CsvDataTypeMismatchException(
169                         ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("field.not.primitive"));
170                 csve.initCause(e);
171                 throw csve;
172             }
173         }
174         return result;
175     }
176 }