1 package integrationTest.Bug252;
2
3 import com.opencsv.bean.*;
4 import com.opencsv.exceptions.CsvDataTypeMismatchException;
5 import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
6 import org.apache.commons.lang3.StringUtils;
7 import org.junit.jupiter.api.Test;
8
9 import java.io.StringWriter;
10 import java.io.Writer;
11 import java.lang.annotation.ElementType;
12 import java.lang.annotation.Retention;
13 import java.lang.annotation.RetentionPolicy;
14 import java.lang.annotation.Target;
15 import java.math.BigDecimal;
16 import java.text.DecimalFormat;
17 import java.text.NumberFormat;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Locale;
21
22 import static org.junit.jupiter.api.Assertions.assertEquals;
23
24 class MiniCaseCsvTest {
25
26 @Test
27 void testExportByName() {
28
29 final CsvNameExporter csvExporter = new CsvNameExporter();
30 final ExportRow exportRow = new ExportRowName();
31 exportRow.setNumber(new BigDecimal("1.23456789"));
32
33 final List<ExportRow> exportRowList = new ArrayList<>();
34 exportRowList.add(exportRow);
35
36
37 final String export = csvExporter.export(exportRowList, ExportRowName.class);
38
39 final String secondLine = export.split("\n")[1];
40 assertEquals("1.2", secondLine);
41 }
42
43 @Test
44 void testExportByPosition() {
45
46 final CsvExporter csvExporter = new CsvExporter();
47 final ExportRow exportRow = new ExportRowPosition();
48 exportRow.setNumber(new BigDecimal("1.23456789"));
49
50 final List<ExportRow> exportRowList = new ArrayList<>();
51 exportRowList.add(exportRow);
52
53
54 final String export = csvExporter.export(exportRowList, ExportRowPosition.class);
55
56 final String secondLine = export.split("\n")[1];
57 assertEquals("1.2", secondLine);
58 }
59
60 class CsvExporter<T extends ExportRow> {
61 public String export(final List<T> rows, final Class<T> clazz) {
62
63 final Writer writer = new StringWriter();
64 final ColumnPositionMappingStrategy<T> strategy = new ColumnPositionMappingStrategy<T>() {
65 @Override
66 public String[] generateHeader(final T row) {
67 return new String[]{"number"};
68 }
69 };
70
71 strategy.setType(clazz);
72 strategy.setColumnMapping("number");
73
74 final StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
75 .withMappingStrategy(strategy)
76 .withQuotechar('"')
77 .withSeparator(',')
78 .withLineEnd("\n")
79 .withApplyQuotesToAll(false)
80 .withOrderedResults(true)
81 .build();
82
83 try {
84 beanToCsv.write(rows);
85 return writer.toString();
86 } catch (final CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
87 throw new RuntimeException("Error while exporting to csv", e);
88 }
89 }
90
91 }
92
93 class CsvNameExporter<T extends ExportRow> {
94 public String export(final List<T> rows, final Class<T> clazz) {
95
96 final Writer writer = new StringWriter();
97 final HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<T>();
98
99 strategy.setType(clazz);
100
101 final StatefulBeanToCsv<T> beanToCsv = new StatefulBeanToCsvBuilder<T>(writer)
102 .withMappingStrategy(strategy)
103 .withQuotechar('"')
104 .withSeparator(',')
105 .withLineEnd("\n")
106 .withApplyQuotesToAll(false)
107 .withOrderedResults(true)
108 .build();
109
110 try {
111 beanToCsv.write(rows);
112 return writer.toString();
113 } catch (final CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
114 throw new RuntimeException("Error while exporting to csv", e);
115 }
116 }
117
118 }
119
120 interface ExportRow {
121 void setNumber(BigDecimal bigDecimal);
122 }
123
124 public static class ExportRowName implements ExportRow {
125
126 @CsvCustomBindByName(column = "number", converter = BigDecimalFormatterTest.class)
127 @CsvBigDecimalFormat(format = "20.1")
128 private BigDecimal number;
129
130 @Override
131 public void setNumber(final BigDecimal bigDecimal) {
132 this.number = bigDecimal;
133 }
134
135 public BigDecimal getNumber() {
136 return number;
137 }
138 }
139
140 public static class ExportRowPosition implements ExportRow {
141
142 @CsvCustomBindByPosition(position = 0, converter = BigDecimalFormatterTest.class)
143 @CsvBigDecimalFormat(format = "20.1")
144 private BigDecimal number;
145
146 @Override
147 public void setNumber(final BigDecimal bigDecimal) {
148 this.number = bigDecimal;
149 }
150
151 public BigDecimal getNumber() {
152 return number;
153 }
154
155 }
156
157 public static class BigDecimalFormatterTest<T, I> extends AbstractBeanField<T, I> {
158
159 public BigDecimalFormatterTest() {
160 }
161
162 @Override
163 protected Object convert(final String value) {
164 throw new UnsupportedOperationException("Only writing of CSV files is possible");
165 }
166
167 @Override
168 protected String convertToWrite(final Object value) {
169 if (value == null) {
170 return "";
171 } else {
172 final CsvBigDecimalFormat annotation = field.getAnnotation(CsvBigDecimalFormat.class);
173 final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
174 if (annotation != null) {
175 ((DecimalFormat) numberFormat).applyPattern(parseFormat(annotation.format()));
176 }
177 return numberFormat.format(value);
178 }
179 }
180
181 private String parseFormat(final String format) {
182 final String[] split = format.split("\\.");
183 if (split.length == 2) {
184 final int digits = Integer.parseInt(split[0]);
185 final int decimalPlaces = Integer.parseInt(split[1]);
186 return StringUtils.repeat("#", digits) + "." + StringUtils.repeat("#", decimalPlaces);
187 } else {
188 final int digits = Integer.parseInt(split[0]);
189 return StringUtils.repeat("#", digits);
190 }
191 }
192 }
193
194 @Retention(RetentionPolicy.RUNTIME)
195 @Target(ElementType.FIELD)
196 public @interface CsvBigDecimalFormat {
197
198 String format();
199
200 }
201
202 }