package com.github.mygreen.supercsv.cellprocessor;
import static org.assertj.core.api.Assertions.*;
import static com.github.mygreen.supercsv.tool.TestUtils.*;
import static com.github.mygreen.supercsv.tool.HasCellProcessorAssert.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Comparator;
import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.supercsv.cellprocessor.CellProcessorAdaptor;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.cellprocessor.ift.StringCellProcessor;
import org.supercsv.util.CsvContext;
import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;
import com.github.mygreen.supercsv.annotation.conversion.CsvConversion;
import com.github.mygreen.supercsv.annotation.conversion.CsvLeftPad;
import com.github.mygreen.supercsv.annotation.conversion.CsvLower;
import com.github.mygreen.supercsv.annotation.conversion.CsvRegexReplace;
import com.github.mygreen.supercsv.annotation.conversion.CsvRightPad;
import com.github.mygreen.supercsv.annotation.conversion.CsvUpper;
import com.github.mygreen.supercsv.builder.ProcessorBuilderResolver;
import com.github.mygreen.supercsv.builder.BuildCase;
import com.github.mygreen.supercsv.builder.Configuration;
import com.github.mygreen.supercsv.builder.FieldAccessor;
import com.github.mygreen.supercsv.builder.standard.StringProcessorBuilder;
import com.github.mygreen.supercsv.cellprocessor.conversion.LeftPad;
import com.github.mygreen.supercsv.cellprocessor.conversion.LeftPadFactory;
import com.github.mygreen.supercsv.cellprocessor.conversion.Lower;
import com.github.mygreen.supercsv.cellprocessor.conversion.LowerFactory;
import com.github.mygreen.supercsv.cellprocessor.conversion.RegexReplace;
import com.github.mygreen.supercsv.cellprocessor.conversion.RegexReplaceFactory;
import com.github.mygreen.supercsv.cellprocessor.conversion.RightPad;
import com.github.mygreen.supercsv.cellprocessor.conversion.RightPadFactory;
import com.github.mygreen.supercsv.cellprocessor.conversion.Upper;
import com.github.mygreen.supercsv.cellprocessor.conversion.UpperFactory;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
/**
* {@link ConversionProcessorHandler}のテスタ
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class ConversionProcessorHandlerTest {
@Rule
public TestName name = new TestName();
private Configuration config;
private ProcessorBuilderResolver builderResolver;
private Comparator<Annotation> comparator;
private ConversionProcessorHandler handlerFactory;
private final Class<?>[] groupEmpty = new Class[]{};
@Before
public void setUp() throws Exception {
this.config = new Configuration();
this.builderResolver = config.getBuilderResolver();
this.comparator = config.getAnnoationComparator();
this.handlerFactory = new ConversionProcessorHandler();
}
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(CsvCustomConversion.List.class)
@CsvConversion(CustomConversionFactory.class)
public static @interface CsvCustomConversion {
String text();
Class<?>[] groups() default {};
int order() default 0;
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
CsvCustomConversion[] value();
}
}
private static class CustomConversionFactory implements ConversionProcessorFactory<CsvCustomConversion> {
@Override
public Optional<CellProcessor> create(CsvCustomConversion anno, Optional<CellProcessor> next,
FieldAccessor field, TextFormatter<?> formatter, Configuration config) {
final CustomConversion processor = next.map(n -> new CustomConversion(anno.text(), n))
.orElseGet(() -> new CustomConversion(anno.text()));
return Optional.of(processor);
}
}
private static class CustomConversion extends CellProcessorAdaptor implements StringCellProcessor {
private String text;
CustomConversion(final String text) {
super();
checkPreconditions(text);
this.text = text;
}
CustomConversion(final String text, final CellProcessor next) {
super(next);
checkPreconditions(text);
this.text = text;
}
private static void checkPreconditions(final String text) {
if(text == null) {
throw new NullPointerException("text should not be null.");
}
}
@Override
public <T> T execute(final Object value, final CsvContext context) {
if(value == null) {
return next.execute(value, context);
}
// 最後尾に文字列を足す
final String result = value.toString() + text;
return next.execute(result, context);
}
}
// テスト用のグループ1
private interface Group1 { }
// テスト用のグループ2
private interface Group2 { }
@CsvBean
private static class TestCsv {
@CsvColumn(number=1)
String col_no_anno;
@CsvColumn(number=2)
@CsvRegexReplace(regex="無期限", replacement="9999/12/31")
String col_register;
@CsvColumn(number=3)
@CsvCustomConversion(text = "Hello!")
String col_custom;
@CsvColumn(number=4)
@CsvRegexReplace(regex="無期限", replacement="9999/12/31", groups=Group1.class)
@CsvCustomConversion(text = "Hello!")
String col_groups;
@CsvColumn(number=5)
@CsvCustomConversion(text="Hello!", order=1)
@CsvCustomConversion(text="World!", order=2)
String col_order1;
@CsvColumn(number=6)
@CsvCustomConversion(text="Hello!", order=2)
@CsvCustomConversion(text="World!", order=1)
String col_order2;
@CsvColumn(number=7)
@CsvLeftPad(size=5, order=1, cases={})
@CsvRightPad(size=5, order=2, cases={BuildCase.Read})
@CsvUpper(order=3, cases={BuildCase.Write})
@CsvLower(order=4, cases={BuildCase.Read, BuildCase.Write})
String col_cases;
}
@Test
public void testCreate_noAnno() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_no_anno", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
assertThat(processor.isPresent()).isEqualTo(false);
}
/**
* 未登録のアノテーション
*/
@Test
public void testCreate_unregister() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_register", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
assertThat(processor).isEmpty();
}
/**
* 登録済みのアノテーション
*/
@Test
public void testCreate_register() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_register", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
handlerFactory.register(CsvRegexReplace.class, new RegexReplaceFactory());
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
printCellProcessorChain(processor.get(), name.getMethodName());
CellProcessor actual = processor.get();
assertThat(actual).isInstanceOf(RegexReplace.class);
{
String input = "無期限";
String output = "9999/12/31";
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(output);
}
}
/**
* 独自のアノテーション
*/
@Test
public void testCreate_custom() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_custom", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
printCellProcessorChain(processor.get(), name.getMethodName());
CellProcessor actual = processor.get();
assertThat(actual).isInstanceOf(CustomConversion.class);
{
String input = "こんにちは";
String output = "こんにちはHello!";
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(output);
}
}
/**
* 属性groupsの指定
*/
@Test
public void testCreate_groups() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_groups", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
handlerFactory.register(CsvRegexReplace.class, new RegexReplaceFactory());
{
// グループの指定なし - デフォルトグループ
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
printCellProcessorChain(processor.get(), name.getMethodName());
CellProcessor actual = processor.get();
assertThat(actual).isInstanceOf(CustomConversion.class);
String input = "こんにちは";
String output = "こんにちはHello!";
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(output);
}
{
// グループの指定あり
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, new Class[]{Group1.class});
printCellProcessorChain(processor.get(), name.getMethodName());
CellProcessor actual = processor.get();
assertThat(actual).isInstanceOf(RegexReplace.class);
String input = "無期限";
String output = "9999/12/31";
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(output);
}
}
/**
* 属性orderの指定 1
*/
@Test
public void testCreate_order1() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_order1", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
handlerFactory.register(CsvRegexReplace.class, new RegexReplaceFactory());
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
printCellProcessorChain(processor.get(), name.getMethodName());
CellProcessor actual = processor.get();
assertThat(actual).isInstanceOf(CustomConversion.class);
String input = "こんにちは";
String output = "こんにちはHello!World!";
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(output);
}
/**
* 属性orderの指定 2
*/
@Test
public void testCreate_order2() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_order2", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
handlerFactory.register(CsvRegexReplace.class, new RegexReplaceFactory());
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
printCellProcessorChain(processor.get(), name.getMethodName());
CellProcessor actual = processor.get();
assertThat(actual).isInstanceOf(CustomConversion.class);
String input = "こんにちは";
String output = "こんにちはWorld!Hello!";
assertThat((Object)actual.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(output);
}
/**
* 属性casesの指定 - 読み込み時の場合
*/
@Test
public void testCreate_cases_read() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_cases", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
handlerFactory.register(CsvLeftPad.class, new LeftPadFactory());
handlerFactory.register(CsvRightPad.class, new RightPadFactory());
handlerFactory.register(CsvUpper.class, new UpperFactory());
handlerFactory.register(CsvLower.class, new LowerFactory());
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Read, groupEmpty);
printCellProcessorChain(processor, name.getMethodName());
CellProcessor actual = processor.get();
// 指定したcasesを持つかどうか
assertThat(actual).hasCellProcessor(LeftPad.class)
.hasCellProcessor(RightPad.class)
.hasCellProcessor(Lower.class);
}
/**
* 属性casesの指定 - 書き込み時の場合
*/
@Test
public void testCreate_cases_write() {
FieldAccessor field = getFieldAccessor(TestCsv.class, "col_cases", comparator);
StringProcessorBuilder builder = (StringProcessorBuilder) builderResolver.resolve(String.class);
TextFormatter<String> formatter = builder.getFormatter(field, config);
handlerFactory.register(CsvLeftPad.class, new LeftPadFactory());
handlerFactory.register(CsvRightPad.class, new RightPadFactory());
handlerFactory.register(CsvUpper.class, new UpperFactory());
handlerFactory.register(CsvLower.class, new LowerFactory());
Optional<CellProcessor> processor = handlerFactory.create(Optional.empty(), field, formatter, config, BuildCase.Write, groupEmpty);
printCellProcessorChain(processor, name.getMethodName());
CellProcessor actual = processor.get();
// 指定したcasesを持つかどうか
assertThat(actual).hasCellProcessor(LeftPad.class)
.hasCellProcessor(Upper.class)
.hasCellProcessor(Lower.class);
}
}