All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.mygreen.supercsv.builder.BeanMappingFactory Maven / Gradle / Ivy
package com.github.mygreen.supercsv.builder;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.exception.SuperCsvReflectionException;
import com.github.mygreen.supercsv.annotation.CsvBean;
import com.github.mygreen.supercsv.annotation.CsvColumn;
import com.github.mygreen.supercsv.annotation.CsvPartial;
import com.github.mygreen.supercsv.annotation.CsvPostRead;
import com.github.mygreen.supercsv.annotation.CsvPostWrite;
import com.github.mygreen.supercsv.annotation.CsvPreRead;
import com.github.mygreen.supercsv.annotation.CsvPreWrite;
import com.github.mygreen.supercsv.annotation.DefaultGroup;
import com.github.mygreen.supercsv.exception.SuperCsvInvalidAnnotationException;
import com.github.mygreen.supercsv.localization.MessageBuilder;
import com.github.mygreen.supercsv.validation.CsvValidator;
/**
* BeanからCSVのマッピング情報を作成するクラス。
*
* @version 2.0
* @author T.TSUCHIE
*
*/
public class BeanMappingFactory {
private Configuration configuration = new Configuration();
/**
* デフォルトコンストラクタ
*/
public BeanMappingFactory() {
}
/**
* Beanクラスから、CSVのマッピング情報を作成します。
*
* @param Beanのタイプ
* @param beanType 作成元のBeanクラス。
* @param groups グループ情報。
* アノテーションを指定したグループで切り替える際に指定します。
* 何も指定しない場合は、デフォルトグループの{@link DefaultGroup}のクラスが指定されたとして処理します。
* @return CSVのマッピング情報。
* @throws NullPointerException {@literal beanType == null.}
* @throws SuperCsvInvalidAnnotationException アノテーションの定義が不正な場合。
*/
@SuppressWarnings({"unchecked"})
public BeanMapping create(final Class beanType, final Class>... groups) {
Objects.requireNonNull(beanType);
final BeanMapping beanMapping = new BeanMapping<>(beanType);
// アノテーション @CsvBeanの取得
final CsvBean beanAnno = beanType.getAnnotation(CsvBean.class);
if(beanAnno == null) {
throw new SuperCsvInvalidAnnotationException(beanAnno, MessageBuilder.create("anno.notFound")
.varWithClass("property", beanType)
.varWithAnno("anno", CsvBean.class)
.format());
}
beanMapping.setHeader(beanAnno.header());
beanMapping.setValidateHeader(beanAnno.validateHeader());
// CsvValidatorの取得
final List> validators = Arrays.stream(beanAnno.validators())
.map(v -> (CsvValidator)configuration.getBeanFactory().create(v))
.collect(Collectors.toList());
beanMapping.addAllValidators(validators);
// アノテーション @CsvColumn の取得
final List columnMappingList = new ArrayList<>();
for(Field field : beanType.getDeclaredFields()) {
final CsvColumn columnAnno = field.getAnnotation(CsvColumn.class);
if(columnAnno != null) {
columnMappingList.add(createColumnMapping(field, columnAnno, configuration, groups));
}
}
// カラムの位置順の並び変えと、位置のチェック
columnMappingList.sort(null);
validateColumnAndSupplyPartialColumn(beanType, columnMappingList, Optional.ofNullable(beanType.getAnnotation(CsvPartial.class)));
beanMapping.addAllColumns(columnMappingList);
// コールバック用のメソッドの取得
for(Method method : beanType.getDeclaredMethods()) {
if(method.getAnnotation(CsvPreRead.class) != null) {
beanMapping.addPreReadMethod(new CallbackMethod(method));
}
if(method.getAnnotation(CsvPostRead.class) != null) {
beanMapping.addPostReadMethod(new CallbackMethod(method));
}
if(method.getAnnotation(CsvPreWrite.class) != null) {
beanMapping.addPreWriteMethod(new CallbackMethod(method));
}
if(method.getAnnotation(CsvPostWrite.class) != null) {
beanMapping.addPostWriteMethod(new CallbackMethod(method));
}
}
// リスナークラスの取得
final List listeners = Arrays.stream(beanAnno.listeners())
.map(l -> configuration.getBeanFactory().create(l))
.collect(Collectors.toList());
beanMapping.addAllListeners(listeners);
for(Object listener : listeners) {
for(Method method : listener.getClass().getDeclaredMethods()) {
if(method.getAnnotation(CsvPreRead.class) != null) {
beanMapping.addPreReadMethod(new ListenerCallbackMethod(listener, method));
}
if(method.getAnnotation(CsvPostRead.class) != null) {
beanMapping.addPostReadMethod(new ListenerCallbackMethod(listener, method));
}
if(method.getAnnotation(CsvPreWrite.class) != null) {
beanMapping.addPreWriteMethod(new ListenerCallbackMethod(listener, method));
}
if(method.getAnnotation(CsvPostWrite.class) != null) {
beanMapping.addPostWriteMethod(new ListenerCallbackMethod(listener, method));
}
}
}
beanMapping.getPreReadMethods().sort(null);
beanMapping.getPostReadMethods().sort(null);
beanMapping.getPreWriteMethods().sort(null);
beanMapping.getPostWriteMethods().sort(null);
beanMapping.setSkipValidationOnWrite(configuration.isSkipValidationOnWrite());
beanMapping.setGroups(groups);
return beanMapping;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private ColumnMapping createColumnMapping(final Field field, final CsvColumn columnAnno,
final Configuration config, final Class>[] groups) {
final FieldAccessor fieldAccessor = new FieldAccessor(field, config.getAnnoationComparator());
final ColumnMapping columnMapping = new ColumnMapping();
columnMapping.setField(fieldAccessor);
columnMapping.setNumber(columnAnno.number());
if(columnAnno.label().isEmpty()) {
columnMapping.setLabel(field.getName());
} else {
columnMapping.setLabel(columnAnno.label());
}
// ProcessorBuilderの取得
ProcessorBuilder builder;
if(columnAnno.builder().length == 0) {
builder = config.getBuilderResolver().resolve(fieldAccessor.getType());
if(builder == null) {
// 不明なタイプの場合
builder = new GeneralProcessorBuilder();
}
} else {
// 直接Builderクラスが指定されている場合
try {
builder = (ProcessorBuilder) config.getBeanFactory().create(columnAnno.builder()[0]);
} catch(Throwable e) {
throw new SuperCsvReflectionException(
String.format("Fail create instance of %s with attribute 'builderClass' of @CsvColumn",
columnAnno.builder()[0].getCanonicalName()), e);
}
}
// CellProcessorの作成
columnMapping.setCellProcessorForReading(
(CellProcessor)builder.buildForReading(field.getType(), fieldAccessor, config, groups).orElse(null));
columnMapping.setCellProcessorForWriting(
(CellProcessor)builder.buildForWriting(field.getType(), fieldAccessor, config, groups).orElse(null));
if(builder instanceof AbstractProcessorBuilder) {
columnMapping.setFormatter(((AbstractProcessorBuilder)builder).getFormatter(fieldAccessor, config));
}
return columnMapping;
}
/**
* カラム情報の検証と、部分的に読み込む場合のカラム情報を補足する。
* @param beanType Beanのクラスタイプ
* @param list カラム番号の昇順に並び変えられたカラム情報。
* @param partialAnno アノテーション{@link CsvPartial}の情報
*/
private void validateColumnAndSupplyPartialColumn(final Class> beanType, final List list,
final Optional partialAnno) {
if(list.isEmpty()) {
throw new SuperCsvInvalidAnnotationException(MessageBuilder.create("anno.notFound")
.varWithClass("property", beanType)
.varWithAnno("anno", CsvColumn.class)
.format());
}
// check duplicated column number value
final Set checkedNumber = new TreeSet<>();
final Set duplicateNumbers = new TreeSet<>();
for(ColumnMapping columnMapping : list) {
if(checkedNumber.contains(columnMapping.getNumber())) {
duplicateNumbers.add(columnMapping.getNumber());
}
checkedNumber.add(columnMapping.getNumber());
}
if(!duplicateNumbers.isEmpty()) {
// 重複している 属性 numberが存在する場合
throw new SuperCsvInvalidAnnotationException(MessageBuilder.create("anno.attr.duplicated")
.var("property", beanType.getName())
.varWithAnno("anno", CsvColumn.class)
.var("attrName", "number")
.var("attrValues", duplicateNumbers)
.format());
}
// カラム番号が1以上かのチェック
final int minColumnNumber = list.get(0).getNumber();
if(minColumnNumber <= 0) {
throw new SuperCsvInvalidAnnotationException(MessageBuilder.create("anno.attr.min")
.var("property", beanType.getName())
.varWithAnno("anno", CsvColumn.class)
.var("attrName", "number")
.var("attrValue", minColumnNumber)
.var("min", 1)
.format());
}
// 定義されている列番号の最大値
final int maxColumnNumber = list.get(list.size()-1).getNumber();
// Beanに定義されていない欠けているカラム番号の取得
final Set lackNumbers = new TreeSet();
for(int i=1; i <= maxColumnNumber; i++) {
if(!checkedNumber.contains(i)) {
lackNumbers.add(i);
}
}
// 定義されているカラム番号より、大きなカラム番号を持つカラム情報の補足
if(partialAnno.isPresent()) {
final int partialColumnSize = partialAnno.get().columnSize();
if(maxColumnNumber > partialColumnSize) {
throw new SuperCsvInvalidAnnotationException(partialAnno.get(), MessageBuilder.create("anno.CsvPartial.columSizeMin")
.var("property", beanType.getName())
.var("columnSize", partialColumnSize)
.var("maxColumnNumber", maxColumnNumber)
.format());
}
if(maxColumnNumber < partialColumnSize) {
for(int i= maxColumnNumber+1; i <= partialColumnSize; i++) {
lackNumbers.add(i);
}
}
}
// 不足分のカラムがある場合は、部分的な読み書き用カラムとして追加する
if(lackNumbers.size() > 0) {
for(int number : lackNumbers) {
list.add(createPartialColumnMapping(number, partialAnno));
}
list.sort(null);
}
}
/**
* 部分的なカラムの場合の作成
* @param columnNumber 列番号
* @param partialAnno
* @return
*/
private ColumnMapping createPartialColumnMapping(int columnNumber, final Optional partialAnno) {
final ColumnMapping columnMapping = new ColumnMapping();
columnMapping.setNumber(columnNumber);
columnMapping.setPartialized(true);
String label = String.format("column%d", columnNumber);
if(partialAnno.isPresent()) {
for(CsvPartial.Header header : partialAnno.get().headers()) {
if(header.number() == columnNumber) {
label = header.label();
}
}
}
columnMapping.setLabel(label);
return columnMapping;
}
/**
* システム情報を取得します。
* @return 既存のシステム情報を変更する際に取得します。
*/
public Configuration getConfiguration() {
return configuration;
}
/**
* システム情報を取得します。
* @param configuraton 新しくシステム情報を変更する際に設定します。
*/
public void setConfiguration(Configuration configuraton) {
this.configuration = configuraton;
}
}