XlsSaver.java
- package com.gh.mygreen.xlsmapper;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.lang.annotation.Annotation;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import org.apache.poi.ss.usermodel.Sheet;
- import org.apache.poi.ss.usermodel.Workbook;
- import org.apache.poi.ss.usermodel.WorkbookFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.gh.mygreen.xlsmapper.annotation.XlsFieldProcessor;
- import com.gh.mygreen.xlsmapper.annotation.XlsListener;
- import com.gh.mygreen.xlsmapper.annotation.XlsPostSave;
- import com.gh.mygreen.xlsmapper.annotation.XlsPreSave;
- import com.gh.mygreen.xlsmapper.annotation.XlsSheet;
- import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessor;
- import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessorFactory;
- import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessorProxy;
- import com.gh.mygreen.xlsmapper.fieldaccessor.FieldAccessorProxyComparator;
- import com.gh.mygreen.xlsmapper.fieldprocessor.FieldProcessor;
- import com.gh.mygreen.xlsmapper.fieldprocessor.ProcessCase;
- import com.gh.mygreen.xlsmapper.localization.MessageBuilder;
- import com.gh.mygreen.xlsmapper.util.ArgUtils;
- import com.gh.mygreen.xlsmapper.util.ClassUtils;
- import com.gh.mygreen.xlsmapper.util.Utils;
- import com.gh.mygreen.xlsmapper.validation.MultipleSheetBindingErrors;
- import com.gh.mygreen.xlsmapper.validation.SheetBindingErrors;
- import com.gh.mygreen.xlsmapper.xml.AnnotationReader;
- /**
- * JavaBeanをExcelのシートにマッピングし出力するクラス。
- *
- * @version 2.0
- * @author T.TSUCHIE
- *
- */
- public class XlsSaver {
- private static final Logger logger = LoggerFactory.getLogger(XlsSaver.class);
- private Configuration configuration;
- /**
- * 独自のシステム情報を設定するコンストラクタ
- * @param configuration システム情報
- */
- public XlsSaver(Configuration configuration) {
- this.configuration = configuration;
- }
- /**
- * デフォルトのコンストラクタ
- */
- public XlsSaver() {
- this(new Configuration());
- }
- /**
- * JavaのオブジェクトをExeclファイルに出力する。
- * <p>出力するファイルは、引数で指定した雛形となるテンプレート用のExcelファイルをもとに出力する。</p>
- *
- * @param templateXlsIn 雛形となるExcelファイルの入力
- * @param xlsOut 出力先のストリーム
- * @param beanObj 書き込むBeanオブジェクト
- * @throws IllegalArgumentException {@literal templateXlsIn == null or xlsOut == null or beanObj == null}
- * @throws XlsMapperException マッピングに失敗した場合
- * @throws IOException テンプレートのファイルの読み込みやファイルの出力に失敗した場合
- */
- public void save(final InputStream templateXlsIn, final OutputStream xlsOut, final Object beanObj)
- throws XlsMapperException, IOException {
- saveDetail(templateXlsIn, xlsOut, beanObj);
- }
- /**
- * JavaのオブジェクトをExeclファイルに出力する。
- * <p>出力するファイルは、引数で指定した雛形となるテンプレート用のExcelファイルをもとに出力する。</p>
- *
- * @param <P> マッピング対象のクラスタイプ
- * @param templateXlsIn 雛形となるExcelファイルの入力
- * @param xlsOut 出力先のストリーム
- * @param beanObj 書き込むBeanオブジェクト
- * @return マッピング結果。
- * {@link Configuration#isIgnoreSheetNotFound()}の値がtrueで、シートが見つからない場合、nullを返します。
- * @throws IllegalArgumentException {@literal templateXlsIn == null or xlsOut == null or beanObj == null}
- * @throws XlsMapperException マッピングに失敗した場合
- * @throws IOException テンプレートのファイルの読み込みやファイルの出力に失敗した場合
- */
- public <P> SheetBindingErrors<P> saveDetail(final InputStream templateXlsIn, final OutputStream xlsOut, final P beanObj)
- throws XlsMapperException, IOException {
- ArgUtils.notNull(templateXlsIn, "templateXlsIn");
- ArgUtils.notNull(xlsOut, "xlsOut");
- ArgUtils.notNull(beanObj, "beanObj");
- final AnnotationReader annoReader = new AnnotationReader(configuration.getAnnotationMapping().orElse(null));
- try(Workbook book = WorkbookFactory.create(templateXlsIn)) {
- final Class<?> clazz = beanObj.getClass();
- final XlsSheet sheetAnno = clazz.getAnnotation(XlsSheet.class);
- if(sheetAnno == null) {
- throw new AnnotationInvalidException(sheetAnno, MessageBuilder.create("anno.notFound")
- .varWithClass("property", clazz)
- .varWithAnno("anno", XlsSheet.class)
- .format());
-
- }
-
- final SheetBindingErrors<P> bindingResult;
- try {
- final Sheet[] xlsSheet = configuration.getSheetFinder().findForSaving(book, sheetAnno, annoReader, beanObj);
- bindingResult = saveSheet(xlsSheet[0], beanObj, annoReader);
-
- } catch(SheetNotFoundException e) {
- if(configuration.isIgnoreSheetNotFound()){
- logger.warn(MessageBuilder.create("log.skipNotFoundSheet").format(), e);
- return null;
-
- } else {
- throw e;
- }
- }
-
- if(configuration.isFormulaRecalcurationOnSave()) {
- book.setForceFormulaRecalculation(true);
- }
-
- book.write(xlsOut);
-
- return bindingResult;
-
- }
- }
- /**
- * 複数のオブジェクトをそれぞれのシートへ保存する。
- * @param templateXlsIn 雛形となるExcelファイルの入力
- * @param xlsOut xlsOut 出力先のストリーム
- * @param beanObjs 書き込むオブジェクトの配列。
- * @throws IllegalArgumentException {@literal templateXlsIn == null or xlsOut == null or beanObjs == null}
- * @throws XlsMapperException マッピングに失敗した場合
- * @throws IOException テンプレートのファイルの読み込みやファイルの出力に失敗した場合
- */
- public void saveMultiple(final InputStream templateXlsIn, final OutputStream xlsOut, final Object[] beanObjs)
- throws XlsMapperException, IOException {
- saveMultipleDetail(templateXlsIn, xlsOut, beanObjs);
- }
- /**
- * 複数のオブジェクトをそれぞれのシートへ保存する。
- * @param templateXlsIn 雛形となるExcelファイルの入力
- * @param xlsOut xlsOut 出力先のストリーム
- * @param beanObjs 書き込むオブジェクトの配列。
- * @return マッピング結果。
- * {@link Configuration#isIgnoreSheetNotFound()}の値がtrueで、シートが見つからない場合、結果に含まれません。
- * @throws IllegalArgumentException {@literal templateXlsIn == null or xlsOut == null or beanObjs == null}
- * @throws XlsMapperException マッピングに失敗した場合
- * @throws IOException テンプレートのファイルの読み込みやファイルの出力に失敗した場合
- */
- public MultipleSheetBindingErrors<Object> saveMultipleDetail(final InputStream templateXlsIn, final OutputStream xlsOut, final Object[] beanObjs)
- throws XlsMapperException, IOException {
- ArgUtils.notNull(templateXlsIn, "templateXlsIn");
- ArgUtils.notNull(xlsOut, "xlsOut");
- ArgUtils.notEmpty(beanObjs, "beanObjs");
- final AnnotationReader annoReader = new AnnotationReader(configuration.getAnnotationMapping().orElse(null));
- final MultipleSheetBindingErrors<Object> multipleResult = new MultipleSheetBindingErrors<>();
- try(Workbook book = WorkbookFactory.create(templateXlsIn)) {
- for(int i=0; i < beanObjs.length; i++) {
- final Object beanObj = beanObjs[i];
- final Class<?> clazz = beanObj.getClass();
-
- final XlsSheet sheetAnno = annoReader.getAnnotation(clazz, XlsSheet.class);
- if(sheetAnno == null) {
- throw new AnnotationInvalidException(sheetAnno, MessageBuilder.create("anno.notFound")
- .varWithClass("property", clazz)
- .varWithAnno("anno", XlsSheet.class)
- .format());
- }
-
- try {
- final Sheet[] xlsSheet = configuration.getSheetFinder().findForSaving(book, sheetAnno, annoReader, beanObj);
- multipleResult.addBindingErrors(saveSheet(xlsSheet[0], beanObj, annoReader));
-
- } catch(SheetNotFoundException e) {
- if(configuration.isIgnoreSheetNotFound()){
- logger.warn(MessageBuilder.create("log.skipNotFoundSheet").format(), e);
- continue;
- } else {
- throw e;
- }
- }
- }
-
- if(configuration.isFormulaRecalcurationOnSave()) {
- book.setForceFormulaRecalculation(true);
- }
-
- book.write(xlsOut);
-
- return multipleResult;
-
- }
- }
- /**
- * 任意のクラスのオブジェクトを、Excelシートにマッピングする。
- * @param sheet
- * @param beanObj
- * @param configuration
- * @param work
- * @throws XlsMapperException
- */
- private <P> SheetBindingErrors<P> saveSheet(final Sheet sheet, final P beanObj, final AnnotationReader annoReader)
- throws XlsMapperException {
- final Class<?> clazz = beanObj.getClass();
- final SheetBindingErrors<P> errors = configuration.getBindingErrorsFactory().create(beanObj);
- errors.setSheetName(sheet.getSheetName());
- errors.setSheetIndex(sheet.getWorkbook().getSheetIndex(sheet));
- final SavingWorkObject work = new SavingWorkObject();
- work.setAnnoReader(annoReader);
- work.setErrors(errors);
- // セルのキャッシュ情報の初期化
- configuration.getCellFormatter().init(false);
- final FieldAccessorFactory adpterFactory = new FieldAccessorFactory(annoReader);
- // リスナークラスの@PreSave用メソッドの実行
- final XlsListener listenerAnno = annoReader.getAnnotation(beanObj.getClass(), XlsListener.class);
- if(listenerAnno != null) {
- for(Class<?> listenerClass : listenerAnno.value()) {
- final Object listenerObj = configuration.createBean(listenerClass);
- for(Method method : listenerObj.getClass().getMethods()) {
- if(annoReader.hasAnnotation(method, XlsPreSave.class)) {
- Utils.invokeNeedProcessMethod(listenerObj, method, beanObj, sheet, configuration, work.getErrors(), ProcessCase.Save);
- }
- }
- }
- }
- // @PreSave用のメソッドの取得と実行
- for(Method method : clazz.getMethods()) {
- final XlsPreSave preProcessAnno = annoReader.getAnnotation(method, XlsPreSave.class);
- if(preProcessAnno != null) {
- Utils.invokeNeedProcessMethod(beanObj, method, beanObj, sheet, configuration, work.getErrors(), ProcessCase.Save);
- }
- }
- final List<FieldAccessorProxy> accessorProxies = new ArrayList<>();
- // public メソッドの処理
- for(Method method : clazz.getMethods()) {
- method.setAccessible(true);
- for(Annotation anno : annoReader.getAnnotations(method)) {
- final XlsFieldProcessor annoFieldProcessor = anno.annotationType().getAnnotation(XlsFieldProcessor.class);
- if(ClassUtils.isAccessorMethod(method) && annoFieldProcessor != null) {
- // 登録済みのFieldProcessorの取得
- FieldProcessor<?> processor = configuration.getFieldProcessorRegistry().getProcessor(anno.annotationType());
- // アノテーションに指定されているFieldProcessorの場合
- if(processor == null && annoFieldProcessor.value().length > 0) {
- processor = configuration.createBean(annoFieldProcessor.value()[0]);
- }
- if(processor != null) {
- final FieldAccessor accessor = adpterFactory.create(method);
- final FieldAccessorProxy accessorProxy = new FieldAccessorProxy(anno, processor, accessor);
- if(!accessorProxies.contains(accessorProxy)) {
- accessorProxies.add(accessorProxy);
- }
- } else {
- // FieldProcessorが見つからない場合
- throw new AnnotationInvalidException(anno, MessageBuilder.create("anno.XlsFieldProcessor.notResolve")
- .varWithAnno("anno", anno.annotationType())
- .format());
- }
- }
- if(anno instanceof XlsPostSave) {
- work.addNeedPostProcess(new NeedProcess(beanObj, beanObj, method));
- }
- }
- }
- // フィールドの処理
- for(Field field : clazz.getDeclaredFields()) {
- field.setAccessible(true);
- final FieldAccessor accessor = adpterFactory.create(field);
- for(Annotation anno : annoReader.getAnnotations(field)) {
- final XlsFieldProcessor annoFieldProcessor = anno.annotationType().getAnnotation(XlsFieldProcessor.class);
- if(annoFieldProcessor != null) {
- // 登録済みのFieldProcessorの取得
- FieldProcessor<?> processor = configuration.getFieldProcessorRegistry().getProcessor(anno.annotationType());
- // アノテーションに指定されているFieldProcessorの場合
- if(processor == null && annoFieldProcessor.value().length > 0) {
- processor = configuration.createBean(annoFieldProcessor.value()[0]);
- }
- if(processor != null) {
- final FieldAccessorProxy accessorProxy = new FieldAccessorProxy(anno, processor, accessor);
- if(!accessorProxies.contains(accessorProxy)) {
- accessorProxies.add(accessorProxy);
- }
- } else {
- // FieldProcessorが見つからない場合
- throw new AnnotationInvalidException(anno, MessageBuilder.create("anno.XlsFieldProcessor.notResolve")
- .varWithAnno("anno", anno.annotationType())
- .format());
- }
- }
- }
- }
- // 順番を並び替えて保存処理を実行する
- Collections.sort(accessorProxies, new FieldAccessorProxyComparator());
- for(FieldAccessorProxy accessorProxy : accessorProxies) {
- accessorProxy.saveProcess(sheet, beanObj, configuration, work);
- }
- // リスナークラスの@PostSaveの取得
- if(listenerAnno != null) {
- for(Class<?> listenerClass : listenerAnno.value()) {
- final Object listenerObj = configuration.createBean(listenerClass);
- for(Method method : listenerObj.getClass().getMethods()) {
- if(annoReader.hasAnnotation(method, XlsPostSave.class)) {
- work.addNeedPostProcess(new NeedProcess(beanObj, listenerObj, method));
- }
- }
- }
- }
- //@PostSaveが付与されているメソッドの実行
- for(NeedProcess need : work.getNeedPostProcesses()) {
- Utils.invokeNeedProcessMethod(need.getProcess(), need.getMethod(), need.getTarget(), sheet, configuration, work.getErrors(), ProcessCase.Save);
- }
- return errors;
- }
- /**
- * システム情報を取得します。
- * @return 現在のシステム情報
- */
- public Configuration getConfiguration() {
- return configuration;
- }
- /**
- * システム情報を設定します。
- * @param configuration システム情報
- */
- public void setConfiguration(Configuration configuration) {
- this.configuration = configuration;
- }
- }