package com.gh.mygreen.xlsmapper.cellconvert.converter; import java.lang.annotation.Annotation; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import com.gh.mygreen.xlsmapper.AnnotationInvalidException; import com.gh.mygreen.xlsmapper.POIUtils; import com.gh.mygreen.xlsmapper.Utils; import com.gh.mygreen.xlsmapper.XlsMapperConfig; import com.gh.mygreen.xlsmapper.XlsMapperException; import com.gh.mygreen.xlsmapper.annotation.XlsConverter; import com.gh.mygreen.xlsmapper.annotation.XlsDateConverter; import com.gh.mygreen.xlsmapper.annotation.XlsFormula; import com.gh.mygreen.xlsmapper.cellconvert.AbstractCellConverter; import com.gh.mygreen.xlsmapper.fieldprocessor.FieldAdaptor; /** * 日時型のConverterの抽象クラス。 * <p>{@link Date}を継承している<code>javax.sql.Time/Date/Timestamp</code>はこのクラスを継承して作成します。 * * @version 1.5 * @author T.TSUCHIE * */ public abstract class AbstractDateCellConverter<T extends Date> extends AbstractCellConverter<T> { @Override public T toObject(final Cell cell, final FieldAdaptor adaptor, final XlsMapperConfig config) throws XlsMapperException { final XlsConverter converterAnno = adaptor.getLoadingAnnotation(XlsConverter.class); final XlsDateConverter anno = getLoadingAnnotation(adaptor); T resultValue = null; if(POIUtils.isEmptyCellContents(cell, config.getCellFormatter())) { if(Utils.hasNotDefaultValue(converterAnno)) { return null; } else if(Utils.isNotEmpty(anno.javaPattern())) { final String defaultValue = converterAnno.defaultValue(); try { resultValue = parseDate(defaultValue, createDateFormat(anno)); } catch(ParseException e) { throw newTypeBindException(e, cell, adaptor, defaultValue) .addAllMessageVars(createTypeErrorMessageVars(anno)); } } } else if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC) { // セルのタイプが数値型の場合は、強制的に取得する resultValue = convertDate(cell.getDateCellValue()); } else if(cell.getCellType() == Cell.CELL_TYPE_FORMULA) { // 式を評価して再帰的に処理する。 final Workbook workbook = cell.getSheet().getWorkbook(); final CreationHelper helper = workbook.getCreationHelper(); final FormulaEvaluator evaluator = helper.createFormulaEvaluator(); try { // 再帰的に処理する final Cell evalCell = evaluator.evaluateInCell(cell); return toObject(evalCell, adaptor, config); } catch(Exception e) { throw newTypeBindException(e, cell, adaptor, cell) .addAllMessageVars(createTypeErrorMessageVars(anno)); } } else { String cellValue = POIUtils.getCellContents(cell, config.getCellFormatter()); cellValue = Utils.trim(cellValue, converterAnno); if(Utils.isNotEmpty(cellValue)) { try { resultValue = parseDate(cellValue, createDateFormat(anno)); } catch(ParseException e) { throw newTypeBindException(e, cell, adaptor, cellValue) .addAllMessageVars(createTypeErrorMessageVars(anno)); } } } if(resultValue != null) { return resultValue; } return null; } /** * アノテーションを元に日付のフォーマッターのインスタンスを作成します。 * @param anno * @return * @throws AnnotationInvalidException フォーマットが指定されていない場合 */ protected DateFormat createDateFormat(final XlsDateConverter anno) throws AnnotationInvalidException { final Locale locale; if(anno.locale().isEmpty()) { locale = Locale.getDefault(); } else { locale = Utils.getLocale(anno.locale()); } final String pattern = anno.javaPattern().isEmpty() ? getDefaultJavaPattern() : anno.javaPattern(); final DateFormat format = new SimpleDateFormat(pattern, locale); format.setLenient(anno.lenient()); return format; } /** * 型変換エラー時のメッセージ変数の作成 */ Map<String, Object> createTypeErrorMessageVars(final XlsDateConverter anno) { final Map<String, Object> vars = new LinkedHashMap<>(); vars.put("javaPattern", anno.javaPattern()); vars.put("lenient", anno.lenient()); vars.put("locale", anno.locale()); return vars; } /** * その型における日付型を返す。 * @param value * @return */ abstract protected T convertDate(final Date value); /** * その型における標準のJavaの書式を返す。 * @since 0.5 * @return {@link SimpleDateFormat}で処理可能な形式。 */ abstract protected String getDefaultJavaPattern(); /** * その型における標準のExcelの書式を返す。 * @since 1.1 * @return Excelの書式 */ abstract protected String getDefaultExcelPattern(); /** * 文字列をその型における日付型を返す。 * <p>アノテーション{@link XlsDateConverter}でフォーマットが与えられている場合は、パースして返す。 * @param value * @param format フォーマットが指定されていない場合はnullが渡される * @return * @throws ParseException */ protected T parseDate(String value, DateFormat format) throws ParseException { return convertDate(format.parse(value)); } XlsDateConverter getDefaultDateConverterAnnotation() { return new XlsDateConverter() { @Override public Class<? extends Annotation> annotationType() { return XlsDateConverter.class; } @Override public String javaPattern() { // 各タイプごとの標準の書式を取得する。 return getDefaultJavaPattern(); } @Override public String locale() { return ""; } @Override public boolean lenient() { return false; } @Override public String excelPattern() { return ""; } }; } XlsDateConverter getLoadingAnnotation(final FieldAdaptor adaptor) { XlsDateConverter anno = adaptor.getLoadingAnnotation(XlsDateConverter.class); if(anno == null) { anno = getDefaultDateConverterAnnotation(); } return anno; } XlsDateConverter getSavingAnnotation(final FieldAdaptor adaptor) { XlsDateConverter anno = adaptor.getSavingAnnotation(XlsDateConverter.class); if(anno == null) { anno = getDefaultDateConverterAnnotation(); } return anno; } @Override public Cell toCell(final FieldAdaptor adaptor, final Date targetValue, final Object targetBean, final Sheet sheet, final int column, final int row, final XlsMapperConfig config) throws XlsMapperException { final XlsConverter converterAnno = adaptor.getSavingAnnotation(XlsConverter.class); final XlsDateConverter anno = getSavingAnnotation(adaptor); final XlsFormula formulaAnno = adaptor.getSavingAnnotation(XlsFormula.class); final boolean primaryFormula = formulaAnno == null ? false : formulaAnno.primary(); final Cell cell = POIUtils.getCell(sheet, column, row); // セルの書式設定 if(converterAnno != null) { POIUtils.wrapCellText(cell, converterAnno.wrapText()); POIUtils.shrinkToFit(cell, converterAnno.shrinkToFit()); } Date value = targetValue; // デフォルト値から値を設定する if(value == null && Utils.hasDefaultValue(converterAnno)) { final String defaultValue = converterAnno.defaultValue(); final DateFormat formatter; if(Utils.isNotEmpty(anno.javaPattern())) { formatter = createDateFormat(anno); } else { formatter = createDateFormat(getDefaultDateConverterAnnotation()); } try { value = parseDate(defaultValue, formatter); } catch (ParseException e) { throw newTypeBindException(e, cell, adaptor, defaultValue) .addAllMessageVars(createTypeErrorMessageVars(anno)); } } // セルの書式の設定 if(Utils.isNotEmpty(anno.excelPattern()) && !POIUtils.getCellFormatPattern(cell).equalsIgnoreCase(anno.excelPattern())) { // 既にCell中に書式が設定され、それが異なる場合 CellStyle style = sheet.getWorkbook().createCellStyle(); style.cloneStyleFrom(cell.getCellStyle()); style.setDataFormat(POIUtils.getDataFormatIndex(sheet, anno.excelPattern())); cell.setCellStyle(style); } else if(Utils.isEmpty(anno.excelPattern()) && POIUtils.getCellFormatPattern(cell).isEmpty()) { // アノテーションの書式が指定されておらず、セルの書式が空の場合 // 標準の書式を設定する。 CellStyle style = sheet.getWorkbook().createCellStyle(); style.cloneStyleFrom(cell.getCellStyle()); style.setDataFormat(POIUtils.getDataFormatIndex(sheet, getDefaultExcelPattern())); cell.setCellStyle(style); } if(value != null && !primaryFormula) { cell.setCellValue(value); } else if(formulaAnno != null) { Utils.setupCellFormula(adaptor, formulaAnno, config, cell, targetBean); } else { cell.setCellType(Cell.CELL_TYPE_BLANK); } return cell; } }