DefaultCellCommentHandler.java
- package com.gh.mygreen.xlsmapper;
- import java.nio.charset.Charset;
- import java.util.Arrays;
- import java.util.Optional;
- import org.apache.poi.hssf.usermodel.HSSFRichTextString;
- import org.apache.poi.ss.usermodel.Cell;
- import org.apache.poi.ss.usermodel.ClientAnchor;
- import org.apache.poi.ss.usermodel.Comment;
- import org.apache.poi.ss.usermodel.CreationHelper;
- import org.apache.poi.ss.usermodel.Drawing;
- import org.apache.poi.ss.usermodel.RichTextString;
- import org.apache.poi.ss.usermodel.Row;
- import org.apache.poi.ss.usermodel.Sheet;
- import org.apache.poi.ss.util.CellRangeAddress;
- import org.apache.poi.xssf.usermodel.XSSFRichTextString;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.gh.mygreen.xlsmapper.annotation.XlsCommentOption;
- import com.gh.mygreen.xlsmapper.util.ArgUtils;
- import com.gh.mygreen.xlsmapper.util.CellPosition;
- /**
- * {@link CellCommentHandler}の標準の実装。
- *
- * @since 2.1
- * @author T.TSUCHIE
- *
- */
- public class DefaultCellCommentHandler implements CellCommentHandler {
- private static final Logger logger = LoggerFactory.getLogger(DefaultCellCommentHandler.class);
-
- /**
- * コメントの縦方向の開始位置。
- * 行数分で表現する。
- */
- private int vertialPrefix = 1;
-
- /**
- * コメントの横方向の開始位置。
- * 列数分で表現する。
- */
- private int horizontalPrefix = 1;
-
- /**
- * コメントの縦方向の最大サイズ。
- * 行数分で表現する。
- */
- private int maxVerticalSize = 4;
-
- /**
- * コメントの列方向の最大サイズ。
- * 列数分で表現する。
- */
- private int maxHorizontalSize = 3;
-
- @Override
- public Optional<String> handleLoad(final Cell cell, Optional<XlsCommentOption> commentOption) {
-
- Comment comment = getMergedCellComment(cell);
- if(comment == null) {
- return Optional.empty();
- }
-
- String commentText = comment.getString().getString();
- return Optional.of(commentText);
- }
-
- /**
- * 結合を考慮したセルのコメントを取得する。
- * @param cell 元となるセル。
- * @return コメント。コメントが設定されていなければ、nullを返す。
- */
- private Comment getMergedCellComment(final Cell cell) {
- Comment comment = cell.getCellComment();
- if(comment != null) {
- return comment;
- }
-
- final Sheet sheet = cell.getSheet();
- final int size = sheet.getNumMergedRegions();
-
- for(int i=0; i < size; i++) {
- final CellRangeAddress range = sheet.getMergedRegion(i);
- if(!range.isInRange(cell)) {
- continue;
- }
-
- // nullでないセルを取得する。
- for(int rowIdx=range.getFirstRow(); rowIdx <= range.getLastRow(); rowIdx++) {
- final Row row = sheet.getRow(rowIdx);
- if(row == null) {
- continue;
- }
- for(int colIdx=range.getFirstColumn(); colIdx <= range.getLastColumn(); colIdx++) {
- final Cell valueCell = row.getCell(colIdx);
- if(valueCell == null) {
- continue;
- }
- comment = valueCell.getCellComment();
- if(comment != null) {
- return comment;
- }
- }
- }
- }
-
- return null;
-
- }
- @Override
- public void handleSave(final Cell cell, final Optional<String> text, final Optional<XlsCommentOption> commentOption) {
-
- if(!text.isPresent()) {
- // コメントが空のとき
- commentOption.ifPresent(option -> {
- if(option.removeIfEmpty()) {
- // コメントが空のとき既存のコメントを削除する
- cell.removeCellComment();
- }
- });
- return;
- }
-
- final Sheet sheet = cell.getSheet();
- final CreationHelper helper = sheet.getWorkbook().getCreationHelper();
- final Drawing<?> drawing = sheet.createDrawingPatriarch();
-
- final Comment comment;
- RichTextString richText = helper.createRichTextString(text.get());
- if(cell.getCellComment() == null) {
- ClientAnchor anchor = createAnchor(drawing, text.get(), cell, commentOption);
- comment = drawing.createCellComment(anchor);
- applyCommentFormat(richText, cell);
- } else {
- // 既存のコメントが存在する場合は、書式やサイズをコピーして使用する。
- comment = cell.getCellComment();
- RichTextString orgText = comment.getString();
- if(orgText.numFormattingRuns() > 0) {
- copyCommentFormat(richText, orgText);
- } else {
- applyCommentFormat(richText, cell);
- }
- }
-
- comment.setString(richText);
-
- // コメントの表示状態の更新
- commentOption.ifPresent(option -> comment.setVisible(option.visible()));
-
- cell.setCellComment(comment);
-
- }
-
- /**
- * コメントの位置、サイズを作成する。
- * @param drawing
- * @param text 書き込むコメント
- * @param cell 書込み対象のセル
- * @param commentOption コメントのオプション
- * @return コメントの表示位置
- */
- protected ClientAnchor createAnchor(final Drawing<?> drawing, final String text, final Cell cell,
- final Optional<XlsCommentOption> commentOption) {
- final CellPosition address = CellPosition.of(cell);
-
- // コメントを開業で分割し、最長の行を取得する。
- String[] split = text.split("\r\n|\r|\n");
- int maxLength = Arrays.stream(split)
- .mapToInt(str -> str.getBytes(Charset.forName("Windows-31j")).length)
- .max().orElse(0);
-
- /*
- * コメントの横サイズ。文字数(バイト数)をもとに決定。
- * ・1セルの文字数を元に出す。
- * ・columnWidthは、1文字の幅を1/256にしたものが単位となる。
- * ・最大3列分とする。
- */
- int charPerColumn = cell.getSheet().getColumnWidth(cell.getColumnIndex())/256;
- int commentColumnSize = (int)Math.ceil(maxLength*1.0 / charPerColumn);
-
- int columnSize = commentColumnSize;
- int lineWrappingCount = 0;
- if(commentColumnSize > maxHorizontalSize) {
- columnSize = maxHorizontalSize;
- // 行の折り返し回数を計算する
- lineWrappingCount = commentColumnSize / maxHorizontalSize;
- }
-
- if(commentOption.isPresent() && commentOption.get().horizontalSize() > 0) {
- // 直接指定されている場合
- columnSize = commentOption.get().horizontalSize();
- // 行の折り返し回数を計算する
- lineWrappingCount = columnSize / maxHorizontalSize;
- }
-
- // コメントの縦サイズ。行数をもとに決定。
- int rowSize = split.length + lineWrappingCount > maxVerticalSize ? maxVerticalSize : split.length + lineWrappingCount;
- if(commentOption.isPresent() && commentOption.get().verticalSize() > 0) {
- // 直接指定されている場合
- rowSize = commentOption.get().verticalSize();
- }
-
- return drawing.createAnchor(
- 0, 0, 0, 0,
- address.getColumn() + horizontalPrefix, address.getRow() + vertialPrefix,
- address.getColumn() + horizontalPrefix + columnSize, address.getRow() + vertialPrefix + rowSize);
- }
-
- /**
- * 新規にコメントの装飾を設定する。
- * セルの装飾に合わせる。
- *
- * @param toRichText 設定先のコメント
- * @param cell コメントを設定する先のセル
- */
- protected void applyCommentFormat(final RichTextString toRichText, final Cell cell) {
-
- toRichText.applyFont(cell.getSheet().getWorkbook().getFontAt(cell.getCellStyle().getFontIndexAsInt()));
-
- }
-
- /**
- * 既にコメントが設定されているときのコメントの装飾を設定する。
- * 既存のコメントの装飾をコピーするが、そのとき、1つ目のフォント設定のみとする。
- *
- * @param toRichText コピー先
- * @param fromrichText コピー元
- */
- protected void copyCommentFormat(final RichTextString toRichText, final RichTextString fromrichText) {
-
- if(toRichText instanceof XSSFRichTextString) {
- toRichText.applyFont(((XSSFRichTextString)fromrichText).getFontOfFormattingRun(0));
-
- } else if(toRichText instanceof HSSFRichTextString) {
- toRichText.applyFont(((HSSFRichTextString)fromrichText).getFontOfFormattingRun(0));
-
- } else {
- logger.warn("not suuported exdcel format comment : {}", toRichText.getClass().getName());
- }
-
- }
- /**
- * コメントの縦方向の開始位置を取得する。
- * 行数分で表現する。
- * @return
- */
- public int getVertialPrefix() {
- return vertialPrefix;
- }
- /**
- * コメントの縦方向の開始位置を設定する。
- * 行数分で表現する。
- * @param vertialPrefix コメントの縦方向の開始位置。(0以上)
- * @throws IllegalArgumentException {@literal vertialPrefix < 0}
- */
- public void setVertialPrefix(int vertialPrefix) {
- ArgUtils.notMin(vertialPrefix, 0, "vertialPrefix");
- this.vertialPrefix = vertialPrefix;
- }
- /**
- * コメントの横方向の開始位置を取得する。
- * 列数分で表現する。
- * @return
- */
- public int getHorizontalPrefix() {
- return horizontalPrefix;
- }
- /**
- * コメントの横方向の開始位置を設定する。
- * 列数分で表現する。
- * @param horizontalPrefix コメントの横方向の開始位置。(0以上)
- * @throws IllegalArgumentException {@literal horizontalPrefix < 0}
- */
- public void setHorizontalPrefix(int horizontalPrefix) {
- ArgUtils.notMin(horizontalPrefix, 0, "horizontalPrefix");
- this.horizontalPrefix = horizontalPrefix;
- }
-
- /**
- * コメントの縦方向の最大サイズを取得する。
- * 行数分で表現する。
- * @return the maxVerticalSize
- */
- public int getMaxVerticalSize() {
- return maxVerticalSize;
- }
-
- /**
- * コメントの縦方向の最大サイズを設定する。
- * 行数分で表現する。
- * @param maxVerticalSize コメントの縦方向の最大サイズ。(1以上)
- * @throws IllegalArgumentException {@literal maxVerticalSize < 1}
- */
- public void setMaxVerticalSize(int maxVerticalSize) {
- ArgUtils.notMin(maxVerticalSize, 1, "maxVerticalSize");
- this.maxVerticalSize = maxVerticalSize;
- }
-
- /**
- * コメントの列方向の最大サイズ。
- * 列数分で表現する。
- * maxHorizontalSize を取得する
- * @return the maxHorizontalSize
- */
- public int getMaxHorizontalSize() {
- return maxHorizontalSize;
- }
-
- /**
- * コメントの列方向の最大サイズ。
- * 列数分で表現する。
- * @param maxHorizontalSize コメントの横方向の最大サイズ。(1以上)
- * @throws IllegalArgumentException {@literal maxHorizontalSize < 1}
- */
- public void setMaxHorizontalSize(int maxHorizontalSize) {
- ArgUtils.notMin(maxHorizontalSize, 1, "maxHorizontalSize");
- this.maxHorizontalSize = maxHorizontalSize;
- }
-
- }