package com.github.mygreen.supercsv.builder;
import static org.junit.Assert.*;
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.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;
import java.util.ResourceBundle;
import javax.xml.XMLConstants;
import javax.xml.bind.DataBindingException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.xml.sax.SAXException;
import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;
import com.github.mygreen.supercsv.annotation.format.CsvFormat;
import com.github.mygreen.supercsv.cellprocessor.format.AbstractTextFormatter;
import com.github.mygreen.supercsv.cellprocessor.format.TextFormatter;
import com.github.mygreen.supercsv.cellprocessor.format.TextParseException;
import com.github.mygreen.supercsv.cellprocessor.format.TextPrintException;
import com.github.mygreen.supercsv.exception.SuperCsvInvalidAnnotationException;
import com.github.mygreen.supercsv.exception.SuperCsvValidationException;
import com.github.mygreen.supercsv.localization.MessageResolver;
import com.github.mygreen.supercsv.localization.ResourceBundleMessageResolver;
import com.github.mygreen.supercsv.validation.CsvExceptionConverter;
/**
* {@link GeneralProcessorBuilder}のテスタ
* <p>独自のクラスタイプ</p>
*
* @since 2.0
* @author T.TSUCHIE
*
*/
public class GeneralProcessorBuilderTest {
@Rule
public TestName name = new TestName();
private BeanMappingFactory beanMappingFactory;
private CsvExceptionConverter exceptionConverter;
private final Class<?>[] groupEmpty = new Class[]{};
private MessageResolver testMessageResolver;
@Before
public void setUp() {
this.beanMappingFactory = new BeanMappingFactory();
this.exceptionConverter = new CsvExceptionConverter();
this.testMessageResolver = new ResourceBundleMessageResolver(ResourceBundle.getBundle("TestMessages"));
}
private static final String TEST_VALUE_STR_VALID = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "\n<sample id=\"id1\">"
+ "\n <value>Hello World!</value>"
+ "\n</sample>"
+ "\n";
private static final String TEST_VALUE_STR_WRONG = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+ "\n<sample>"
+ "\n <value>Hello World!</value>"
+ "\n</sample>"
+ "\n";
private static final SampleObject TEST_VALUE_OBJ_VALID = new SampleObject("id1", "Hello World!");
private static final SampleObject TEST_VALUE_OBJ_WRONG = new SampleObject(null, "Hello World!");
/**
* アノテーション{@link CsvFormat}で独自のフォーマッタを指定する
*/
@CsvBean
private static class TestCustomFormatterCsv {
@CsvColumn(number=1)
@CsvFormat(formatter=SampleObjectFormatter.class)
private SampleObject col_default;
@CsvColumn(number=2)
@CsvFormat(formatter=SampleObjectFormatter.class, message="テストメッセージ")
private SampleObject col_message;
@CsvColumn(number=3, builder=SampleObjectBuilder.class)
private SampleObject col_builder;
}
/**
* 独自のクラスの場合 - フォーマッタの指定がない場合
*
*/
@CsvBean
private static class TestErrorNotFormatterCsv {
@CsvColumn(number=1)
private SampleObject col_default;
}
/**
* 独自のクラス - JAXBによるXML変換
*
*/
@XmlRootElement(name="sample")
@XmlAccessorType(XmlAccessType.FIELD)
private static class SampleObject {
@XmlAttribute(required=true)
private String id;
@XmlElement(name="value")
private String data;
public SampleObject() {
}
public SampleObject(final String id, final String data) {
this.id = id;
this.data = data;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(!(obj instanceof SampleObject)) {
return false;
}
SampleObject other = (SampleObject) obj;
if(data == null) {
if(other.data != null) {
return false;
}
} else if(!data.equals(other.data)) {
return false;
}
if(id == null) {
if(other.id != null) {
return false;
}
} else if(!id.equals(other.id)) {
return false;
}
return true;
}
}
private static class SampleObjectFormatter extends AbstractTextFormatter<SampleObject> {
private static Schema schema;
static {
try {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schema = factory.newSchema(GeneralProcessorBuilderTest.class.getResource("SampleObject.xsd"));
} catch (SAXException e) {
throw new RuntimeException("fail loading schema.", e);
}
}
private static JAXBContext context;
static {
try {
context = JAXBContext.newInstance(SampleObject.class);
} catch (JAXBException e) {
throw new RuntimeException("fail create jaxbcontext.", e);
}
}
@Override
public SampleObject parse(final String text) {
try {
Unmarshaller unmashaller = context.createUnmarshaller();
unmashaller.setSchema(schema);
// SampleObject object = JAXB.unmarshal(new StringReader(text), SampleObject.class);
SampleObject object = (SampleObject) unmashaller.unmarshal(new StringReader(text));
return object;
} catch(JAXBException | DataBindingException e) {
throw new TextParseException(text, SampleObject.class);
}
}
@Override
public String print(final SampleObject object) {
try(StringWriter writer = new StringWriter()) {
Marshaller marshaller = context.createMarshaller();
marshaller.setSchema(schema);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, writer);
// JAXB.marshal(object, writer);
writer.flush();
String text = writer.toString();
return text;
} catch(IOException | JAXBException | DataBindingException e) {
throw new TextPrintException(object, e);
}
}
}
private static class SampleObjectBuilder extends AbstractProcessorBuilder<SampleObject> {
@Override
protected TextFormatter<SampleObject> getDefaultFormatter(FieldAccessor field,
Configuration config) {
return new SampleObjectFormatter();
}
}
/**
* 独自のクラスで、@CsvFormatの指定がない場合
*/
@Test
public void testCustomFormatter_noFormatter() {
assertThatThrownBy(() -> beanMappingFactory.create(TestErrorNotFormatterCsv.class, groupEmpty))
.isInstanceOf(SuperCsvInvalidAnnotationException.class)
.hasMessage("'%s' において、アノテーション @CsvFormat の付与は必須です。",
TestErrorNotFormatterCsv.class.getName() + "#col_default");
}
@Test
public void testCustomFormatter_read() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_default").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
{
// null input
String input = null;
SampleObject expected = null;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// valid input
String input = TEST_VALUE_STR_VALID;
SampleObject expected = TEST_VALUE_OBJ_VALID;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// wrong input - wrong format
String input = "abc";
try {
processor.execute(input, ANONYMOUS_CSVCONTEXT);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
SuperCsvValidationException validationException = (SuperCsvValidationException)e;
assertThat(validationException.isParedError()).isTrue();
assertThat(validationException.getRejectedValue()).isEqualTo(input);
assertThat(validationException.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.cellprocessor.format.ParseProcessor.violated}");
assertThat(validationException.getMessageVariables()).isEmpty();
}
}
{
// wrong input - wrong bind
String input = TEST_VALUE_STR_WRONG;
try {
processor.execute(input, ANONYMOUS_CSVCONTEXT);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
SuperCsvValidationException validationException = (SuperCsvValidationException)e;
assertThat(validationException.isParedError()).isTrue();
assertThat(validationException.getRejectedValue()).isEqualTo(input);
assertThat(validationException.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.cellprocessor.format.ParseProcessor.violated}");
assertThat(validationException.getMessageVariables()).isEmpty();
}
}
}
@Test
public void testCustomFormatter_write() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_default").get();
CellProcessor processor = columnMapping.getCellProcessorForWriting();
printCellProcessorChain(processor, name.getMethodName());
{
// null input
SampleObject input = null;
String expected = null;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// valid input
SampleObject input = TEST_VALUE_OBJ_VALID;
String expected = TEST_VALUE_STR_VALID;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// wrong input - wrong bind
SampleObject input = TEST_VALUE_OBJ_WRONG;
try {
processor.execute(input, ANONYMOUS_CSVCONTEXT);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
SuperCsvValidationException validationException = (SuperCsvValidationException)e;
assertThat(validationException.isParedError()).isFalse();
assertThat(validationException.getRejectedValue()).isEqualTo(input);
assertThat(validationException.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.cellprocessor.format.PrintProcessor.violated}");
assertThat(validationException.getMessageVariables()).isEmpty();
}
}
}
@Test
public void testCustomBuilder_read() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_builder").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
{
// null input
String input = null;
SampleObject expected = null;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// valid input
String input = TEST_VALUE_STR_VALID;
SampleObject expected = TEST_VALUE_OBJ_VALID;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// wrong input - wrong format
String input = "abc";
try {
processor.execute(input, ANONYMOUS_CSVCONTEXT);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
SuperCsvValidationException validationException = (SuperCsvValidationException)e;
assertThat(validationException.isParedError()).isTrue();
assertThat(validationException.getRejectedValue()).isEqualTo(input);
assertThat(validationException.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.cellprocessor.format.ParseProcessor.violated}");
assertThat(validationException.getMessageVariables()).isEmpty();
}
}
{
// wrong input - wrong bind
String input = TEST_VALUE_STR_WRONG;
try {
processor.execute(input, ANONYMOUS_CSVCONTEXT);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
SuperCsvValidationException validationException = (SuperCsvValidationException)e;
assertThat(validationException.isParedError()).isTrue();
assertThat(validationException.getRejectedValue()).isEqualTo(input);
assertThat(validationException.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.cellprocessor.format.ParseProcessor.violated}");
assertThat(validationException.getMessageVariables()).isEmpty();
}
}
}
@Test
public void testCustomBuilder_write() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_builder").get();
CellProcessor processor = columnMapping.getCellProcessorForWriting();
printCellProcessorChain(processor, name.getMethodName());
{
// null input
SampleObject input = null;
String expected = null;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// valid input
SampleObject input = TEST_VALUE_OBJ_VALID;
String expected = TEST_VALUE_STR_VALID;
assertThat((Object)processor.execute(input, ANONYMOUS_CSVCONTEXT)).isEqualTo(expected);
}
{
// wrong input - wrong bind
SampleObject input = TEST_VALUE_OBJ_WRONG;
try {
processor.execute(input, ANONYMOUS_CSVCONTEXT);
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
SuperCsvValidationException validationException = (SuperCsvValidationException)e;
assertThat(validationException.isParedError()).isFalse();
assertThat(validationException.getRejectedValue()).isEqualTo(input);
assertThat(validationException.getValidationMessage()).isEqualTo("{com.github.mygreen.supercsv.cellprocessor.format.PrintProcessor.violated}");
assertThat(validationException.getMessageVariables()).isEmpty();
}
}
}
@Test
public void testErrorMessage_default() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_default").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
String input = "abc";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("[2行, 1列] : 項目「col_default」の値(abc)の書式は不正です。");
}
}
@Test
public void testErrorMessage_default_write() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_default").get();
CellProcessor processor = columnMapping.getCellProcessorForWriting();
printCellProcessorChain(processor, name.getMethodName());
SampleObject input = TEST_VALUE_OBJ_WRONG;
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("[2行, 1列] : 項目「col_default」の値の書き込みに失敗しました。");
}
}
/**
* アノテーションの属性messageの指定
*/
@Test
public void testErrorMessage_message() {
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_message").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
String input = "abc";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("テストメッセージ");
}
}
/**
* プロパティファイルのtypeMismatchからメッセージを取得する場合
*/
@Test
public void testErrorMessage_typeMismatch() {
// 独自のメッセージに入れ替え
exceptionConverter.setMessageResolver(testMessageResolver);
BeanMapping<TestCustomFormatterCsv> beanMapping = beanMappingFactory.create(TestCustomFormatterCsv.class, groupEmpty);
ColumnMapping columnMapping = beanMapping.getColumnMapping("col_default").get();
CellProcessor processor = columnMapping.getCellProcessorForReading();
printCellProcessorChain(processor, name.getMethodName());
String input = "abc";
try {
processor.execute(input, testCsvContext(columnMapping, input));
fail();
} catch(Exception e) {
assertThat(e).isInstanceOf(SuperCsvValidationException.class);
List<String> messages = exceptionConverter.convertAndFormat((SuperCsvValidationException)e, beanMapping);
assertThat(messages).hasSize(1)
.contains("[2行, 1列] : 項目「col_default」の値は、XMLの値として不正です。");
}
}
}