![JAR search and dependency download from the Maven repository](/logo.png)
com.github.mygreen.supercsv.io.LazyCsvAnnotationBeanWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of super-csv-annotation Show documentation
Show all versions of super-csv-annotation Show documentation
CSVのJavaライブラリであるSuperCSVに、アノテーション機能を追加したライブラリです。
package com.github.mygreen.supercsv.io;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.supercsv.exception.SuperCsvException;
import org.supercsv.prefs.CsvPreference;
import com.github.mygreen.supercsv.annotation.CsvPartial;
import com.github.mygreen.supercsv.builder.BeanMapping;
import com.github.mygreen.supercsv.builder.ColumnMapping;
import com.github.mygreen.supercsv.builder.HeaderMapper;
import com.github.mygreen.supercsv.builder.BeanMappingFactoryHelper;
import com.github.mygreen.supercsv.builder.LazyBeanMappingFactory;
import com.github.mygreen.supercsv.exception.SuperCsvBindingException;
import com.github.mygreen.supercsv.localization.MessageBuilder;
/**
* カラムの定義が曖昧なアノテーションを元にCSVファイルを書き出すためのクラス。
* カラム番号が指定されていないBean定義を元にマッピングします。
*
* @param マッピング対象のBeanのクラスタイプ
*
* @version 2.2
* @since 2.1
* @author T.TSUCHIE
*
*/
public class LazyCsvAnnotationBeanWriter extends AbstractCsvAnnotationBeanWriter {
/**
* Beanのマッピング情報。
* ・初期化は済んでいない場合があるため、キャッシュとは別に管理する。
*/
private final BeanMapping beanMapping;
/**
* ヘッダー情報を元に初期化済みかどうか
*/
private boolean initialized = false;
/**
* Beanのクラスタイプを指定して、{@link LazyCsvAnnotationBeanWriter}を作成するコンストラクタ。
* {@link BufferedWriter}にラップして実行されるため、ラップする必要はありません。
*
* @param beanType Beanのクラスタイプ。
* @param writer the writer
* @param preference CSV preferences.
* @param groups グループ情報。適用するアノテーションを切り替える際に指定します。
* @throws NullPointerException {@literal if beanType or writer or preferences are null.}
*/
public LazyCsvAnnotationBeanWriter(final Class beanType, final Writer writer, final CsvPreference preference,
final Class>... groups) {
super(writer, preference);
Objects.requireNonNull(beanType, "beanType should not be null.");
LazyBeanMappingFactory factory = new LazyBeanMappingFactory();
this.beanMapping = factory.create(beanType, groups);
this.validators.addAll(beanMapping.getValidators());
}
/**
* Beanのマッピング情報を指定して、{@link LazyCsvAnnotationBeanWriter}を作成するコンストラクタ。
* {@link BufferedWriter}にラップして実行されるため、ラップする必要はありません。
* Beanのマッピング情報を独自にカスタマイズして、{@link LazyBeanMappingFactory}から作成する場合に利用します。
*
* @param beanMapping Beanのマッピング情報。
* @param writer the writer
* @param preference the CSV preferences.
* @throws NullPointerException {@literal if beanMapping or writer or preferences are null.}
*/
public LazyCsvAnnotationBeanWriter(final BeanMapping beanMapping, final Writer writer, final CsvPreference preference,
final Class>... groups) {
super(writer, preference);
Objects.requireNonNull(beanMapping, "beanMapping should not be null.");
this.beanMapping = beanMapping;
this.validators.addAll(beanMapping.getValidators());
}
/**
* 初期化が完了していないときに呼ばれたときにスローする例外のインスタンスを作成します。
*/
private IllegalStateException newNotInitialzedException() {
return new IllegalStateException(MessageBuilder.create("noinit.onLazyWrite").format());
}
/**
* ヘッダー情報を指定しないで初期化を行います。
* カラム番号が指定されていないカラムは、フィールドの名称順に空いている番号が割り振られます。
*/
public void init() {
init(new String[]{});
}
/**
* ヘッダー情報を指定して初期化を行います。
* カラム番号が指定されていないカラムは、フィールドの名称順に空いている番号が割り振られます。
* @param headers ヘッダー情報
*/
public void init(final String... headers) {
setupMappingColumns(headers);
this.beanMappingCache = BeanMappingCache.create(beanMapping);
// 初期化完了
this.initialized = true;
}
/**
* 指定したヘッダーを元に、マッピング情報を補完する。
* カラムの位置である番号を確定する。
* 存在しないカラムがある場合は、部分的な読み込みとして、ダミーのカラム情報を作成する。
* @param headers
* @param SuperCsvInvalidAnnotationException
*/
private void setupMappingColumns(final String[] headers) {
final List columnMappingList = beanMapping.getColumns();
final HeaderMapper headerMapper = beanMapping.getHeaderMapper();
// 一致するラベルがあれば、カラムの番号を補完する
final int headerSize = headers.length;
for(int i=0; i < headerSize;i ++) {
final String header = headers[i];
/*
* 番号が決まっておらず、ラベルが一致するカラム情報を抽出する。
* ※既に番号が決まっているが、ラベルが一致しないのものは、後からチェックする。
*/
List undeterminedColumnList = columnMappingList.stream()
.filter(col -> !col.isDeterminedNumber())
.filter(col -> headerMapper.toMap(col, beanMapping.getConfiguration(), beanMapping.getGroups()).equals(header))
.collect(Collectors.toList());
final int columnNumber = i+1;
undeterminedColumnList.forEach(col -> col.setNumber(columnNumber));
}
// カラムの番号順に並び変える
columnMappingList.sort(null);
/*
* ヘッダーでは指定されていない番号が未決定のカラムについて、空いている番号に振る
* ・@CsvPartialで指定した場合とかぶらないようにすべき?
*/
final Optional partialAnno = Optional.ofNullable(beanMapping.getType().getAnnotation(CsvPartial.class));
final List undeterminedColumnList = columnMappingList.stream()
.filter(col -> !col.isDeterminedNumber())
.collect(Collectors.toList());
if(!undeterminedColumnList.isEmpty()) {
final Set determinedNumbers = columnMappingList.stream()
.filter(col -> col.isDeterminedNumber())
.map(col -> col.getNumber())
.collect(Collectors.toSet());
// @CsvParitalで定義されているカラムは、決定されている番号として処理する。
partialAnno.ifPresent(anno -> Arrays.stream(anno.headers()).forEach(header -> determinedNumbers.add(header.number())));
int counter = 1;
for(ColumnMapping col : undeterminedColumnList) {
while(true) {
if(!determinedNumbers.contains(counter)) {
break;
}
counter++;
}
col.setNumber(counter);
determinedNumbers.add(counter);
}
// 第度カラムの番号順に並び変える
columnMappingList.sort(null);
}
// 決定していないカラム番号のチェック
BeanMappingFactoryHelper.validateNonDeterminedColumnNumber(beanMapping.getType(), columnMappingList, headers);
// 重複しているカラム番号のチェック
BeanMappingFactoryHelper.validateDuplicatedColumnNumber(beanMapping.getType(), columnMappingList);
// 不足しているカラム番号の補完
BeanMappingFactoryHelper.supplyLackedNumberMappingColumn(beanMapping.getType(), columnMappingList, partialAnno, headers);
beanMapping.setColumns(columnMappingList);
}
/**
* ヘッダー情報を書き出します。
* ただし、列番号を省略され、定義がされていないカラムは、{@literal column[カラム番号]}の形式となります。
* @throws IOException ファイルの出力に失敗した場合。
* @throws IllegalStateException {@link #init()} メソッドによる初期化が完了していない場合
*/
public void writeHeader() throws IOException {
if(!initialized) {
throw newNotInitialzedException();
}
super.writeHeader(getDefinedHeader());
}
/**
* レコードのデータを全て書き込みます。
* ヘッダー行も自動的に処理されます。2回目以降に呼び出した場合、ヘッダー情報は書き込まれません。
* レコード処理中に例外が発生した場合、その時点で処理を終了します。
*
* @param sources 書き込むレコードのデータ。
* @throws NullPointerException sources is null.
* @throws IOException レコードの出力に失敗した場合。
* @throws SuperCsvBindingException セルの値に問題がある場合
* @throws SuperCsvException 設定など、その他に問題がある場合
*
*/
public void writeAll(final Collection sources) throws IOException {
writeAll(sources, false);
}
/**
* レコードのデータを全て書き込みます。
* ヘッダー行も自動的に処理されます。2回目以降に呼び出した場合、ヘッダー情報は書き込まれません。
*
* @param sources 書き込むレコードのデータ。
* @param continueOnError continueOnError レコードの処理中に、
* 例外{@link SuperCsvBindingException}が発生しても、続行するかどうか指定します。
* trueの場合、例外が発生しても、次の処理を行います。
* @throws NullPointerException sources is null.
* @throws IOException レコードの出力に失敗した場合。
* @throws SuperCsvBindingException セルの値に問題がある場合
* @throws SuperCsvException 設定など、その他に問題がある場合
*
*/
public void writeAll(final Collection sources, final boolean continueOnError) throws IOException {
Objects.requireNonNull(sources, "sources should not be null.");
if(!initialized) {
init();
}
if(beanMappingCache.getOriginal().isHeader() && getLineNumber() == 0) {
writeHeader();
}
for(T record : sources) {
try {
write(record);
} catch(SuperCsvBindingException e) {
if(!continueOnError) {
throw e;
}
}
}
super.flush();
}
/**
* {@inheritDoc}
* @throws IllegalStateException {@link #init()} メソッドによる初期化が完了していない場合
*/
@Override
public void write(final T source) throws IOException {
if(!initialized) {
throw newNotInitialzedException();
}
super.write(source);
}
/**
* {@inheritDoc}
* @throws IllegalStateException {@link #init()} メソッドによる初期化が完了していない場合
*/
@Override
public String[] getDefinedHeader() {
if(!initialized) {
throw newNotInitialzedException();
}
return super.getDefinedHeader();
}
/**
* {@inheritDoc}
* @throws IllegalStateException {@link #init()} メソッドによる初期化が完了していない場合
*/
@Override
public BeanMapping getBeanMapping() {
if(!initialized) {
throw newNotInitialzedException();
}
return super.getBeanMapping();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy