package com.gh.mygreen.xlsmapper; import java.awt.Point; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.poi.hssf.model.InternalSheet; import org.apache.poi.hssf.record.DVRecord; import org.apache.poi.hssf.record.ExtendedFormatRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.aggregates.DataValidityTable; import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidationConstraint; import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressBase; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.extensions.XSSFCellAlignment; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellAlignment; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidation; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDataValidations; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; import com.gh.mygreen.xlsmapper.cellconvert.LinkType; import com.gh.mygreen.xlsmapper.fieldprocessor.CellAddress; import com.github.mygreen.cellformatter.POICell; /** * Apache POIとJExcel APIの差を埋めるユーティリティクラス。 * * @version 1.6 * @author T.TSUCHIE * */ public class POIUtils { /** 標準のセルフォーマッター */ private static CellFormatter defaultCellFormatter = new DefaultCellFormatter(); /** * セルの「縮小して表示する」のメソッドが利用可能かどうか。 * POI-3.10以上の場合、trueとなる。 * @since 0.2.3 */ public static final boolean AVAILABLE_METHOD_CELL_SHRINK_TO_FIT; static { boolean available = false; try { //POI-3.10以降 final Method method = CellStyle.class.getMethod("setShrinkToFit", boolean.class); method.setAccessible(true); available = true; } catch (Exception e) { available = false; } AVAILABLE_METHOD_CELL_SHRINK_TO_FIT = available; } /** * セルの入力規則の取得のメソッドが利用可能かどうか。 * POI-3.11以上の場合、trueとなる。 * @since 0.3 */ public static final boolean AVAILABLE_METHOD_SHEET_DAVA_VALIDATION; static { boolean available = false; try { // POI-3.11移行 final Method method = Sheet.class.getMethod("getDataValidations"); method.setAccessible(true); available = true; } catch(Exception e) { available = false; } AVAILABLE_METHOD_SHEET_DAVA_VALIDATION = available; } /** * セルのハイパーリンクの削除メソッドが利用可能かどうか。 * POI-3.11以上の場合、trueとなる。 * @since 0.4 */ public static final boolean AVAILABLE_METHOD_CELL_REMOVE_HYPERLINK; static { boolean available = false; try { //POI-3.11以降 final Method method = Cell.class.getMethod("removeHyperlink", boolean.class); method.setAccessible(true); available = true; } catch(Exception e) { available = false; } AVAILABLE_METHOD_CELL_REMOVE_HYPERLINK = available; } /** * セルのコメントの設定方法を示す列挙型「AnchorType」が利用可能か。 * POI-3.15委譲の場合、trueとなる。 * @since 1.6 */ public static final boolean AVAILABLE_ANCHOR_TYPE_ENUM; static { boolean available = false; try { //POI-3.15以降 Class.forName("org.apache.poi.ss.usermodel.ClientAnchor$AnchorType"); available = true; } catch(Exception e) { available = false; } AVAILABLE_ANCHOR_TYPE_ENUM = available; } /** * シートの結合セルノ削除メソッドが利用可能かどうか。 * POI-3.15以上の場合、trueとなる。 * @since 1.6 */ public static final boolean AVAILABLE_METHOD_SHEET_REMOVE_MERGE_REGIONS; static { boolean available = false; try { //POI-3.15以降 final Method method = Sheet.class.getMethod("removeMergedRegions", Collection.class); method.setAccessible(true); available = true; } catch(Exception e) { available = false; } AVAILABLE_METHOD_SHEET_REMOVE_MERGE_REGIONS = available; } /** * シートの最大列数を取得する。 * @see jxl.Sheet.getColumns() * @param sheet * @return */ public static int getColumns(final Sheet sheet) { ArgUtils.notNull(sheet, "sheet"); int minRowIndex = sheet.getFirstRowNum(); int maxRowIndex = sheet.getLastRowNum(); int maxColumnsIndex = 0; for(int i = minRowIndex; i <= maxRowIndex; i++) { final Row row = sheet.getRow(i); if(row == null) { continue; } final int column = row.getLastCellNum(); if(column > maxColumnsIndex) { maxColumnsIndex = column; } } return maxColumnsIndex; } /** * シートの最大行数を取得する * * @see jxl.Sheet.getRows() * @param sheet * @return */ public static int getRows(final Sheet sheet) { ArgUtils.notNull(sheet, "sheet"); return sheet.getLastRowNum() + 1; } /** * シートから任意のセルを取得する。 * @since 0.5 * @param sheet シートオブジェクト * @param address アドレス(Point.x=column, Point.y=row) * @return */ public static Cell getCell(final Sheet sheet, final Point address) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notNull(address, "address"); return getCell(sheet, address.x, address.y); } /** * シートから任意のセルを取得する。 * @since 1.4 * @param sheet シートオブジェクト * @param address セルのアドレス * @return */ public static Cell getCell(final Sheet sheet, final CellAddress address) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notNull(address, "address"); return getCell(sheet, address.getColumn(), address.getRow()); } /** * シートから任意のセルを取得する。 * * @see jxl.Sheet.getCell(int column, int row) * @param sheet * @param column * @param row * @return */ public static Cell getCell(final Sheet sheet, final int column, final int row) { ArgUtils.notNull(sheet, "sheet"); Row rows = sheet.getRow(row); if(rows == null) { rows = sheet.createRow(row); } Cell cell = rows.getCell(column); if(cell == null) { cell = rows.createCell(column, Cell.CELL_TYPE_BLANK); } return cell; } /** * 任意の行のセルを全て取得する。 * @see jxl.Seet.getRow(int row) * @param sheet * @param row * @return */ public static Cell[] getRow(final Sheet sheet, final int row) { ArgUtils.notNull(sheet, "sheet"); Row rows = sheet.getRow(row); if(rows == null) { rows = sheet.createRow(row); } int maxColumn = getColumns(sheet); Cell[] cells = new Cell[maxColumn]; for(int i=0; i < maxColumn; i++) { Cell cell = rows.getCell(i); if(cell == null) { cell = rows.createCell(i, Cell.CELL_TYPE_BLANK); } cells[i] = cell; } return cells; } /** * 任意の列のセルを全て取得する。 * @see jxl.Seet.getColumn(int col) * @param sheet * @param col * @return */ public static Cell[] getColumn(final Sheet sheet, final int col) { ArgUtils.notNull(sheet, "sheet"); int maxRow = getRows(sheet); Cell[] cells = new Cell[maxRow]; for(int i=0; i < maxRow; i++) { Row rows = sheet.getRow(i); if(rows == null) { rows = sheet.createRow(i); } Cell cell = rows.getCell(col); if(cell == null) { cell = rows.createCell(col, Cell.CELL_TYPE_BLANK); } cells[i] = cell; } return cells; } /** * フォーマッターを指定してセルの値を取得する * * @param cell * @param cellFormatter * @return */ public static String getCellContents(final Cell cell, final CellFormatter cellFormatter) { ArgUtils.notNull(cell, "cell"); ArgUtils.notNull(cellFormatter, "cellFormatter"); return cellFormatter.format(cell); } /** * 指定してセルの値が空かどうか判定する。 * <p>ブランクセルなどの判定は優先的に行う。 * @param cell * @return */ public static boolean isEmptyCellContents(final Cell cell) { return isEmptyCellContents(cell, defaultCellFormatter); } /** * フォーマッターを指定してセルの値が空かどうか判定する。 * <p>ブランクセルなどの判定は優先的に行う。 * @param sheet シート * @param cellFormatter * @throws IllegalArgumentException {@literal sheet == null.} * @throws IllegalArgumentException {@literal cellFormatter == null.} * @return */ public static boolean isEmptyCellContents(final Cell cell, final CellFormatter cellFormatter) { ArgUtils.notNull(cell, "cell"); ArgUtils.notNull(cellFormatter, "cellFormatter"); return getCellContents(cell, cellFormatter).isEmpty(); } /** * 指定した書式のインデックス番号を取得する。シートに存在しない場合は、新しく作成する。 * @param sheet シート * @param pattern 作成する書式のパターン * @return 書式のインデックス番号。 * @throws IllegalArgumentException {@literal sheet == null.} * @throws IllegalArgumentException {@literal pattern == null || pattern.isEmpty().} */ public static short getDataFormatIndex(final Sheet sheet, final String pattern) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(pattern, "pattern"); return sheet.getWorkbook().getCreationHelper().createDataFormat().getFormat(pattern); } /** * セルに設定されている書式を取得する。 * @since 1.1 * @param cell セルのインスタンス。 * @return 書式が設定されていない場合は、空文字を返す。 * cellがnullの場合も空文字を返す。 * 標準の書式の場合も空文字を返す。 */ public static String getCellFormatPattern(final Cell cell) { if(cell == null) { return ""; } POICell poiCell = new POICell(cell); if(poiCell.getFormatIndex() == 0) { return ""; } else { return poiCell.getFormatPattern(); } } /** * 指定した範囲のセルを結合する。 * @param sheet * @param startCol * @param startRow * @param endCol * @param endRow * @return */ public static CellRangeAddress mergeCells(final Sheet sheet, int startCol, int startRow, int endCol, int endRow) { ArgUtils.notNull(sheet, "sheet"); // 結合先のセルの値を空に設定する for(int r=startRow; r <= endRow; r++) { for(int c=startCol; c <= endCol; c++) { if(r == startRow && c == startCol) { continue; } Cell cell = getCell(sheet, c, r); cell.setCellType(Cell.CELL_TYPE_BLANK); } } final CellRangeAddress range = new CellRangeAddress(startRow, endRow, startCol, endCol); sheet.addMergedRegion(range); return range; } /** * 指定したセルのアドレスの結合情報を取得する。 * @since 0.5 * @param sheet * @param rowIdx * @param colIdx * @return 結合していない場合nullを返す。 */ public static CellRangeAddress getMergedRegion(final Sheet sheet, final int rowIdx, final int colIdx) { ArgUtils.notNull(sheet, "sheet"); final int num = sheet.getNumMergedRegions(); for(int i=0; i < num; i ++) { final CellRangeAddress range = sheet.getMergedRegion(i); if(range.isInRange(rowIdx, colIdx)) { return range; } } return null; } /** * 指定した範囲の結合を解除する。 * @param sheet * @param mergedRange * @return 引数で指定した結合が見つからない場合。 */ public static boolean removeMergedRange(final Sheet sheet, final CellRangeAddress mergedRange) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notNull(mergedRange, "mergedRange"); final String mergedAddress = mergedRange.formatAsString(sheet.getSheetName(), true); final int num = sheet.getNumMergedRegions(); for(int i=0; i < num; i ++) { final CellRangeAddress range = sheet.getMergedRegion(i); final String rangeAddress = range.formatAsString(sheet.getSheetName(), true); if(rangeAddress.equals(mergedAddress)) { sheet.removeMergedRegion(i); return true; } } return false; } /** * 指定した行の下に行を1行追加する * @param sheet * @param rowIndex 追加する行数 * @return 追加した行を返す。 */ public static Row insertRow(final Sheet sheet, final int rowIndex) { ArgUtils.notNull(sheet, "cell"); ArgUtils.notMin(rowIndex, 0, "rowIndex"); // 最終行を取得する int lastRow = sheet.getLastRowNum(); if(lastRow < rowIndex) { // データが定義されている範囲害の場合は、行を新たに作成して返す。 return sheet.createRow(rowIndex); } sheet.shiftRows(rowIndex, lastRow+1, 1); return sheet.createRow(rowIndex); } /** * 指定した行を削除する。 * <p>削除した行は上に詰める。 * @since 0.5 * @param sheet * @param rowIndex 削除する行数 * @return 削除した行 */ public static Row removeRow(final Sheet sheet, final int rowIndex) { ArgUtils.notNull(sheet, "cell"); ArgUtils.notMin(rowIndex, 0, "rowIndex"); final Row row = sheet.getRow(rowIndex); if(row == null) { // 削除対象の行にデータが何もない場合 return row; } sheet.removeRow(row); // 上に1つ行をずらす int lastRow = sheet.getLastRowNum(); if(rowIndex +1 > lastRow) { return row; } sheet.shiftRows(rowIndex+1, lastRow, -1); return row; } /** * セルの折り返し設定を有効にする * @param cell * @param forceWrapText trueの場合有効にする。falseの場合は変更しない。 */ public static void wrapCellText(final Cell cell, final boolean forceWrapText) { ArgUtils.notNull(cell, "cell"); if(!forceWrapText) { return; } final CellStyle style = cell.getCellStyle(); style.setWrapText(true); setCellStyleWithShrinkToFit(cell, style, false); cell.setCellStyle(style); } /** * セルの縮小表示設定を有効にする。 * @param cell * @param forceShrinkToFit trueの場合有効にする。falseの場合は変更しない。 */ public static void shrinkToFit(final Cell cell, final boolean forceShrinkToFit) { ArgUtils.notNull(cell, "cell"); if(!forceShrinkToFit) { return; } final CellStyle style = cell.getCellStyle(); style.setWrapText(false); setCellStyleWithShrinkToFit(cell, style, true); } /** * セルの縮小表示設定を変更する。 * <p>POI-3.9以前の場合は、リフレクションで強制的に変更する。 * @param cell 変更対象のセル * @param style 縮小表示設定を行うStyle * @param shrinkToFit */ public static void setCellStyleWithShrinkToFit(final Cell cell, final CellStyle style, final boolean shrinkToFit) { ArgUtils.notNull(cell, "cell"); ArgUtils.notNull(style, "style"); if(AVAILABLE_METHOD_CELL_SHRINK_TO_FIT) { try { //POI-3.10以降 final Method method = style.getClass().getMethod("setShrinkToFit", boolean.class); method.setAccessible(true); method.invoke(style, true); cell.setCellStyle(style); return; } catch (Exception e) {} } if(style instanceof HSSFCellStyle) { // POI-3.9以前のExcel2003形式 try { final Field field = style.getClass().getDeclaredField("_format"); field.setAccessible(true); ExtendedFormatRecord record = (ExtendedFormatRecord) field.get(style); record.setShrinkToFit(shrinkToFit); cell.setCellStyle(style); return; } catch (Exception e ) { } } else if(style instanceof XSSFCellStyle) { // POI-3.9以前のExcel2007形式 try { final Method aligngmentMethod = style.getClass().getDeclaredMethod("getCellAlignment"); aligngmentMethod.setAccessible(true); final XSSFCellAlignment alignment = (XSSFCellAlignment) aligngmentMethod.invoke(style); final Field alignmentField = alignment.getClass().getDeclaredField("cellAlignement"); alignmentField.setAccessible(true); CTCellAlignment alignment2 = (CTCellAlignment) alignmentField.get(alignment); alignment2.setShrinkToFit(shrinkToFit); cell.setCellStyle(style); return; } catch (Exception e ) { } } } /** * 座標をExcelのアドレス形式'A1'などに変換する * @param rowIndex 行インデックス * @param colIndex 列インデックス * @return */ public static String formatCellAddress(final int rowIndex, final int colIndex) { return CellReference.convertNumToColString(colIndex) + String.valueOf(rowIndex+1); } /** * 座標をExcelのアドレス形式'A1'になどに変換する。 * @param cellAddress セルの位置情報 * @return * @throws IllegalArgumentException address == null. */ public static String formatCellAddress(final Point cellAddress) { ArgUtils.notNull(cellAddress, "cellAddress"); return formatCellAddress(cellAddress.y, cellAddress.x); } /** * セルのアドレス'A1'を取得する。 * @param cell セル情報 * @return IllegalArgumentException cell == null. */ public static String formatCellAddress(final Cell cell) { ArgUtils.notNull(cell, "cell"); return CellReference.convertNumToColString(cell.getColumnIndex()) + String.valueOf(cell.getRowIndex()+1); } /** * リンクのアドレスを判定する。 * @param linkAddress リンクのアドレス(URL) * @return 不明な場合は{@link LinkType#UNKNOWN}を返す。 * @throws IllegalArgumentException linkAddress が空文字の場合。 */ public static LinkType judgeLinkType(final String linkAddress) { ArgUtils.notEmpty(linkAddress, "linkAddress"); if(linkAddress.matches(".*![\\p{Alnum}]+")) { // !A1のアドレスを含むかどうか return LinkType.DOCUMENT; } else if(linkAddress.matches("[\\p{Alpha}]+[0-9]+")) { // A1の通常のアドレスの形式 return LinkType.DOCUMENT; } else if(linkAddress.matches(".+@.+")) { // @を含むかどうか return LinkType.EMAIL; } else if(linkAddress.matches("[\\p{Alpha}]+://.+")) { // プロトコル付きかどうか return LinkType.URL; } else if(linkAddress.matches(".+\\.[\\p{Alnum}]+")) { // 拡張子付きかどうか return LinkType.FILE; } else { return LinkType.UNKNOWN; } } /** * 入力規則の範囲を更新する。 * @since 0.5 * @param sheet シート * @param oldRegion 更新対象の範囲。 * @param newRegion 新しい範囲。 * @return true:更新完了。false:指定した範囲を持つ入力規則が見つからなかった場合。 */ public static boolean updateDataValidationRegion(final Sheet sheet, final CellRangeAddressList oldRegion, final CellRangeAddressList newRegion) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notNull(oldRegion, "oldRegion"); ArgUtils.notNull(newRegion, "newRegion"); if(sheet instanceof XSSFSheet) { final List<String> oldSqref = convertSqref(oldRegion); try { final XSSFSheet xssfSheet = (XSSFSheet) sheet; Field fWorksheet = XSSFSheet.class.getDeclaredField("worksheet"); fWorksheet.setAccessible(true); CTWorksheet worksheet = (CTWorksheet) fWorksheet.get(xssfSheet); CTDataValidations dataValidations = worksheet.getDataValidations(); if(dataValidations == null) { return false; } for(int i=0; i < dataValidations.getCount(); i++) { CTDataValidation dv = dataValidations.getDataValidationArray(i); // 規則の範囲を比較し、同じならば範囲を書き換える。 @SuppressWarnings("unchecked") List<String> sqref = new ArrayList<>(dv.getSqref()); if(equalsSqref(sqref, oldSqref)) { List<String> newSqref = convertSqref(newRegion); dv.setSqref(newSqref); // 設定し直す dataValidations.setDataValidationArray(i, dv); return true; } } return false; } catch(Exception e) { throw new RuntimeException("fail update DataValidation's Regsion.", e); } } else if(sheet instanceof HSSFSheet) { final HSSFSheet hssfSheet = (HSSFSheet) sheet; try { Field fWorksheet = HSSFSheet.class.getDeclaredField("_sheet"); fWorksheet.setAccessible(true); InternalSheet worksheet = (InternalSheet) fWorksheet.get(hssfSheet); DataValidityTable dvt = worksheet.getOrCreateDataValidityTable(); // シート内の入力規則のデータを検索して、一致するものがあれば書き換える。 final AtomicBoolean updated = new AtomicBoolean(false); RecordVisitor visitor = new RecordVisitor() { @Override public void visitRecord(final Record r) { if (!(r instanceof DVRecord)) { return; } final DVRecord dvRecord = (DVRecord) r; final CellRangeAddressList region = dvRecord.getCellRangeAddress(); if(equalsRegion(region, oldRegion)) { // 一旦既存の範囲を削除する。 while(region.countRanges() != 0) { region.remove(0); } // 新しい範囲を追加する。 for(CellRangeAddress newRange : newRegion.getCellRangeAddresses()) { region.addCellRangeAddress(newRange); } updated.set(true); return; } } }; dvt.visitContainedRecords(visitor); return updated.get(); } catch(Exception e) { throw new RuntimeException("fail update DataValidation's Regsion.", e); } } else { throw new UnsupportedOperationException("not supported update dava validation's region for type " + sheet.getClass().getName()); } } /** * CellRangeAddressを文字列形式のリストに変換する。 * @since 0.5 * @param region * @return */ private static List<String> convertSqref(final CellRangeAddressList region) { List<String> sqref = new ArrayList<>(); for(CellRangeAddress range : region.getCellRangeAddresses()) { sqref.add(range.formatAsString()); } return sqref; } /** * 文字列形式のセルの範囲が同じかどうか比較する。 * @since 0.5 * @param sqref1 * @param sqref2 * @return */ public static boolean equalsSqref(final List<String> sqref1, final List<String> sqref2) { if(sqref1.size() != sqref2.size()) { return false; } Collections.sort(sqref1); Collections.sort(sqref2); final int size = sqref1.size(); for(int i=0; i < size; i++) { if(!sqref1.get(i).equals(sqref2.get(i))) { return false; } } return true; } /** * 文字列形式のセルの範囲が同じかどうか比較する。 * @since 0.5 * @param region1 * @param region2 * @return */ public static boolean equalsRegion(final CellRangeAddressList region1, final CellRangeAddressList region2) { return equalsSqref(convertSqref(region1), convertSqref(region2)); } // public static boolean removeDataValidation(final Sheet sheet, final DataValidation dataValidation) { // ArgUtils.notNull(sheet, "sheet"); // ArgUtils.notNull(dataValidation, "dataValidation"); // // if(sheet instanceof XSSFSheet) { // final XSSFSheet xssfSheet = (XSSFSheet) sheet; // final XSSFDataValidation xssfDataValidation = (XSSFDataValidation) dataValidation; // // try { // final Field fWorksheet = XSSFSheet.class.getDeclaredField("worksheet"); // fWorksheet.setAccessible(true); // CTWorksheet worksheet = (CTWorksheet) fWorksheet.get(xssfSheet); // // // 既存の入力規則の取得 // CTDataValidations dataValidations = worksheet.getDataValidations(); // if(dataValidations == null) { // return false; // } // // final Method mCtVal = XSSFDataValidation.class.getDeclaredMethod("getCtDdataValidation"); // mCtVal.setAccessible(true); // // CTDataValidation removeVal = (CTDataValidation) mCtVal.invoke(xssfDataValidation); // // List<CTDataValidation> newList = new ArrayList<>(); // for(int i=0; i < dataValidations.getCount(); i++) { // CTDataValidation itemVal = dataValidations.getDataValidationArray(i); // if(!itemVal.equals(removeVal)) { // newList.add(itemVal); // } // // } // // // 削除された(サイズが変わった)場合に、入力規則を設定し直す。 // if(newList.size() != dataValidations.getCount()) { // dataValidations.setDataValidationArray(newList.toArray(new CTDataValidation[newList.size()])); // return true; // } // // return false; // // } catch (Exception e) { // throw new RuntimeException("fail remove sheet validation rule.", e); // } // // } else if(sheet instanceof HSSFSheet) { // // final HSSFSheet hssfSheet = (HSSFSheet) sheet; // final HSSFDataValidation hssfDataValidation = (HSSFDataValidation) dataValidation; // try { // // Field fWorksheet = HSSFSheet.class.getDeclaredField("_sheet"); // fWorksheet.setAccessible(true); // InternalSheet worksheet = (InternalSheet) fWorksheet.get(hssfSheet); // // DataValidityTable dvt = worksheet.getOrCreateDataValidityTable(); // final CellRangeAddressList removeRegion = hssfDataValidation.getRegions(); // // // シート内の入力規則のデータを検索して、一致するものがあれば書き換える。 // final AtomicBoolean removed = new AtomicBoolean(false); // RecordVisitor visitor = new RecordVisitor() { // // @Override // public void visitRecord(final Record r) { // if (!(r instanceof DVRecord)) { // return; // } // // final DVRecord dvRecord = (DVRecord) r; // final CellRangeAddressList region = dvRecord.getCellRangeAddress(); // if(equalsRegion(region, removeRegion)) { // //TODO: // // } // // } // }; // // dvt.visitContainedRecords(visitor); // // return removed.get(); // // } catch (Exception e) { // throw new RuntimeException("fail remove sheet validation rule.", e); // } // // } else { // throw new UnsupportedOperationException("not supported remove dava validation's region for type " + sheet.getClass().getName()); // } // } /** * テンプレートの入力規則の制約「リスト」を追加する。 * <p>POI-3.7以上が必要。 * @param sheet シート * @param constraints 制約とするコレクションの中身 * @param startPosition 開始位置 * @param endPosition 終了位置 */ public static void setupExplicitListConstaint(final Sheet sheet, final Collection<String> constraints, final Point startPosition, final Point endPosition) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(constraints, "constraints"); ArgUtils.notNull(startPosition, "startPosition"); ArgUtils.notNull(endPosition, "endPosition"); setupExplicitListConstaint(sheet, constraints.toArray(new String[constraints.size()]), startPosition, endPosition); } /** * テンプレートの入力規則の制約「リスト」を追加する。 * <p>POI-3.7以上が必要。 * @param sheet シート * @param constraints 制約とするリストの中身 * @param startPosition 開始位置 * @param endPosition 終了位置 */ public static void setupExplicitListConstaint(final Sheet sheet, final String[] constraints, final Point startPosition, final Point endPosition) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(constraints, "constraints"); ArgUtils.notNull(startPosition, "startPosition"); ArgUtils.notNull(endPosition, "endPosition"); final DataValidationHelper helper = sheet.getDataValidationHelper(); final DataValidationConstraint constraint = helper.createExplicitListConstraint(constraints); setupConstaint(sheet, constraint, startPosition, endPosition); } /** * テンプレートの入力規則の制約「リスト」を式形式で追加する。 * <p>POI-3.7以上が必要。 * @param sheet シート * @param listFormula 入力規則の式('='は含まない) * @param startPosition 設定するセルの開始位置 * @param endPosition 設定するセルの終了位置 */ public static void setupFormulaListConstaint(final Sheet sheet, final String listFormula, final Point startPosition, final Point endPosition) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(listFormula, "listFormula"); ArgUtils.notNull(startPosition, "startPosition"); ArgUtils.notNull(endPosition, "endPosition"); final DataValidationHelper helper = sheet.getDataValidationHelper(); final DataValidationConstraint constraint = helper.createFormulaListConstraint("=" + listFormula); setupConstaint(sheet, constraint, startPosition, endPosition); } /** * 指定した範囲のセルに制約を追加する。 * <p>POI-3.7以上が必要。 * @param sheet シート * @param constraint 制約 * @param startPosition 設定するセルの開始位置 * @param endPosition 設定するセルの終了位置 */ public static void setupConstaint(final Sheet sheet, final DataValidationConstraint constraint, final Point startPosition, final Point endPosition) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notNull(constraint, "constraint"); ArgUtils.notNull(startPosition, "startPosition"); ArgUtils.notNull(endPosition, "endPosition"); final DataValidationHelper helper = sheet.getDataValidationHelper(); final CellRangeAddressList region = new CellRangeAddressList( startPosition.y, endPosition.y, startPosition.x, endPosition.x ); final DataValidation dataValidation = helper.createValidation(constraint, region); sheet.addValidationData(dataValidation); } /** * 指定した範囲の名前を登録する。 * <p>POI-3.7以上が必要。 * <p>指定した名前が既に存在する場合は、新しい範囲に書き換える。 * @param sheet シート * @param name 名前 * @param startPosition 設定するセルの開始位置 * @param endPosition 設定するセルの終了位置 * @return */ public static Name defineName(final Sheet sheet, final String name, final Point startPosition, final Point endPosition) { ArgUtils.notNull(sheet, "sheet"); ArgUtils.notEmpty(name, "name"); ArgUtils.notNull(startPosition, "startPosition"); ArgUtils.notNull(endPosition, "endPosition"); final Workbook workbook = sheet.getWorkbook(); Name nameObj = workbook.getName(name); if(nameObj == null) { nameObj = workbook.createName(); nameObj.setNameName(name); } final AreaReference areaRef = buildNameArea(sheet.getSheetName(), startPosition, endPosition); nameObj.setRefersToFormula(areaRef.formatAsString()); return nameObj; } /** * 名前の範囲の形式を組み立てる。 * <code>シート名!$A$1:$A:$5</code> * @param sheetName シート名 * @param startPosition 設定するセルの開始位置 * @param endPosition 設定するセルの終了位置 * @return */ public static AreaReference buildNameArea(final String sheetName, final Point startPosition, final Point endPosition) { ArgUtils.notEmpty(sheetName, "sheetName"); ArgUtils.notNull(startPosition, "startPosition"); ArgUtils.notNull(endPosition, "endPosition"); final CellReference firstRefs = new CellReference(sheetName, startPosition.y, startPosition.x, true, true); final CellReference lastRefs = new CellReference(sheetName, endPosition.y, endPosition.x, true, true); return new AreaReference(firstRefs, lastRefs); } /** * セルに設定されているハイパーリンクを削除する。 * <p>POIのバージョンによって、{@link Cell#removeHyperlink()}のネイティブのメソッドを呼び出す。 * @since 0.4 * @param cell * @return true: ハイパーリンクが設定されており削除できた場合。false:ハイパーリンクが設定されていない場合。 * @throws IllegalArgumentException cell == null. */ public static boolean removeHyperlink(final Cell cell) { ArgUtils.notNull(cell, "cell"); final Hyperlink link = cell.getHyperlink(); if(link == null) { return false; } if(AVAILABLE_METHOD_CELL_REMOVE_HYPERLINK) { cell.removeHyperlink(); return true; } else { // 既存のハイパーリンクのURLをクリアし、再設定する。 link.setAddress(""); cell.setHyperlink(link); return true; } } /** * セルのコメントのanchorTypeを設定する。 * <p>POI-3.15からタイプがint型から列挙型に変わったため、列挙型利用可能であれば自動的に切り替える。</p> * @since 1.6 * @param anchor * @param type アンカーのタイプ */ public static void setClientAnchorType(final ClientAnchor anchor, final Object type) { try { final Method method; if(AVAILABLE_ANCHOR_TYPE_ENUM) { // AnchorTypeが利用可能なとき method = ClientAnchor.class.getMethod("setAnchorType", Class.forName("org.apache.poi.ss.usermodel.ClientAnchor$AnchorType")); } else { method = ClientAnchor.class.getMethod("setAnchorType", int.class); } method.invoke(anchor, type); } catch(Exception e) { throw new RuntimeException("fail invoke method ClientAnchor#setAnchorType(type).", e); } } /** * セルの範囲が重複(交錯)しているかどうか判定する。 * <p>このメソッドは、POI-3.14で追加されたメソッド{@literal Sheet#intersects(...)}と後方互換性を保つためのもの。</p> * * @param my * @param other * @return trueの場合、1つでもセルの範囲が重複している。 */ public static boolean intersectsRegion(final CellRangeAddressBase my, final CellRangeAddressBase other) { return my.getFirstRow() <= other.getLastRow() && my.getFirstColumn() <= other.getLastColumn() && other.getFirstRow() <= my.getLastRow() && other.getFirstColumn() <= my.getLastColumn(); } }