-
-
Save HtetWaiYanLin/248f5ff8a3068e3862b3d50c99fc8d67 to your computer and use it in GitHub Desktop.
Revisions
-
ammmze revised this gist
Feb 13, 2019 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -73,8 +73,7 @@ else if(field.isAnnotationPresent(CsvDate.class)) { } private String extractHeaderName(final BeanField beanField) { if (beanField == null || beanField.getField() == null) { return StringUtils.EMPTY; } -
ammmze revised this gist
Feb 12, 2019 . 4 changed files with 216 additions and 93 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,69 @@ package com.example.csv; import com.opencsv.bean.ConverterDate; import com.opencsv.exceptions.CsvDataTypeMismatchException; import java.lang.reflect.InvocationTargetException; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import java.util.Locale; import org.apache.commons.lang3.StringUtils; public class ConverterDateAndJavaTime extends ConverterDate { private static String DEFAULT_FORMAT = "yyyyMMdd'T'HHmmss"; private static String DEFAULT_DATE_ONLY_FORMAT = "yyyyMMdd"; private static String DEFAULT_TIME_ONLY_FORMAT = "HHmmss"; private DateTimeFormatter format; /** * @param type The type of the field being populated * @param formatString The string to use for formatting the date. See * {@link com.opencsv.bean.CsvDate#value()} * @param locale If not null or empty, specifies the locale used for * converting locale-specific data types * @param errorLocale The locale to use for error messages. */ public ConverterDateAndJavaTime(Class<?> type, String locale, Locale errorLocale, String formatString) { super(type, locale, errorLocale, formatString); // if the type is LocalDate and using the default format, we know it will fail. Lets use just the date portion of the default date format if (DEFAULT_FORMAT.equals(formatString) && LocalDate.class.isAssignableFrom(type)) { formatString = DEFAULT_DATE_ONLY_FORMAT; } else if (DEFAULT_FORMAT.equals(formatString) && LocalTime.class.isAssignableFrom(type)) { formatString = DEFAULT_TIME_ONLY_FORMAT; } if (this.locale != null) { format = DateTimeFormatter.ofPattern(formatString, this.locale); } else { format = DateTimeFormatter.ofPattern(formatString); } } @Override public Object convertToRead(String value) throws CsvDataTypeMismatchException { if (StringUtils.isNotBlank(value) && Temporal.class.isAssignableFrom(type)) { return convertToTemporal(value); } return super.convertToRead(value); } @Override public String convertToWrite(Object value) throws CsvDataTypeMismatchException { if (value != null && Temporal.class.isAssignableFrom(value.getClass())) { return convertFromTemporal((Temporal) value); } return super.convertToWrite(value); } private Temporal convertToTemporal(String value) { try { return (Temporal) type.getMethod("parse", CharSequence.class, DateTimeFormatter.class).invoke(null, value, format); } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) { throw new RuntimeException("Failed to invoke the parse method of " + type.getName(), e); } } private String convertFromTemporal(Temporal value) { return format.format(value); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,84 @@ package com.example.csv; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import com.opencsv.bean.CsvConverter; import com.opencsv.bean.CsvDate; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Locale; import org.junit.Test; public class ConverterDateAndJavaTimeTest { private static final Locale ERROR_LOCALE = Locale.getDefault(); private static final String LOCALE = ERROR_LOCALE.toString(); private static String DEFAULT_FORMAT; static { try { DEFAULT_FORMAT = (String) CsvDate.class.getMethod("value").getDefaultValue(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } @Test public void convertToRead_When_ValueIsBlank_Expect_ToReceiveNull() throws Exception { assertNull(converter(LocalDate.class, DEFAULT_FORMAT).convertToRead("")); } @Test public void convertToRead_When_ValueIsValidAndTypeIsLocalDate_Expect_ToReceiveLocalDate() throws Exception { LocalDate actual = (LocalDate) converter(LocalDate.class, DEFAULT_FORMAT).convertToRead("20190214"); assertNotNull(actual); assertEquals(2019, actual.getYear()); assertEquals(2, actual.getMonthValue()); assertEquals(14, actual.getDayOfMonth()); } @Test public void convertToRead_When_ValueIsValidAndTypeIsLocalTime_Expect_ToReceiveLocalTime() throws Exception { LocalTime actual = (LocalTime) converter(LocalTime.class, DEFAULT_FORMAT).convertToRead("221546"); assertNotNull(actual); assertEquals(22, actual.getHour()); assertEquals(15, actual.getMinute()); assertEquals(46, actual.getSecond()); } @Test public void convertToRead_When_ValueIsValidAndTypeIsLocalDateTime_Expect_ToReceiveLocalDateTime() throws Exception { LocalDateTime actual = (LocalDateTime) converter(LocalDateTime.class, DEFAULT_FORMAT).convertToRead("20190214T221546"); assertNotNull(actual); assertEquals(2019, actual.getYear()); assertEquals(2, actual.getMonthValue()); assertEquals(14, actual.getDayOfMonth()); assertEquals(22, actual.getHour()); assertEquals(15, actual.getMinute()); assertEquals(46, actual.getSecond()); } @Test public void convertToWrite_When_ValueIsLocalDate_Expect_ToReceiveFormattedDate() throws Exception { String actual = converter(LocalDate.class, DEFAULT_FORMAT).convertToWrite(LocalDate.of(2019, 2, 14)); assertEquals("20190214", actual); } @Test public void convertToWrite_When_ValueIsLocalTime_Expect_ToReceiveFormattedTime() throws Exception { String actual = converter(LocalTime.class, DEFAULT_FORMAT).convertToWrite(LocalTime.of(22, 15, 46)); assertEquals("221546", actual); } @Test public void convertToWrite_When_ValueIsLocalDateTime_Expect_ToReceiveFormattedDateTime() throws Exception { String actual = converter(LocalDateTime.class, DEFAULT_FORMAT).convertToWrite(LocalDateTime.of(2019, 2, 14,22, 15, 46)); assertEquals("20190214T221546", actual); } private CsvConverter converter(Class type, String format) { return new ConverterDateAndJavaTime(type, LOCALE, ERROR_LOCALE, format); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,120 +1,89 @@ package com.example.csv; import com.opencsv.bean.AbstractCsvConverter; import com.opencsv.bean.BeanField; import com.opencsv.bean.CsvBindByName; import com.opencsv.bean.CsvConverter; import com.opencsv.bean.CsvCustomBindByName; import com.opencsv.bean.CsvDate; import com.opencsv.bean.HeaderColumnNameMappingStrategy; import com.opencsv.bean.comparator.LiteralComparator; import com.opencsv.exceptions.CsvBadConverterException; import com.opencsv.exceptions.CsvRequiredFieldEmptyException; import java.lang.reflect.Field; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; public class HeaderColumnNameAndOrderMappingStrategy<T> extends HeaderColumnNameMappingStrategy<T> { public HeaderColumnNameAndOrderMappingStrategy(Class<T> type) { setType(type); } @Override public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException { // overriding this method to allow us to preserve the header column name casing String[] header = super.generateHeader(bean); final int numColumns = findMaxFieldIndex(); if (!isAnnotationDriven() || numColumns == -1) { return header; } header = new String[numColumns + 1]; BeanField beanField; for (int i = 0; i <= numColumns; i++) { beanField = findField(i); String columnHeaderName = extractHeaderName(beanField); header[i] = columnHeaderName; } return header; } @Override protected void loadFieldMap() throws CsvBadConverterException { // overriding this method to support setting column order by the custom `CsvBindByNameOrder` annotation if (writeOrder == null && type.isAnnotationPresent(CsvBindByNameOrder.class)) { setColumnOrderOnWrite( new LiteralComparator<>(Arrays.stream(type.getAnnotation(CsvBindByNameOrder.class).value()) .map(String::toUpperCase).toArray(String[]::new))); } super.loadFieldMap(); } @Override protected CsvConverter determineConverter(Field field, Class<?> elementType, String locale, Class<? extends AbstractCsvConverter> customConverter) throws CsvBadConverterException { // overrides the converter for the `CsvDate` to use our custom converter that supports java.time api // A custom converter always takes precedence if specified. if(customConverter != null && !customConverter.equals(AbstractCsvConverter.class)) { return super.determineConverter(field, elementType, locale, customConverter); } // Perhaps a date instead else if(field.isAnnotationPresent(CsvDate.class)) { String formatString = field.getAnnotation(CsvDate.class).value(); return new ConverterDateAndJavaTime(elementType, locale, errorLocale, formatString); } return super.determineConverter(field, elementType, locale, customConverter); } private String extractHeaderName(final BeanField beanField) { if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { return StringUtils.EMPTY; } if (beanField.getField().isAnnotationPresent(CsvBindByName.class)) { return beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0].column(); } else if (beanField.getField().isAnnotationPresent(CsvCustomBindByName.class)) { return beanField.getField().getDeclaredAnnotationsByType(CsvCustomBindByName.class)[0].column(); } return StringUtils.EMPTY; } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,6 +5,7 @@ This creates a MappingStrategy for use with OpenCSV (specifically tested for gen 1. Preserves the column name casing in the `@CsvBindByName` annotation 2. Adds a `@CsvBindByNameOrder` annotation you can apply to the bean class to define the order of the columns. * Any field not included in the order, but is still annotated with `@CsvBindName` will still be included AFTER all the columns that have a defined order. Those remaining columns will be added in alphabetical order (this is the default behavior of the `HeaderColumnNameMappingStrategy`) 3. Overrides the converter used with `@CsvDate` to use a custom converter that adds support for the java time api (`LocalDate`, `LocalTime`, and `LocalDateTime` are tested) ## Usage -
ammmze revised this gist
Feb 12, 2019 . 1 changed file with 8 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,6 +2,7 @@ import com.opencsv.bean.BeanField; import com.opencsv.bean.CsvBindByName; import com.opencsv.bean.CsvCustomBindByName; import com.opencsv.bean.HeaderColumnNameMappingStrategy; import com.opencsv.bean.comparator.LiteralComparator; import com.opencsv.exceptions.CsvBadConverterException; @@ -54,8 +55,12 @@ private String extractHeaderName(final BeanField beanField) { return StringUtils.EMPTY; } if (beanField.getField().isAnnotationPresent(CsvBindByName.class)) { return beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0].column(); } else if (beanField.getField().isAnnotationPresent(CsvCustomBindByName.class)) { return beanField.getField().getDeclaredAnnotationsByType(CsvCustomBindByName.class)[0].column(); } return StringUtils.EMPTY; } } -
ammmze created this gist
Feb 12, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,14 @@ package com.example.csv; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface CsvBindByNameOrder { String[] value() default {}; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,61 @@ package com.example.csv; import com.opencsv.bean.BeanField; import com.opencsv.bean.CsvBindByName; import com.opencsv.bean.HeaderColumnNameMappingStrategy; import com.opencsv.bean.comparator.LiteralComparator; import com.opencsv.exceptions.CsvBadConverterException; import com.opencsv.exceptions.CsvRequiredFieldEmptyException; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; public class HeaderColumnNameAndOrderMappingStrategy<T> extends HeaderColumnNameMappingStrategy<T> { public HeaderColumnNameAndOrderMappingStrategy(Class<T> type) { setType(type); } @Override public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException { // overriding this method to allow us to preserve the header column name casing String[] header = super.generateHeader(bean); final int numColumns = findMaxFieldIndex(); if (!isAnnotationDriven() || numColumns == -1) { return header; } header = new String[numColumns + 1]; BeanField beanField; for (int i = 0; i <= numColumns; i++) { beanField = findField(i); String columnHeaderName = extractHeaderName(beanField); header[i] = columnHeaderName; } return header; } @Override protected void loadFieldMap() throws CsvBadConverterException { // overriding this method to support setting column order by the custom `CsvBindByNameOrder` annotation if (writeOrder == null && type.isAnnotationPresent(CsvBindByNameOrder.class)) { setColumnOrderOnWrite( new LiteralComparator<>(Arrays.stream(type.getAnnotation(CsvBindByNameOrder.class).value()) .map(String::toUpperCase).toArray(String[]::new))); } super.loadFieldMap(); } private String extractHeaderName(final BeanField beanField) { if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { return StringUtils.EMPTY; } final CsvBindByName bindByNameAnnotation = beanField.getField() .getDeclaredAnnotationsByType(CsvBindByName.class)[0]; return bindByNameAnnotation.column(); } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,120 @@ package com.example.csv; import static org.junit.Assert.assertEquals; import com.opencsv.bean.CsvBindByName; import com.opencsv.bean.StatefulBeanToCsv; import com.opencsv.bean.StatefulBeanToCsvBuilder; import java.io.StringWriter; import java.util.Collections; import java.util.List; import org.junit.Test; public class HeaderColumnNameAndOrderMappingStrategyTest { @Test public void getCsv_When_AllNamedColumnsAreListedInTheCsvBindByNameOrderAnnotation_Expect_ToReceiveHeadersInCorrectOrder() throws Exception { String csv = getCsv(Collections.singletonList(new PojoWithAllColumnsInOrderAnnotation()), PojoWithAllColumnsInOrderAnnotation.class); assertEquals("Baz,Foo,Bar", csv.split("\n")[0]); } @Test public void getCsv_When_CustomOrderIsGiven_Expect_ToReceiveColumnDataInTheCorrectOrder() throws Exception { PojoWithAllColumnsInOrderAnnotation pojo = new PojoWithAllColumnsInOrderAnnotation(); pojo.setBar(1); pojo.setBaz(2); pojo.setFoo(3); String csv = getCsv(Collections.singletonList(pojo), PojoWithAllColumnsInOrderAnnotation.class); assertEquals("2,3,1", csv.split("\n")[1]); } @Test public void getCsv_When_SomeNamedColumnsAreListedInTheCsvBindByNameOrderAnnotation_Expect_ToReceiveHeaderInOrderOfTheAnnotationAndAllRemainingOnesAreAddedAlphabetically() throws Exception { String csv = getCsv(Collections.singletonList(new PojoWithFirstColumnInOrderAnnotation()), PojoWithFirstColumnInOrderAnnotation.class); assertEquals("Baz,Bar,Foo", csv.split("\n")[0]); } private <T> String getCsv(List<T> beans, Class<T> type) throws Exception { StringWriter writer = new StringWriter(); StatefulBeanToCsv<T> csvWriter = new StatefulBeanToCsvBuilder<T>(writer) .withApplyQuotesToAll(false) .withMappingStrategy(new HeaderColumnNameAndOrderMappingStrategy<>(type)) .build(); csvWriter.write(beans); return writer.toString(); } @CsvBindByNameOrder({"Baz","Foo","Bar"}) public static class PojoWithAllColumnsInOrderAnnotation { @CsvBindByName(column = "Bar") private Integer bar; @CsvBindByName(column = "Foo") private Integer foo; @CsvBindByName(column = "Baz") private Integer baz; public Integer getBar() { return bar; } public void setBar(Integer bar) { this.bar = bar; } public Integer getFoo() { return foo; } public void setFoo(Integer foo) { this.foo = foo; } public Integer getBaz() { return baz; } public void setBaz(Integer baz) { this.baz = baz; } } @CsvBindByNameOrder({"Baz"}) public static class PojoWithFirstColumnInOrderAnnotation { @CsvBindByName(column = "Bar") private Integer bar; @CsvBindByName(column = "Foo") private Integer foo; @CsvBindByName(column = "Baz") private Integer baz; public Integer getBar() { return bar; } public void setBar(Integer bar) { this.bar = bar; } public Integer getFoo() { return foo; } public void setFoo(Integer foo) { this.foo = foo; } public Integer getBaz() { return baz; } public void setBaz(Integer baz) { this.baz = baz; } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,52 @@ # HeaderColumnNameAndOrderMappingStrategy This creates a MappingStrategy for use with OpenCSV (specifically tested for generating a CSV from beans) which does the following: 1. Preserves the column name casing in the `@CsvBindByName` annotation 2. Adds a `@CsvBindByNameOrder` annotation you can apply to the bean class to define the order of the columns. * Any field not included in the order, but is still annotated with `@CsvBindName` will still be included AFTER all the columns that have a defined order. Those remaining columns will be added in alphabetical order (this is the default behavior of the `HeaderColumnNameMappingStrategy`) ## Usage **Annotate your bean with something like...** ```java @CsvBindByNameOrder({"Foo","Bar"}) public class MyBean { @CsvBindByName(column = "Foo") private String foo; @CsvBindByName(column = "Bar") private String bar; // getter/setters omitted for brevity } ``` **Setup your writer...** ```java List<MyBean> beans = new ArrayList(); MyBean bean = new MyBean(); bean.setFoo("fooit"); bean.setBar("barit"); beans.add(bean); StringWriter writer = new StringWriter(); StatefulBeanToCsv<MyBean> csvWriter = new StatefulBeanToCsvBuilder<MyBean>(writer) .withApplyQuotesToAll(false) .withMappingStrategy(new HeaderColumnNameAndOrderMappingStrategy<>(MyBean.class)) .build(); csvWriter.write(beans); return writer.toString(); ``` **Results** With the above you should get something like... ```csv Foo,Bar fooit,barit ```