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 }