001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.model; 017 018import java.io.InputStream; 019// import java.io.FileInputStream; // 8.5.4.2 (2024/01/12) delete 020import java.io.BufferedReader; // 6.2.2.0 (2015/03/27) 021import java.io.BufferedInputStream; 022import java.io.FileNotFoundException; 023import java.io.File; 024import java.io.IOException; 025import java.nio.file.Files; // 6.2.2.0 (2015/03/27) 026import java.nio.charset.Charset; // 6.2.2.0 (2015/03/27) 027 028import java.util.zip.ZipException; // 8.5.0.0 (2023/04/21) 029import java.util.Set; // 6.0.2.3 (2014/10/10) 030import java.util.TreeSet; // 6.0.2.3 (2014/10/10) 031import java.util.List; // 6.4.6.0 (2016/05/27) poi-3.15 032// import java.util.ArrayList; // 8.0.1.0 (2021/10/29) 033 034// import org.apache.xmlbeans.XmlException; // 8.0.0.0 (2021/07/31) Delete 035// import org.apache.poi.POITextExtractor; 036import org.apache.poi.extractor.POITextExtractor; // 7.0.0.0 (2018/10/01) poi-3.17.jar → poi-4.0.0.jar 037// import org.apache.poi.extractor.ExtractorFactory; 038// import org.apache.poi.ooxml.extractor.ExtractorFactory; // 7.0.0.0 (2018/10/01) poi-ooxml-3.17.jar → poi-ooxml-4.0.0.jar 039import org.apache.poi.ooxml.extractor.POIXMLExtractorFactory; // 8.0.0.0 (2021/07/31) poi-ooxml-4.0.0.jar → poi-ooxml-5.0.0.jar 040import org.apache.poi.hwpf.HWPFDocument; 041import org.apache.poi.hwpf.usermodel.Range; 042import org.apache.poi.hwpf.usermodel.Paragraph; 043import org.apache.poi.hssf.usermodel.HSSFCellStyle; 044import org.apache.poi.hslf.usermodel.HSLFTextParagraph; // 6.4.6.0 (2016/05/27) poi-3.15 045import org.apache.poi.hslf.usermodel.HSLFSlide; // 6.4.6.0 (2016/05/27) poi-3.15 046import org.apache.poi.hslf.usermodel.HSLFSlideShow; // 6.4.6.0 (2016/05/27) poi-3.15 047 048import org.apache.poi.xwpf.usermodel.XWPFDocument; // 6.2.0.0 (2015/02/27) 049import org.apache.poi.xwpf.usermodel.XWPFParagraph; // 6.2.0.0 (2015/02/27) 050import org.apache.poi.xwpf.model.XWPFCommentsDecorator; // 8.5.0.0 (2023/04/21) Wordのテキスト抜出を改造 051import org.apache.poi.xwpf.usermodel.IBodyElement; // 8.5.0.0 (2023/04/21) 052import org.apache.poi.xwpf.usermodel.XWPFTable; // 8.5.0.0 (2023/04/21) 053import org.apache.poi.xwpf.usermodel.XWPFTableCell; // 8.5.0.0 (2023/04/21) 054import org.apache.poi.xwpf.usermodel.XWPFTableRow; // 8.5.0.0 (2023/04/21) 055import org.apache.poi.xwpf.usermodel.XWPFSDT; // 8.5.0.0 (2023/04/21) 056 057import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; // 8.1.0.1 (2022/01/07) 058import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; // 8.1.0.1 (2022/01/07) 059 060import org.apache.poi.xslf.usermodel.XMLSlideShow; // 6.2.0.0 (2015/02/27) 061// import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor; // 7.0.0.0 (2018/10/01) POI4.0.0 deprecation 062// import org.apache.poi.sl.extractor.SlideShowExtractor; // 7.0.0.0 (2018/10/01) POI4.0.0 XSLFPowerPointExtractor → SlideShowExtractor 8.5.0.0 (2023/04/21) Delete 063import org.apache.poi.xslf.usermodel.XSLFSlide; // 8.5.0.0 (2023/04/21) 064import org.apache.poi.xslf.usermodel.XSLFShape; // 7.0.0.0 (2018/10/01) POI4.0.0 XSLFPowerPointExtractor → SlideShowExtractor 065import org.apache.poi.xslf.usermodel.XSLFTextParagraph; // 7.0.0.0 (2018/10/01) POI4.0.0 XSLFPowerPointExtractor → SlideShowExtractor 066import org.apache.poi.sl.usermodel.TableCell; // 8.5.0.0 (2023/04/21) 067import org.apache.poi.sl.usermodel.ShapeContainer; // 8.5.0.0 (2023/04/21) 068import org.apache.poi.sl.usermodel.TableShape; // 8.5.0.0 (2023/04/21) 069import org.apache.poi.sl.usermodel.Shape; // 8.5.0.0 (2023/04/21) 070import org.apache.poi.sl.usermodel.TextShape; // 8.5.0.0 (2023/04/21) 071import org.apache.poi.sl.usermodel.TextRun; // 8.5.0.0 (2023/04/21) 072 073import org.apache.pdfbox.pdmodel.PDDocument; // 8.5.0.0 (2023/05/12) PDFファイル処理 074// import org.apache.pdfbox.pdmodel.PDPage; // 8.5.5.1 (2024/02/29) pdfbox 3.0.1 075import org.apache.pdfbox.Loader; // 8.5.5.1 (2024/02/29) pdfbox 3.0.1 076import org.apache.pdfbox.text.PDFTextStripper; // 8.5.0.0 (2023/05/12) 077import org.apache.pdfbox.io.RandomAccessReadBufferedFile; // 8.5.5.1 (2024/02/29) pdfbox 3.0.1 078 079// 8.5.0.0 (2023/05/12) pdfboxの警告抑止 … ただし、Log4J に切り替えると、制御できない。 080// import java.util.logging.Logger; // 8.5.0.0 (2023/05/12) pdfboxの警告抑止 081// import java.util.logging.Level; // 8.5.0.0 (2023/05/12) 082 083import org.apache.poi.xssf.usermodel.XSSFSimpleShape; // 8.1.0.1 (2022/01/07) テキスト変換処理 084 085// import org.apache.poi.openxml4j.exceptions.InvalidFormatException; // 8.0.0.0 (2021/07/31) Delete 086// import org.apache.poi.openxml4j.exceptions.OpenXML4JException ; // 6.1.0.0 (2014/12/26) findBugs 8.0.0.0 (2021/07/31) Delete 087import org.apache.poi.ss.usermodel.WorkbookFactory; 088import org.apache.poi.ss.usermodel.Workbook; 089import org.apache.poi.ss.usermodel.Sheet; 090import org.apache.poi.ss.usermodel.Row; 091import org.apache.poi.ss.usermodel.Cell; 092import org.apache.poi.ss.usermodel.CellStyle; 093import org.apache.poi.ss.usermodel.CreationHelper; // 8.1.2.3 (2022/05/20) 復活 094import org.apache.poi.ss.usermodel.RichTextString; 095import org.apache.poi.ss.usermodel.DateUtil; 096import org.apache.poi.ss.usermodel.FormulaEvaluator; // 8.1.2.3 (2022/05/20) 復活 097import org.apache.poi.ss.usermodel.CellValue; // 8.1.2.3 (2022/05/20) 098import org.apache.poi.ss.usermodel.Name; // 6.0.2.3 (2014/10/10) 099import org.apache.poi.ss.usermodel.CellType; // 6.5.0.0 (2016/09/30) poi-3.15 100import org.apache.poi.ss.util.SheetUtil; 101 102import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 103import org.opengion.fukurou.util.FileInfo; // 6.2.3.0 (2015/05/01) 104// import org.opengion.fukurou.system.ThrowUtil; // 6.4.2.0 (2016/01/29) 8.5.0.0 (2023/04/21) Delete 105import org.opengion.fukurou.system.Closer; // 6.2.0.0 (2015/02/27) 106import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 107import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.4.2.1 (2016/02/05) refactoring 108// import org.apache.poi.sl.usermodel.Slide; // 8.5.0.0 (2023/04/21) Delete 109 110/** 111 * POI による、Excel/Word/PoworPoint等に対する、ユーティリティクラスです。 112 * 113 * 基本的には、ネイティブファイルを読み取り、テキストを取得する機能が主です。 114 * Excel、Word、PowerPoint、Visio、Publisher からのテキスト取得が可能です。 115 * 116 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 117 * @og.rev 6.2.0.0 (2015/02/27) パッケージ変更(util → model) 118 * @og.group その他 119 * 120 * @version 6.0 121 * @author Kazuhiko Hasegawa 122 * @since JDK7.0, 123 */ 124public final class POIUtil { 125 /** このプログラムのVERSION文字列を設定します。 {@value} */ 126 private static final String VERSION = "8.5.5.1 (2024/02/29)" ; 127 128 // 6.2.3.0 (2015/05/01) 129 /** POI対象サフィックス {@value} */ 130 public static final String POI_SUFIX = "ppt,pptx,doc,docx,xls,xlsx,xlsm" ; 131 132 // 8.5.0.0 (2023/04/21) 133 /** テキスト対象サフィックス {@value} */ 134 public static final String TXT_SUFIX = "txt,csv,jsp,java,html,xml,css,js,json,py" ; 135 136 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 137 private static final String ERR_OPEN = "ファイルがすでにオープンされています"; 138 private static final String ERR_NOT_EXIST = "ファイルが存在しません"; 139 private static final String ERR_READ = "ファイル読込みエラー"; 140 private static final String ERR_PATH_GET = "正規のパス名取得エラー"; 141 142 /** 143 * デフォルトコンストラクターをprivateにして、 144 * オブジェクトの生成をさせないようにする。 145 * 146 */ 147 private POIUtil() {} 148 149 /** 150 * 引数ファイルが、POI関連の拡張子ファイルかどうかを判定します。 151 * 152 * Excel、Word、PowerPoint、Visio、Publisher からのテキスト取得が可能です。 153 * ファイルの拡張子が、{@value #POI_SUFIX} の場合、true を返します。 154 * 155 * @og.rev 6.2.3.0 (2015/05/01) POI関連の拡張子ファイルかどうかを判定 156 * 157 * @param file 判定するファイル 158 * @return POI関連の拡張子の場合、true 159 */ 160 public static boolean isPOI( final File file ) { 161 return POI_SUFIX.contains( FileInfo.getSUFIX( file ) ); 162 } 163 164 /** 165 * 引数ファイルが、処理対象となるテキスト関連の拡張子ファイルかどうかを判定します。 166 * 167 * ファイルの拡張子が、{@value #TXT_SUFIX} の場合、true を返します。 168 * 169 * @og.rev 8.5.0.0 (2023/04/21) 新規追加 170 * 171 * @param file 判定するファイル 172 * @return テキスト関連の拡張子の場合、true 173 */ 174 public static boolean isText( final File file ) { 175 return TXT_SUFIX.contains( FileInfo.getSUFIX( file ) ); 176 } 177 178 /** 179 * 引数ファイルが、textReader の読み取り対象となる拡張子ファイルかどうかを判定します。 180 * 181 * ファイルの拡張子が {@value #POI_SUFIX}、{@value #TXT_SUFIX}、pdf の場合、true を返します。 182 * 183 * @og.rev 8.5.0.0 (2023/05/12) 新規追加 184 * 185 * @param file 判定するファイル 186 * @return テキスト関連の拡張子の場合、true 187 */ 188 public static boolean isReadText( final File file ) { 189 final String sufix = FileInfo.getSUFIX( file ); // sufix は小文字変換されてくる。 190 191 return POI_SUFIX.contains( sufix ) 192 || TXT_SUFIX.contains( sufix ) 193 || "pdf".equals( sufix ) ; 194 } 195 196 /** 197 * 引数ファイルを、POITextExtractor を使用してテキスト化します。 198 * 199 * Excel、Word、PowerPoint、Visio、Publisher からのテキスト取得が可能です。 200 * 拡張子から、ファイルの種類を自動判別します。 201 * 202 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 203 * @og.rev 6.2.0.0 (2015/02/27) getText → extractor に変更 204 * @og.rev 8.0.0.0 (2021/07/31) ExtractorFactory → POIXMLExtractorFactory に変更 205 * 206 * @param file 入力ファイル名 207 * @return 変換後のテキスト 208 * @og.rtnNotNull 209 */ 210 public static String extractor( final File file ) { 211 // InputStream fis = null; 212 POITextExtractor extractor = null; 213 try { 214 // fis = new BufferedInputStream( new FileInputStream( file ) ); 215 // extractor = ExtractorFactory.createExtractor( fis ); 216 // extractor = ExtractorFactory.createExtractor( file ); 217 extractor = new POIXMLExtractorFactory().create( file , null ); // 8.0.0.0 (2021/07/31) poi-ooxml-4.0.0.jar → poi-ooxml-5.0.0.jar 218 return extractor.getText(); 219 } 220 catch( final FileNotFoundException ex ) { 221 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 222// final String errMsg = "ファイルが存在しません[" + file + "]" + CR + ex.getMessage() ; 223 final String errMsg = simpleErrMsg( ERR_NOT_EXIST,file,ex ); // "ファイルが存在しません" 224 throw new OgRuntimeException( errMsg,ex ); 225 } 226 catch( final IOException ex ) { 227 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 228// final String errMsg = "ファイル処理エラー[" + file + "]" + CR + ex.getMessage() ; 229 final String errMsg = simpleErrMsg( "ファイル処理エラー",file,ex ); 230 throw new OgRuntimeException( errMsg,ex ); 231 } 232 // 8.0.0.0 (2021/07/31) poi-ooxml-4.0.0.jar → poi-ooxml-5.0.0.jar 233// catch( final InvalidFormatException ex ) { 234// final String errMsg = "ファイルフォーマットエラー[" + file + "]" + CR + ex.getMessage() ; 235// throw new OgRuntimeException( errMsg,ex ); 236// } 237// catch( final OpenXML4JException ex ) { 238// final String errMsg = "ODF-XML処理エラー[" + file + "]" + CR + ex.getMessage() ; 239// throw new OgRuntimeException( errMsg,ex ); 240// } 241// catch( final XmlException ex ) { 242// final String errMsg = "XML処理エラー[" + file + "]" + CR + ex.getMessage() ; 243// throw new OgRuntimeException( errMsg,ex ); 244// } 245 finally { 246 Closer.ioClose( extractor ); 247 // Closer.ioClose( fis ); 248 } 249 } 250 251 /** 252 * 引数ファイル(Text)を、テキスト化します。 253 * 254 * ここでは、ファイルとエンコードを指定して、ファイルのテキスト全てを読み取ります。 255 * 256 * @og.rev 6.2.2.0 (2015/03/27) 引数ファイル(Text)を、テキスト化。 257 * @og.rev 6.2.3.0 (2015/05/01) textReader → extractor に変更 258 * 259 * @param file 入力ファイル 260 * @param encode エンコード名 261 * @return ファイルのテキスト 262 */ 263 public static String extractor( final File file , final String encode ) { 264 try { 265 // 指定のファイルをバイト列として読み込む 266 final byte[] bytes = Files.readAllBytes( file.toPath() ); 267 // 読み込んだバイト列を エンコードして文字列にする 268 return new String( bytes, encode ); 269 } 270 // catch( final UnsupportedEncodingException ex ) { 271 // final String errMsg = "エンコードが不正です[" + file + "] , ENCODE=[" + encode + "]" ; 272 // throw new OgRuntimeException( errMsg,ex ); 273 // } 274 catch( final IOException ex ) { 275 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 276// final String errMsg = "ファイル読込みエラー[" + file + "] , ENCODE=[" + encode + "]" ; 277 final String errMsg = simpleErrMsg( ERR_READ,file,ex,"ENCODE",encode ); // "ファイル読込みエラー" 278 throw new OgRuntimeException( errMsg,ex ); 279 } 280 } 281 282 /** 283 * 引数ファイル(Text)を、テキスト化します。 284 * 285 * ここでは、ファイルとエンコードを指定して、ファイルのテキスト全てを読み取ります。 286 * 287 * @og.rev 6.2.2.0 (2015/03/27) 引数ファイル(Text)を、テキスト化。 288 * @og.rev 8.5.4.2 (2024/01/12) テキストのコメントに返す行番号に、+1 しておく。 289 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 290 * 291 * @param file 入力ファイル 292 * @param conv イベント処理させるI/F 293 * @param encode エンコード名 294 */ 295 public static void textReader( final File file , final TextConverter<String,String> conv , final String encode ) { 296 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 297// BufferedReader reader = null ; 298 299 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 300// try { 301// reader = Files.newBufferedReader( file.toPath() , Charset.forName( encode ) ); 302 try ( BufferedReader reader = Files.newBufferedReader( file.toPath() , Charset.forName( encode ) ) ) { 303 String line ; 304 while((line = reader.readLine()) != null) { 305 // 8.5.4.2 (2024/01/12) 初期値は 0 のままとしておく(読み込みエラー時に、1 ではおかしい) 306// conv.change( line,String.valueOf( rowNo++ ) ); 307 conv.change( line,String.valueOf( ++rowNo ) ); // 8.5.4.2 (2024/01/12) 308 } 309 } 310 catch( final IOException ex ) { 311 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 312// final String errMsg = "ファイル読込みエラー[" + file + "] , ENCODE=[" + encode + "] , ROW=[" + rowNo + "]" ; 313 final String errMsg = simpleErrMsg( ERR_READ,file,ex,"ENCODE",encode,"ROW",String.valueOf(rowNo) ); // "ファイル読込みエラー" 314 throw new OgRuntimeException( errMsg,ex ); 315 } 316// finally { 317// Closer.ioClose( reader ); 318// } 319 } 320 321 /** 322 * 引数ファイル(Word,PoworPoint,Excel)を、TableModelHelper を使用してテキスト化します。 323 * 324 * ここでは、ファイル名の拡張子で、処理するメソッドを選別します。 325 * 拡張子が、対象かどうかは、#isPOI( File ) メソッドで判定できます。 326 * 327 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 328 * 表形式オブジェクトの形で処理されます。 329 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 330 * スキップされます。 331 * 332 * @og.rev 6.2.3.0 (2015/05/01) 新規作成 333 * @og.rev 6.2.5.0 (2015/06/05) xls,xlsxは、それぞれ excelReader1,excelReader2 で処理します。 334 * @og.rev 8.5.0.0 (2023/04/21) txt,csv,jsp,java,xml,css,js は、UTF-8 固定で、textReader を呼び出す。 335 * @og.rev 8.5.0.0 (2023/05/12) pdfReader1 追加 336 * @og.rev 8.5.4.0 (2023/12/01) 拡張子無しの対応 337 * 338 * @param file 入力ファイル 339 * @param conv イベント処理させるI/F 340 */ 341 public static void textReader( final File file , final TextConverter<String,String> conv ) { 342 final String sufix = FileInfo.getSUFIX( file ); // sufix は小文字変換されてくる。 343 344 if( "doc".equalsIgnoreCase( sufix ) ) { 345 wordReader1( file,conv ); 346 } 347 else if( "docx".equalsIgnoreCase( sufix ) ) { 348 wordReader2( file,conv ); 349 } 350 else if( "ppt".equalsIgnoreCase( sufix ) ) { 351 pptReader1( file,conv ); 352 } 353 else if( "pptx".equalsIgnoreCase( sufix ) ) { 354 pptReader2( file,conv ); 355 } 356 else if( "xls".equalsIgnoreCase( sufix ) ) { 357 excelReader1( file,conv ); // 6.2.5.0 (2015/06/05) 358 } 359 else if( "xlsx".equalsIgnoreCase( sufix ) || "xlsm".equalsIgnoreCase( sufix ) ) { 360 excelReader2( file,conv ); // 6.2.5.0 (2015/06/05) 361 } 362 else if( "pdf".equalsIgnoreCase( sufix ) ) { 363 pdfReader1( file,conv ); // 8.5.0.0 (2023/05/12) 364 } 365 // 8.5.0.0 (2023/04/21) txt,csv,jsp,java,xml,css,js は、UTF-8 固定で、textReader を呼び出す。 366// else if( TXT_SUFIX.contains( sufix ) ) { 367 else if( sufix != null && !sufix.isEmpty() && TXT_SUFIX.contains( sufix ) ) { // 8.5.4.0 (2023/12/01) Modify 368 try { 369 textReader( file,conv,"UTF-8" ); 370 } 371 catch( final OgRuntimeException ex ) { // ほとんどのケースでencode違い 372 conv.change( ex.getMessage() , file.getAbsolutePath() ); 373 374 textReader( file,conv,"Windows-31J" ); // Windows-31J で読み直し。 375 } 376 } 377 else { 378// final String errMsg = "拡張子は、" + POI_SUFIX + " にしてください。[" + file + "]" ; 379// throw new OgRuntimeException( errMsg ); 380 try { 381 final String filename = file.getCanonicalPath(); 382 conv.change( "テキスト化対象外です" , filename ); // text,cmnt の順 383 } 384 catch( final IOException ex ) { 385 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 386// final String errMsg = "正規のパス名取得エラー[" + file + "]" ; 387 final String errMsg = simpleErrMsg( ERR_PATH_GET,file,ex ); // "正規のパス名取得エラー" 388 throw new OgRuntimeException( errMsg,ex ); 389 } 390 } 391 } 392 393 /** 394 * 引数ファイル(Word)を、HWPFDocument を使用してテキスト化します。 395 * 396 * 拡張子(.doc)のファイルを処理します。 397 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 398 * 表形式オブジェクトの形で処理されます。 399 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 400 * スキップされます。 401 * 402 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 403 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 404 * @og.rev 6.2.4.2 (2015/05/29) 改行以外に、「。」で分割します。 405 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 406 * 407 * @param file 入力ファイル名 408 * @param conv イベント処理させるI/F 409 */ 410 private static void wordReader1( final File file , final TextConverter<String,String> conv ) { 411 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 412// InputStream fis = null; 413// try { 414// // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正 415// fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 416 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 417// try ( InputStream fis = new BufferedInputStream( new FileInputStream( file ) ) ) { // 6.2.0.0 (2015/02/27) 418 try ( InputStream fis = new BufferedInputStream( Files.newInputStream( file.toPath() ) ) ) { // 6.2.0.0 (2015/02/27) 419 final HWPFDocument doc = new HWPFDocument( fis ); 420 421 // int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 422 423 // // WordExtractor を使ったサンプル 424 // WordExtractor we = new WordExtractor( doc ); 425 // for( String txt : we.getParagraphText() ) { 426 // String text = WordExtractor.stripFields( txt ) 427 // .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" ) 428 // .replaceAll( "\\x0b" , "\n" ).trim(); 429 // helper.value( text.trim(),rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 430 // } 431 432 // Range,Paragraph を使ったサンプル 433 final Range rng = doc.getRange(); 434 for( int pno=0; pno<rng.numParagraphs(); pno++ ) { 435 final Paragraph para = rng.getParagraph(pno); 436 final String text = Range.stripFields( para.text() ) 437 .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" ) 438 .replaceAll( "\\x0b" , "\n" ).trim(); 439 // conv.change( text, String.valueOf( rowNo++ ) ); 440 if( text.length() > 0 ) { // 8.5.0.0 (2023/04/21) 存在する場合のみ抜き出す 441 conv.change( text, String.valueOf( pno ) ); // 行番号代わりの数値は飛び番となる 442 } 443 } 444 445 // Range,Paragraph,CharacterRun を使ったサンプル(変な個所で文字が分断される) 446 // final Range rng = doc.getRange(); 447 // for( int pno = 0; pno < rng.numParagraphs(); pno++ ) { 448 // final Paragraph para = rng.getParagraph(pno); 449 // for( int cno = 0; cno < para.numCharacterRuns(); cno++ ) { 450 // final CharacterRun crun = para.getCharacterRun(cno); 451 // String text = Range.stripFields( crun.text() ) 452 // .replaceAll( "\\x13[^\\x01]+\\x01\\x14" , "" ) 453 // .replaceAll( "\\x0b" , "\n" ).trim(); 454 // helper.value( text,rowNo++,0 ); // 6.2.0.0 (2015/02/27) イベント変更 455 // } 456 // } 457 } 458 catch( final IOException ex ) { 459 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 460// final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 461 final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 462 throw new OgRuntimeException( errMsg,ex ); 463 } 464// finally { 465// Closer.ioClose( fis ); 466// } 467 } 468 469 /** 470 * 引数ファイル(Word)を、XWPFDocument を使用してテキスト化します。 471 * 472 * 拡張子(.docx)のファイルを処理します。 473 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 474 * 表形式オブジェクトの形で処理されます。 475 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 476 * スキップされます。 477 * 478 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 479 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 480 * @og.rev 6.2.4.2 (2015/05/29) 改行以外に、「。」で分割します。 481 * @og.rev 8.5.0.0 (2023/04/21) XWPFWordExtractor を参考に、テキスト化を詳細化した。 482 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 483 * 484 * @param file 入力ファイル 485 * @param conv イベント処理させるI/F 486 */ 487 private static void wordReader2( final File file , final TextConverter<String,String> conv ) { 488 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 489// InputStream fis = null; 490// try { 491// // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正 492// fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 493 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 494// try ( InputStream fis = new BufferedInputStream( new FileInputStream( file ) ) ) { // 6.2.0.0 (2015/02/27) 495 try ( InputStream fis = new BufferedInputStream( Files.newInputStream( file.toPath() ) ) ) { // 6.2.0.0 (2015/02/27) 496 final XWPFDocument doc = new XWPFDocument( fis ); 497 498 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 499 500// for( final XWPFParagraph para : doc.getParagraphs() ) { // 8.5.0.0 (2023/04/21) 削除 501// final String text = para.getParagraphText().trim(); 502// conv.change( text, String.valueOf( rowNo++ ) ); 503// } 504 505 // 8.5.0.0 (2023/04/21) XWPFWordExtractor を参考に、テキスト化を詳細化した。 506 // \poi-ooxml\src\main\java\org\apache\poi\xwpf\extractor\XWPFWordExtractor.java 507 for( final IBodyElement ibody : doc.getBodyElements() ) { 508 if (ibody instanceof XWPFParagraph) { 509 appendParagraphText(conv,rowNo, (XWPFParagraph) ibody); 510 } else if (ibody instanceof XWPFTable) { 511 appendTableText(conv,rowNo, (XWPFTable) ibody); 512 } else if (ibody instanceof XWPFSDT) { 513 final String text = ((XWPFSDT) ibody).getContent().getText().trim(); 514 if( text.length() > 0 ) { 515 conv.change( text, "XWPFSDT " + rowNo ); 516 } 517 } 518 rowNo++ ; 519 } 520 } 521 catch( final ZipException ex ) { 522 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 523// final String errMsg = "ファイルがすでにオープンされています[" + file + "]" + CR + ex.getMessage() ; 524 final String errMsg = simpleErrMsg( ERR_OPEN,file,ex ); // "ファイルがすでにオープンされています" 525 throw new OgRuntimeException( errMsg,ex ); 526 } 527 catch( final IOException ex ) { 528 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 529// final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 530 final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 531 throw new OgRuntimeException( errMsg,ex ); 532 } 533// finally { 534// Closer.ioClose( fis ); 535// } 536 } 537 538 /** 539 * wordReader2 から派生した、部分処理 540 * 541 * 引数ファイル(Word)を、XWPFDocument を使用してテキスト化するにあたり 542 * XWPFParagraph のテキスト化を行います。 543 * 544 * @og.rev 8.5.0.0 (2023/04/21) XWPFWordExtractor を参考に、テキスト化を詳細化した。 545 * 546 * @param conv イベント処理させるI/F 547 * @param rowNo 検索における連番 548 * @param paragraph XWPFParagraphオブジェクト 549 */ 550 private static void appendParagraphText(final TextConverter<String,String> conv, final int rowNo, final XWPFParagraph paragraph) { 551// final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 552 553// for (final IRunElement run : paragraph.getIRuns()) { 554// if (run instanceof XWPFSDT) { 555// buf.append( ((XWPFSDT)run).getContent().getText() ); 556// } 557// else if (run instanceof XWPFRun) { 558// buf.append( ((XWPFRun)run).text() ); 559// } 560// else { 561// buf.append( String.valueOf( run ) ); 562// } 563// } 564// final String text = buf.toString().trim(); // Paragraph は、1行にまとめる 565 566 final String text = paragraph.getText().trim(); 567 if( text.length() > 0 ) { 568 conv.change( text, "Paragraph " + rowNo ); 569 } 570 571 // FootnoteText 572 final String note = paragraph.getFootnoteText().trim(); 573 if( note.length() > 0 ) { 574 conv.change( note, "Footnote " + rowNo ); 575 } 576 577 // Add comments 578 final XWPFCommentsDecorator decorator = new XWPFCommentsDecorator(paragraph, null); 579 final String cmnt = decorator.getCommentText().trim(); 580 if( cmnt.length() > 0 ) { 581 conv.change( cmnt, "Comment " + rowNo ); 582 } 583 } 584 585 /** 586 * wordReader2 から派生した、部分処理 587 * 588 * 引数ファイル(Word)を、XWPFDocument を使用してテキスト化するにあたり 589 * XWPFTable のテキスト化を行います。 590 * 591 * @og.rev 8.5.0.0 (2023/04/21) XWPFWordExtractor を参考に、テキスト化を詳細化した。 592 * 593 * @param conv イベント処理させるI/F 594 * @param rowNo 検索における連番 595 * @param table XWPFTableオブジェクト 596 */ 597 private static void appendTableText(final TextConverter<String,String> conv, final int rowNo, final XWPFTable table) { 598 int subNo = 0; 599 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 600 601 //this works recursively to pull embedded tables from tables 602 for (final XWPFTableRow row : table.getRows()) { 603 // テーブルのセルは、タブで結合する。 604 for( final XWPFTableCell cell : row.getTableCells() ) { 605 buf.append( cell.getText().trim() ).append( '\t' ); 606 } 607 608// final List<ICell> cells = row.getTableICells(); 609// // テーブルのセルは、タブで結合する。 610// for (int i = 0; i < cells.size(); i++) { 611// final ICell cell = cells.get(i); 612// if (cell instanceof XWPFTableCell) { 613// final String text = ((XWPFTableCell) cell).getTextRecursively(); 614// buf.append( text.trim() ).append( '\t' ); 615// } else if (cell instanceof XWPFSDTCell) { 616// final String text = ((XWPFSDTCell) cell).getContent().getText(); 617// buf.append( text.trim() ).append( '\t' ); 618// } 619// } 620 621 final String text = buf.toString().trim(); // 先に trim することで、空行を除く 622 if( text.length() > 0 ) { 623 conv.change( text , "TableRow " + rowNo + ":" + subNo ); 624 } 625 buf.setLength(0); // Clearの事 626 subNo++ ; 627 } 628 } 629 630 /** 631 * 引数ファイル(PoworPoint)を、HSLFSlideShow を使用してテキスト化します。 632 * 633 * 拡張子(.ppt)のファイルを処理します。 634 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 635 * 表形式オブジェクトの形で処理されます。 636 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 637 * スキップされます。 638 * 639 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 640 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 641 * @og.rev 6.4.6.0 (2016/05/27) poi-3.15 準備 642 * @og.rev 8.5.0.0 (2023/04/21) conv.change の cmnt 修正 643 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 644 * 645 * @param file 入力ファイル 646 * @param conv イベント処理させるI/F 647 */ 648 private static void pptReader1( final File file , final TextConverter<String,String> conv ) { 649 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 650// InputStream fis = null; 651// try { 652// // 6.2.0.0 (2015/02/27) TableModelHelper 変更に伴う修正 653// fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 654 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 655// try ( InputStream fis = new BufferedInputStream( new FileInputStream( file ) ) ) { // 6.2.0.0 (2015/02/27) 656 try ( InputStream fis = new BufferedInputStream( Files.newInputStream( file.toPath() ) ) ) { // 6.2.0.0 (2015/02/27) 657 658 // 6.4.6.0 (2016/05/27) poi-3.15 659 final HSLFSlideShow ss = new HSLFSlideShow( fis ); 660 final List<HSLFSlide> slides = ss.getSlides(); // 6.4.6.0 (2016/05/27) poi-3.15 661 int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 662 for( final HSLFSlide slide : slides ) { // 6.4.6.0 (2016/05/27) poi-3.15 663 int subNo = 0; // 8.5.0.0 (2023/04/21) 664 for( final List<HSLFTextParagraph> txtList : slide.getTextParagraphs() ) { // 6.4.6.0 (2016/05/27) poi-3.15 665 final String text = HSLFTextParagraph.getText( txtList ).trim(); 666 if( text.length() > 0 ) { 667// conv.change( text, String.valueOf( rowNo++ ) ); 668 conv.change( text, "Slide " + rowNo + ":" + subNo ); // 8.5.0.0 (2023/04/21) cmnt 修正 669 } 670 subNo++; // 8.5.0.0 (2023/04/21) 671 } 672 rowNo++ ; // 8.5.0.0 (2023/04/21) 673 } 674 675 // 6.4.6.0 (2016/05/27) poi-3.12 676 // final SlideShow ss = new SlideShow( new HSLFSlideShow( fis ) ); 677 // final Slide[] slides = ss.getSlides(); 678 // int rowNo = 0; // 6.2.0.0 (2015/02/27) 行番号 679 // for( int sno=0; sno<slides.length; sno++ ) { 680 // final TextRun[] textRun = slides[sno].getTextRuns(); 681 // for( int tno=0; tno<textRun.length; tno++ ) { 682 // final String text = textRun[tno].getText(); 683 // // データとして設定されているレコードのみイベントを発生させる。 684 // if( text.length() > 0 ) { 685 // conv.change( text, String.valueOf( rowNo++ ) ); 686 // } 687 // } 688 // } 689 } 690 catch( final IOException ex ) { 691 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 692// final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 693 final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 694 throw new OgRuntimeException( errMsg,ex ); 695 } 696// finally { 697// Closer.ioClose( fis ); 698// } 699 } 700 701 /** 702 * 引数ファイル(PoworPoint)を、XMLSlideShow を使用してテキスト化します。 703 * 704 * 拡張子(.pptx)のファイルを処理します。 705 * TableModelHelper によるイベント処理できますが、TEXTというカラム名を持つ 706 * 表形式オブジェクトの形で処理されます。 707 * また、内部的に、先頭に、# がある場合や、行データが存在しない場合は、 708 * スキップされます。 709 * 710 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 711 * @og.rev 6.2.0.0 (2015/02/27) POIEvent → TableModelHelper に変更 712 * @og.rev 6.2.4.2 (2015/05/29) 行単位に、取り込むようにします。 713 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] org.apache.poi.xslf.extractorのXSLFPowerPointExtractorは推奨されません (POI4.0.0) 714 * @og.rev 8.5.0.0 (2023/04/21) SlideShowExtractor を参考に、テキスト化を詳細化した。 715 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 716 * 717 * @param file 入力ファイル 718 * @param conv イベント処理させるI/F 719 */ 720 private static void pptReader2( final File file , final TextConverter<String,String> conv ) { 721 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 722// InputStream fis = null; 723// try { 724// fis = new BufferedInputStream( new FileInputStream( file ) ); // 6.2.0.0 (2015/02/27) 725 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 726// try ( InputStream fis = new BufferedInputStream( new FileInputStream( file ) ) ) { // 6.2.0.0 (2015/02/27) 727 try ( InputStream fis = new BufferedInputStream( Files.newInputStream( file.toPath() ) ) ) { // 6.2.0.0 (2015/02/27) 728 // 6.2.0.0 (2015/02/27) TableModelEvent 変更に伴う修正 729 final XMLSlideShow ss = new XMLSlideShow( fis ); 730 // final SlideShowExtractor<XSLFShape,XSLFTextParagraph> ext = new SlideShowExtractor<>( ss ); // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated. 731 // final String[] vals = ext.getText().split( "\\n" ); // 6.2.4.2 (2015/05/29) 行単位に、取り込むようにします。 732 // for( int row=0; row<vals.length; row++ ) { 733 // conv.change( vals[row], String.valueOf( row ) ); 734 // } 735 int rowNo = 0; 736 for (final XSLFSlide slide : ss.getSlides()) { 737 conv.change( slide.getSlideName(), "Slide " + rowNo ); 738 printShapeText( slide,conv,rowNo ); 739 rowNo ++ ; 740 } 741 } 742 catch( final IOException ex ) { 743 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 744// final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 745 final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 746 throw new OgRuntimeException( errMsg,ex ); 747 } 748// finally { 749// Closer.ioClose( fis ); 750// } 751 } 752 753 /** 754 * pptReader2 から派生した、部分処理 755 * 756 * 引数ファイル(PowerPoint)を、XSLFSlide を使用してテキスト化します。 757 * 758 * @og.rev 8.5.0.0 (2023/04/21) SlideShowExtractor を参考に、テキスト化を詳細化した。 759 * 760 * @param container XSLFShapeを含むShapeContainerオブジェクト 761 * @param conv イベント処理させるI/F 762 * @param rowNo 検索における連番 763 */ 764 @SuppressWarnings("unchecked") 765 private static void printShapeText(final ShapeContainer<XSLFShape,XSLFTextParagraph> container, final TextConverter<String,String> conv,final int rowNo ) { 766 int subNo = 0; 767 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 768 769 for (final Shape<XSLFShape,XSLFTextParagraph> shape : container) { 770 if (shape instanceof TextShape) { 771 buf.setLength(0); 772 printTextParagraphs(((TextShape<XSLFShape,XSLFTextParagraph>)shape).getTextParagraphs(),buf); 773 final String text = buf.toString().trim(); 774 if( text.length() > 0 ) { 775 conv.change( text, "Shape " + rowNo + ":" + subNo ); 776 } 777 subNo++ ; 778 } else if (shape instanceof TableShape) { 779 printTableShape((TableShape<XSLFShape,XSLFTextParagraph>)shape,conv,rowNo); 780 } else if (shape instanceof ShapeContainer) { 781 printShapeText((ShapeContainer<XSLFShape,XSLFTextParagraph>)shape,conv,rowNo); 782 } 783 } 784 } 785 786 /** 787 * pptReader2 から派生した、部分処理 788 * 789 * 引数ファイル(PowerPoint)を、XSLFSlide を使用してテキスト化します。 790 * 791 * @og.rev 8.5.0.0 (2023/04/21) SlideShowExtractor を参考に、テキスト化を詳細化した。 792 * 793 * @param shape XSLFShapeを含むTableShapeオブジェクト 794 * @param conv イベント処理させるI/F 795 * @param rowNo 検索における連番 796 */ 797 private static void printTableShape(final TableShape<XSLFShape,XSLFTextParagraph> shape, final TextConverter<String,String> conv,final int rowNo) { 798 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 799 800 final int nrows = shape.getNumberOfRows(); 801 final int ncols = shape.getNumberOfColumns(); 802 for (int row = 0; row < nrows; row++) { 803 for (int col = 0; col < ncols; col++){ 804 final TableCell<XSLFShape,XSLFTextParagraph> cell = shape.getCell(row, col); 805 //defensive null checks; don't know if they're necessary 806 if (cell != null) { 807 printTextParagraphs( cell.getTextParagraphs(),buf ); 808 buf.append('\t'); 809 } 810 } 811 final String text = buf.toString().trim(); 812 if( text.length() > 0 ) { 813 conv.change( text, "TableRow " + rowNo + ":" + row ); 814 } 815 buf.setLength(0); // Clearの事 816 } 817 } 818 819 /** 820 * pptReader2 から派生した、部分処理 821 * 822 * 引数ファイル(PowerPoint)を、XSLFSlide を使用してテキスト化します。 823 * 824 * @og.rev 8.5.0.0 (2023/04/21) SlideShowExtractor を参考に、テキスト化を詳細化した。 825 * 826 * @param paras XSLFTextParagraphオブジェクト のリスト 827 * @param buf 文字列バッファ 828 */ 829 private static void printTextParagraphs( final List<XSLFTextParagraph> paras ,final StringBuilder buf ) { 830 for (final XSLFTextParagraph para : paras) { 831 for (final TextRun run : para) { 832 buf.append( run.getRawText().trim() ); 833 } 834 } 835 } 836 837 /** 838 * title, file, その他文字列を連結した文字列を作成します。 839 * 840 * 用途的には、errMsg 文字列の簡易作成用です。 841 * 842 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 843 * 844 * @param title タイトル 845 * @param file 対象となるファイルオブジェクト 846 * @param ex エラーメッセージを出す用のException 847 * @param vals 連結したい文字列キーと値のペア(可変長引数で偶数) 848 * @return エラーメッセージ用に連結した文字列 849 */ 850 private static String simpleErrMsg( final String title, final File file, final Exception ex, final String... vals ) { 851 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 852 .append( title ).append( '[' ).append( file ).append( ']' ).append( CR ) 853 .append( ex.getMessage() ).append( CR ) ; 854 855 // キーと値が交互に現れる。偶数の場合は、キーだけ表示して終了 856 boolean isKey = true; 857 for( final String val : vals ) { 858 if( isKey ) { 859 buf.append( ", " ).append( val ); 860 } 861 else { 862 buf.append( "=[" ).append( val ).append( ']' ); 863 } 864 isKey = !isKey; // 一回ずつ反転する。 865 } 866 867 return buf.toString(); 868 } 869 870 /** 871 * 引数ファイル(Excel)を、テキスト化します。 872 * 873 * TableModelHelper を与えることで、EXCELデータをテキスト化できます。 874 * ここでは、HSSF(.xls)形式を処理します。 875 * シート名、セル、テキストオブジェクトをテキスト化します。 876 * 877 * @og.rev 6.2.5.0 (2015/06/05) 新規作成 878 * 879 * @param file 入力ファイル 880 * @param conv イベント処理させるI/F 881 * @see org.opengion.fukurou.model.ExcelModel 882 */ 883 public static void excelReader1( final File file , final TextConverter<String,String> conv ) { 884 excelReader2( file , conv ); 885 } 886 887 /** 888 * 引数ファイル(Excel)を、テキスト化します。 889 * 890 * TableModelHelper を与えることで、EXCELデータをテキスト化できます。 891 * ここでは、ExcelModelを使用して、(.xlsx , .xlsm)形式を処理します。 892 * シート名、セル、テキストオブジェクトをテキスト化します。 893 * 894 * @og.rev 6.2.5.0 (2015/06/05) 新規作成 895 * @og.rev 6.3.1.0 (2015/06/28) TextConverterに、引数(cmnt)を追加 896 * @og.rev 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 897 * 898 * @param file 入力ファイル 899 * @param conv イベント処理させるI/F 900 * @see org.opengion.fukurou.model.ExcelModel 901 */ 902 public static void excelReader2( final File file , final TextConverter<String,String> conv ) { 903 final ExcelModel excel = new ExcelModel( file, true ); 904 905 // 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 906 // textConverter を使いますが、テキストを読み込むだけで、変換しません。 907 excel.textConverter( 908 ( val,cmnt ) -> { 909 conv.change( val,cmnt ); // 変換したくないので、引数の TextConverter を直接渡せない。 910 return null; // nullを返せば、変換しません。 911 } 912 ); 913 } 914 915 /** 916 * 引数ファイル(PDF)を、テキスト化します。 917 * 918 * 引数ファイル(PDF)を、pdfbox を使用してテキスト化します。 919 * 920 * @og.rev 8.5.0.0 (2023/05/12) pdfReader1 追加 921 * @og.rev 8.5.4.2 (2024/01/12) PDFのページを直接開けるように、cmnt を変更する。 922 * 923 * @param file 入力ファイル 924 * @param conv イベント処理させるI/F 925 */ 926 public static void pdfReader1( final File file , final TextConverter<String,String> conv ) { 927// Logger.getLogger("org.apache.fontbox").setLevel(Level.OFF); // 警告抑止 … 警告が止められない 928 929 // pdfbox-3.0.1.jar 930 // pdfbox-io-3.0.1.jar 931 // fontbox-3.0.1.jar 932 try( PDDocument document = Loader.loadPDF( new RandomAccessReadBufferedFile( file ) )) { 933 final int psize = document.getNumberOfPages(); // 総ページ数 934 final PDFTextStripper stripper = new PDFTextStripper(); 935 final String sep = stripper.getLineSeparator(); 936 for( int pno=1; pno<=psize; pno++ ) { 937 stripper.setStartPage(pno); 938 stripper.setEndPage(pno); 939 int lno = 1; 940 final String text = stripper.getText(document); 941 for( final String line : text.split( sep ) ) { // PDFTextStripper を使うと、テキスト行のみ抜き出すので 942 final String cmnt = "page=" + pno + " : " + lno ; // 8.5.4.2 (2024/01/12) 943 conv.change( line,cmnt ); 944 lno++ ; 945 } 946 } 947 } 948 catch( final IOException ex ) { 949 final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 950 throw new OgRuntimeException( errMsg,ex ); 951 } 952 953// // pdfbox-2.0.29.jar 954// // fontbox-2.0.29.jar 955// try( PDDocument document = PDDocument.load(file) ) { 956// final int psize = document.getNumberOfPages(); // 総ページ数 957// final PDFTextStripper stripper = new PDFTextStripper(); 958// final String sep = stripper.getLineSeparator(); 959// for( int pno=1; pno<=psize; pno++ ) { 960// stripper.setStartPage(pno); 961// stripper.setEndPage(pno); 962// int lno = 1; 963// final String text = stripper.getText(document); 964// for( final String line : text.split( sep ) ) { // PDFTextStripper を使うと、テキスト行のみ抜き出すので 965// // if( line.length() > 0 ) { // 行番号は取れない…感じ。 966// // final String cmnt = "Page" + pno + " : " + lno ; 967// final String cmnt = "page=" + pno + " : " + lno ; // 8.5.4.2 (2024/01/12) 968// conv.change( line,cmnt ); 969// // } 970// lno++ ; 971// } 972// } 973// } 974// catch( final IOException ex ) { 975// // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 976//// final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 977// final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 978// throw new OgRuntimeException( errMsg,ex ); 979// } 980 } 981 982 /** 983 * Excelの行列記号を、行番号と列番号に分解します。 984 * 985 * Excelの行列記号とは、A1 , B5 , AA23 などの形式を指します。 986 * これを、行番号と列番号に分解します。例えば、A1→0行0列、B5→4行1列、AA23→22行26列 となります。 987 * 分解した結果は、内部変数の、rowNo と colNo にセットされます。 988 * これらは、0 から始まる int型の数字で表します。 989 * 990 * ①行-列形式 991 * 行列は、EXCELオブジェクトに準拠するため、0から始まる整数です。 992 * 0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。 993 * ②EXCEL表記 994 * EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。 995 * なお、A1,A2,B1 の記述は、必ず、英字1文字+数字 にしてください。(A~Zまで) 996 * ③EXCELシート名をキーに割り当てるために、"SHEET" という記号に対応します。 997 * rowNo = -1 をセットします。 998 * 999 * @og.rev 6.0.3.0 (2014/11/13) 新規作成 1000 * @og.rev 6.2.6.0 (2015/06/19) 行-列形式と、SHEET文字列判定を採用。 1001 * 1002 * @param kigo Excelの行列記号( A1 , B5 , AA23 など ) 1003 * @return 行と列の番号を持った配列([0]=行=ROW , [1]=列=COL) 1004 * @og.rtnNotNull 1005 */ 1006 public static int[] kigo2rowCol( final String kigo ) { 1007 int rowNo = 0; 1008 int colNo = -1; // +1 して、26 かける処理をしているので、辻褄合わせ 1009 1010 // 6.2.6.0 (2015/06/19) 行-列形式と、SHEET文字列判定を採用。 1011 if( "SHEET".equalsIgnoreCase( kigo ) ) { 1012 rowNo = -1; 1013 } 1014 else { 1015 final int adrs = kigo.indexOf( '-' ); 1016 if( adrs > 0 ) { 1017 rowNo = Integer.parseInt( kigo.substring( 0,adrs ) ); 1018 colNo = Integer.parseInt( kigo.substring( adrs+1 ) ); 1019 } 1020 else { 1021 for( int i=0; i<kigo.length(); i++ ) { 1022 final char ch = kigo.charAt(i); 1023 if( 'A' <= ch && ch <= 'Z' ) { colNo = (colNo+1)*26 + ch-'A'; } 1024 else { 1025 // アルファベットでなくなったら、残りは 行番号(ただし、-1する) 1026 rowNo = Integer.parseInt( kigo.substring( i ) ) -1; 1027 break; 1028 } 1029 } 1030 } 1031 } 1032 return new int[] { rowNo,colNo }; 1033 } 1034 1035 /** 1036 * セルオブジェクト(Cell)から値を取り出します。 1037 * 1038 * セルオブジェクトが存在しない場合は、null を返します。 1039 * それ以外で、うまく値を取得できなかった場合は、ゼロ文字列を返します。 1040 * 1041 * @og.rev 3.8.5.3 (2006/08/07) 取り出し方法を少し修正 1042 * @og.rev 5.5.1.2 (2012/04/06) フォーマットセルを実行して、その結果を再帰的に処理する。 1043 * @og.rev 6.0.3.0 (2014/11/13) セルフォーマットエラー時に、RuntimeException を throw しない。 1044 * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil#ogStackTrace(String,Throwable) に置き換え。 1045 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX) 1046 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0) 1047 * @og.rev 7.3.0.0 (2021/01/06) フォーマットエラー時に、エラーメッセージ取得でもエラーになる。 1048 * @og.rev 8.0.0.0 (2021/07/31) FORMULA処理のエラー対応(出来るだけ…) 1049 * @og.rev 8.1.2.3 (2022/05/20) 計算式の計算を行う。 1050 * @og.rev 8.5.0.0 (2023/04/21) セルフォーマット処理エラー時には、スタックトレースは出さずに計算式を返す。 1051 * @og.rev 8.5.5.1 (2024/02/29) switch式の使用 1052 * 1053 * @param oCell EXCELのセルオブジェクト 1054 * @return セルの値 1055 */ 1056 public static String getValue( final Cell oCell ) { 1057 // 8.5.5.1 (2024/02/29) switch式の使用 1058 if( oCell == null ) { return null; } 1059// String strText = ""; 1060 // final int nCellType = oCell.getCellType(); // 6.5.0.0 (2016/09/30) poi-3.12 1061 // switch(nCellType) { // 6.5.0.0 (2016/09/30) poi-3.12 1062// switch( oCell.getCellTypeEnum() ) { // 6.5.0.0 (2016/09/30) poi-3.15 1063 // 8.5.5.1 (2024/02/29) switch式の使用 (長いので、直接修正します) 1064 return switch( oCell.getCellType() ) { // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated. 1065 case NUMERIC -> getNumericTypeString( oCell ); // 6.5.0.0 (2016/09/30) poi-3.15 1066 case STRING -> { // 6.5.0.0 (2016/09/30) poi-3.15 1067 // POI3.0 strText = oCell.getStringCellValue(); 1068 final RichTextString richText = oCell.getRichStringCellValue(); 1069// if( richText != null ) { 1070// strText = richText.getString(); 1071// } 1072 yield richText == null ? "" : richText.getString(); // 8.5.5.1 (2024/02/29) 1073 } 1074 case FORMULA -> { // 6.5.0.0 (2016/09/30) poi-3.15 1075 // POI3.0 strText = oCell.getStringCellValue(); 1076 // 5.5.1.2 (2012/04/06) フォーマットセルを実行して、その結果を再帰的に処理する。 1077 // final Workbook wb = oCell.getSheet().getWorkbook(); 1078 // final CreationHelper crateHelper = wb.getCreationHelper(); 1079 // final FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator(); 1080 1081 String strText = ""; 1082 try { 1083 // 8.1.2.3 (2022/05/20) 計算式の計算を行う。 1084 final Workbook wb = oCell.getSheet().getWorkbook(); 1085 final CreationHelper crateHelper = wb.getCreationHelper(); 1086 final FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator(); 1087 final CellValue value = evaluator.evaluate(oCell); 1088 1089 // 8.5.5.1 (2024/02/29) switch式の使用 1090// switch (value.getCellType()) { 1091// case STRING: 1092// strText = value.getStringValue(); 1093// break; 1094// case NUMERIC: 1095// strText = Double.toString(value.getNumberValue()); 1096// break; 1097// case BOOLEAN: 1098// strText = Boolean.toString(value.getBooleanValue()); 1099// break; 1100// default: 1101// strText = oCell.getCellFormula(); // 計算式のまま 1102// break; 1103// } 1104 strText = switch(value.getCellType()) { 1105 case STRING -> value.getStringValue(); 1106 case NUMERIC -> Double.toString(value.getNumberValue()); 1107 case BOOLEAN -> Boolean.toString(value.getBooleanValue()); 1108 default -> oCell.getCellFormula(); // 計算式のまま 1109 }; 1110 1111 // strText = oCell.getCellFormula(); // 8.1.2.3 (2022/05/20) 計算式は返さない 1112 // 8.0.0.0 (2021/07/31) FORMULA処理のエラー対応(出来るだけ…) 1113 // final Cell fCell = evaluator.evaluateInCell(oCell); 1114 // strText = getValue( fCell ); 1115 } 1116 catch( final Throwable th ) { 1117 // 7.3.0.0 (2021/01/06) フォーマットエラー時に、エラーメッセージ取得でもエラーになる。 1118 // final String errMsg = "セルフォーマットが解析できません。"; 1119 // 8.5.0.0 (2023/04/21) セルフォーマット処理エラー時には、スタックトレースは出さずに計算式を返す。 1120 final String errMsg = "セルフォーマットが解析できません。" 1121 + CR + " Formula=[" + oCell.getCellFormula() + "]" 1122 + CR + getCellMsg( oCell ); 1123 // throw new OgRuntimeException( errMsg,th ); 1124 // System.err.println( ThrowUtil.ogStackTrace( errMsg,th ) ); // 6.4.2.0 (2016/01/29) , 8.5.0.0 (2023/04/21) Delete 1125 System.err.println( errMsg ); // 8.5.0.0 (2023/04/21) Add 1126 strText = oCell.toString(); // 8.5.0.0 (2023/04/21) Add 1127 } 1128 yield strText; 1129 } 1130 case BOOLEAN -> String.valueOf(oCell.getBooleanCellValue()); // 6.5.0.0 (2016/09/30) poi-3.15 1131 case BLANK -> ""; // 6.5.0.0 (2016/09/30) poi-3.15 1132 case ERROR -> ""; // 6.5.0.0 (2016/09/30) poi-3.15 1133 default -> { 1134 final String errMsg = "セルタイプが不明です。CellType=" + oCell.getCellType() 1135 + CR + getCellMsg( oCell ); // 8.5.0.0 (2023/04/21) Add 1136 System.err.println( errMsg ); // 8.5.0.0 (2023/04/21) Add 1137 yield oCell.toString(); // 8.5.0.0 (2023/04/21) Add 1138 } 1139 }; 1140// return strText ; 1141 } 1142 1143 /** 1144 * セルオブジェクト(Cell)に、値をセットします。 1145 * 1146 * セルオブジェクトが存在しない場合は、何もしません。 1147 * 引数は、文字列で渡しますが、セルの形式に合わせて、変換します。 1148 * 変換がうまくいかなかった場合は、エラーになりますので、ご注意ください。 1149 * 1150 * @og.rev 6.3.9.0 (2015/11/06) 新規追加 1151 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX) 1152 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0) 1153 * @og.rev 7.3.0.0 (2021/01/06) setCellType( CellType.BLANK )(Deprecated) → setBlank() (poi-4.1.2) 1154 * @og.rev 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 1155 * 1156 * @param oCell EXCELのセルオブジェクト 1157 * @param val セットする値 1158 */ 1159 public static void setValue( final Cell oCell , final String val ) { 1160 if( oCell == null ) { return ; } 1161 // if( val == null || val.isEmpty() ) { oCell.setCellType( Cell.CELL_TYPE_BLANK ); } // 6.5.0.0 (2016/09/30) poi-3.12 1162 // if( val == null || val.isEmpty() ) { oCell.setCellType( CellType.BLANK ); } // 6.5.0.0 (2016/09/30) poi-3.15 1163 if( val == null || val.isEmpty() ) { oCell.setBlank(); } // 7.3.0.0 (2021/01/06) poi-4.1.2 1164 1165 // 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 1166// // switch( oCell.getCellType() ) { // 6.5.0.0 (2016/09/30) poi-3.12 1167//// switch( oCell.getCellTypeEnum() ) { // 6.5.0.0 (2016/09/30) poi-3.15 1168// switch( oCell.getCellType() ) { // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated. 1169// // case Cell.CELL_TYPE_NUMERIC: // 6.5.0.0 (2016/09/30) poi-3.12 1170// case NUMERIC: // 6.5.0.0 (2016/09/30) poi-3.15 1171//// oCell.setCellValue( Double.valueOf( val ) ); 1172// oCell.setCellValue( Double.parseDouble( val ) ); // 7.3.0.0 (2021/01/06) SpotBugs 疑わしいプリミティブ値のボクシング 1173// break; 1174// // case Cell.CELL_TYPE_BOOLEAN: // 6.5.0.0 (2016/09/30) poi-3.12 1175// case BOOLEAN: // 6.5.0.0 (2016/09/30) poi-3.15 1176// oCell.setCellValue( "true".equalsIgnoreCase( val ) ); 1177// break; 1178// default : 1179// oCell.setCellValue( val ); 1180// break; 1181// } 1182 switch( oCell.getCellType() ) { // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated. 1183 // 7.3.0.0 (2021/01/06) SpotBugs 疑わしいプリミティブ値のボクシング 1184 case NUMERIC -> oCell.setCellValue( Double.parseDouble( val ) ); // 6.5.0.0 (2016/09/30) poi-3.15 1185 case BOOLEAN -> oCell.setCellValue( "true".equalsIgnoreCase( val ) ); // 6.5.0.0 (2016/09/30) poi-3.15 1186 default -> oCell.setCellValue( val ); 1187 } 1188 } 1189 1190 /** 1191 * セル値が数字の場合に、数字か日付かを判断して、対応する文字列を返します。 1192 * 1193 * @og.rev 3.8.5.3 (2006/08/07) 新規追加 1194 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。 1195 * @og.rev 6.3.1.0 (2015/06/28) ExcelStyleFormat を使用します。 1196 * 1197 * @param oCell EXCELのセルオブジェクト 1198 * @return 数字の場合は、文字列に変換した結果を、日付の場合は、"yyyyMMddHHmmss" 形式で返します。 1199 */ 1200 public static String getNumericTypeString( final Cell oCell ) { 1201 final String strText ; 1202 1203 final double dd = oCell.getNumericCellValue() ; 1204 if( DateUtil.isCellDateFormatted( oCell ) ) { 1205 // strText = DateSet.getDate( DateUtil.getJavaDate( dd ).getTime() , "yyyyMMddHHmmss" ); // 5.5.7.2 (2012/10/09) HybsDateUtil を利用 1206 strText = ExcelStyleFormat.dateFormat( dd ); 1207 } 1208 else { 1209 // final NumberFormat numFormat = NumberFormat.getInstance(); 1210 // if( numFormat instanceof DecimalFormat ) { 1211 // ((DecimalFormat)numFormat).applyPattern( "#.####" ); 1212 // } 1213 // strText = numFormat.format( dd ); 1214 final String fmrs = oCell.getCellStyle().getDataFormatString(); 1215 strText = ExcelStyleFormat.getNumberValue( fmrs,dd ); 1216 } 1217 return strText ; 1218 } 1219 1220 /** 1221 * 全てのSheetに対して、autoSizeColumn設定を行います。 1222 * 1223 * 重たい処理なので、ファイルの書き出し直前に一度だけ実行するのがよいでしょう。 1224 * autoSize設定で、カラム幅が大きすぎる場合、現状では、 1225 * 初期カラム幅のmaxColCount倍を限度に設定します。 1226 * ただし、maxColCount がマイナスの場合は、無制限になります。 1227 * 1228 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1229 * @og.rev 6.8.2.4 (2017/11/20) rowObj のnull対策(poi-3.17) 1230 * 1231 * @param wkbook 処理対象のWorkbook 1232 * @param maxColCount 最大幅を標準セル幅の何倍にするかを指定。マイナスの場合は、無制限 1233 * @param dataStRow データ行の開始位置。未設定時は、-1 1234 */ 1235 public static void autoCellSize( final Workbook wkbook , final int maxColCount , final int dataStRow ) { 1236 final int shCnt = wkbook.getNumberOfSheets(); 1237 1238 for( int shNo=0; shNo<shCnt; shNo++ ) { 1239 final Sheet sht = wkbook.getSheetAt( shNo ); 1240 final int defW = sht.getDefaultColumnWidth(); // 標準カラムの文字数 1241 final int maxWidth = defW*256*maxColCount ; // Widthは、文字数(文字幅)*256*最大セル数 1242 1243 int stR = sht.getFirstRowNum(); 1244 final int edR = sht.getLastRowNum(); 1245 1246 // 6.8.2.4 (2017/11/20) rowObj のnull対策(poi-3.17) 1247 // poi-3.15 でも同じ現象が出ていますが、sht.getRow( stR ) で、Rowオブジェクトにnullが返ってきます。 1248 // なんとなく、最後の行だけ、返ってきている感じです。 1249 // 頻繁には使わないと思いますので、最大カラム数=256 として処理します。 1250 1251 final Row rowObj = sht.getRow( stR ); 1252 // Row rowObj = sht.getRow( stR ); 1253 // if( rowObj == null ) { 1254 // for( int i=stR+1; i<edR; i++ ) { 1255 // rowObj = sht.getRow( i ); 1256 // if( rowObj != null ) { break; } 1257 // } 1258 // } 1259 1260 final int stC = rowObj == null ? 0 : rowObj.getFirstCellNum(); // 6.8.2.4 (2017/11/20) rowObj のnull対策 1261 final int edC = rowObj == null ? 256 : rowObj.getLastCellNum(); // 含まない (xlsxでは、最大 16,384 列 1262 1263 // SheetUtil を使用して、計算範囲を指定します。 1264 if( stR < dataStRow ) { stR = dataStRow; } // 計算範囲 1265 for( int colNo=stC; colNo<edC; colNo++ ) { 1266 final double wpx = SheetUtil.getColumnWidth( sht,colNo,true,stR,edR ); 1267 if( wpx >= 0.0 ) { // Cellがないと、マイナス値が戻る。 1268 int wd = (int)Math.ceil(wpx * 256) ; 1269 if( maxWidth >= 0 && wd > maxWidth ) { wd = maxWidth; } // 最大値が有効な場合は、置き換える 1270 sht.setColumnWidth( colNo,wd ); 1271 } 1272 } 1273 1274 // Sheet#autoSizeColumn(int) を使用して、自動計算させる場合。 1275 // for( int colNo=stC; colNo<edC; colNo++ ) { 1276 // sht.autoSizeColumn( colNo ); 1277 // if( maxWidth >= 0 ) { // 最大値が有効な場合は、置き換える 1278 // int wd = sht.getColumnWidth( colNo ); 1279 // if( wd > maxWidth ) { sht.setColumnWidth( colNo,maxWidth ); } 1280 // } 1281 // } 1282 } 1283 } 1284 1285// /** 1286// * 指定の Workbook の全Sheetを対象に、実際の有効行と有効カラムを取得します。 1287// * 1288// * ※ 現在、唯一LibreOfficeでのみ、xslx 変換できますが、有効行とカラムが 1289// * シュリンクされず、無駄な行とカラムが存在します。 1290// * これは、xsl で出力されたファイルから有効な値を取得して、xslxに適用させるための 1291// * 機能で、本来きちんとした有効範囲の xslx が生成されれば、不要な処理です。 1292// * 1293// * 配列は、[0]=行の最大値(Sheet#getLastRowNum())と、[1]は有効行の中の列の 1294// * 最大値(Row#getLastCellNum())を、シートごとにListに追加していきます。 1295// * 1296// * @og.rev 8.0.1.0 (2021/10/29) 全Sheetを対象に、実際の有効行と有効カラムを取得 1297// * @og.rev 8.0.3.0 (2021/12/17) 処理が中途半端だったので、廃止します。 1298// * 1299// * @param wkbook 処理対象のWorkbook 1300// * @return シートごとの有効行の配列リスト 1301// * @see #activeWorkbook( Workbook,List ) 1302// */ 1303// public static List<int[]> getLastRowCellNum( final Workbook wkbook ) { 1304// final List<int[]> rcList = new ArrayList<>(); // シートごとの有効行の配列リスト 1305// 1306// final int shCnt = wkbook.getNumberOfSheets(); 1307// for( int shNo=0; shNo<shCnt; shNo++ ) { 1308// final Sheet sht = wkbook.getSheetAt( shNo ); 1309// final int stR = sht.getFirstRowNum(); 1310// final int edR = sht.getLastRowNum(); 1311// int lastNo = 0; // 行の有効最大値 1312// for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 1313// final Row rowObj = sht.getRow( rowNo ); 1314// if( rowObj != null ) { 1315// final int edC = rowObj.getLastCellNum(); // 列の有効最大値 1316// if( lastNo < edC ) { lastNo = edC; } // シート内での列の最大有効値 1317// } 1318// } 1319// rcList.add( new int[] {edR,lastNo} ); // 有効行の配列 1320// } 1321// return rcList; 1322// } 1323 1324 /** 1325 * 指定の Workbook の全Sheetを対象に、空行を取り除き、全体をシュリンクします。 1326 * 1327 * この処理は、#saveFile( String ) の直前に行うのがよいでしょう。 1328 * 1329 * ここでは、Row を逆順にスキャンし、Cellが 存在しない間は、行を削除します。 1330 * 途中の空行の削除ではなく、最終行からの連続した空行の削除です。 1331 * 1332 * isCellDel=true を指定すると、Cellの末尾削除を行います。 1333 * 有効行の最後のCellから空セルを削除していきます。 1334 * 表形式などの場合は、Cellのあるなしで、レイアウトが崩れる場合がありますので 1335 * 処理が不要な場合は、isCellDel=false を指定してください。 1336 * 1337 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1338 * @og.rev 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます。 1339 * @og.rev 6.0.2.5 (2014/10/31) Cellの開始、終了番号が、マイナスのケースの対応 1340 * @og.rev 6.5.0.0 (2016/09/30) poi-3.15 対応(Cell.CELL_TYPE_XXXX → CellType.XXXX) 1341 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] CellのgetCellTypeEnum()は推奨されません (POI4.0.0) 1342 * @og.rev 8.0.1.0 (2021/10/29) CellStyle は not null になったための修正 1343 * 1344 * @param wkbook 処理対象のWorkbook 1345 * @param isCellDel Cellの末尾削除を行うかどうか(true:行う/false:行わない) 1346 * @see #activeWorkbook( Workbook,List ) 1347 */ 1348 public static void activeWorkbook( final Workbook wkbook , final boolean isCellDel ) { 1349 final int shCnt = wkbook.getNumberOfSheets(); 1350 for( int shNo=0; shNo<shCnt; shNo++ ) { 1351 final Sheet sht = wkbook.getSheetAt( shNo ); 1352 final int stR = sht.getFirstRowNum(); 1353 final int edR = sht.getLastRowNum(); 1354 1355 boolean isRowDel = true; // 行の削除は、Cellが見つかるまで。 1356 for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 1357 final Row rowObj = sht.getRow( rowNo ); 1358 if( rowObj != null ) { 1359 final int stC = rowObj.getFirstCellNum(); 1360 final int edC = rowObj.getLastCellNum(); 1361 // 8.0.1.0 (2021/10/29) 各行の最初のセルスタイルをベースとして比較する。 1362 1363 if( stC >= 0 && edC >= 0 ) { // 8.0.3.0 (2021/12/17) 存在しない場合もある。 1364 final CellStyle endCellStyle = rowObj.getCell( stC ).getCellStyle(); // nullチェック入れてない… 1365 for( int colNo=edC; colNo>=stC && colNo>=0; colNo-- ) { // 6.0.2.5 (2014/10/31) stC,edC が、マイナスのケースがある。 1366 final Cell colObj = rowObj.getCell( colNo ); 1367 if( colObj != null ) { 1368 final String val = getValue( colObj ); 1369 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 1370// // 7.0.0.0 (2018/10/01) poi-4.0.0 Deprecated. 1371// if( colObj.getCellType() != CellType.BLANK && val != null && val.length() > 0 ) { 1372// isRowDel = false; // 一つでも現れれば、行の削除は中止 1373// break; 1374// } 1375// // 6.0.2.3 (2014/10/10) CellStyle の有無も判定基準に含めます。 1376// // 8.0.1.0 (2021/10/29) 各行の最初のセルスタイルをベースとして比較する。 1377// // else if( colObj.getCellStyle() != null ) { 1378// else if( ! endCellStyle.equals(colObj.getCellStyle()) ) { 1379// isRowDel = false; // 一つでも現れれば、行の削除は中止 1380// break; 1381// } 1382 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 … まとめます。 1383 if( colObj.getCellType() != CellType.BLANK && val != null && val.length() > 0 1384 || ! endCellStyle.equals(colObj.getCellStyle()) ) { 1385 isRowDel = false; // 一つでも現れれば、行の削除は中止 1386 break; 1387 } 1388 else if( isCellDel ) { 1389 rowObj.removeCell( colObj ); // CELL_TYPE_BLANK の場合は、削除 1390 } 1391 } 1392 } 1393 } 1394 if( isRowDel ) { sht.removeRow( rowObj ); } 1395 else if( !isCellDel ) { break; } // Cell の末尾削除を行わない場合は、break すればよい。 1396 } 1397 } 1398 } 1399 } 1400 1401 /** 1402 * 指定の Workbook の全Sheetを対象に、実際の有効行と有効カラムを元に全体をシュリンクします。 1403 * 1404 * ※ 現在、唯一LibreOfficeでのみ、xslx 変換できますが、有効行とカラムが 1405 * シュリンクされず、無駄な行とカラムが存在します。 1406 * これは、xsl で出力されたファイルから有効な値を取得して、xslxに適用させるための 1407 * 機能で、本来きちんとした有効範囲の xslx が生成されれば、不要な処理です。 1408 * 1409 * 引数のListオブジェクトに従って、無条件に処理を行います。 1410 * 1411 * @og.rev 8.0.1.0 (2021/10/29) 全Sheetを対象に、実際の有効行と有効カラムを取得 1412 * @og.rev 8.0.3.0 (2021/12/17) シート毎の行数Listに変更。 1413 * 1414 * @param wkbook 処理対象のWorkbook 1415// * @param rcList シートごとの有効行の配列リスト 1416 * @param rowCntList シートごとの有効行の配列リスト 1417// * @see #getLastRowCellNum( Workbook ) 1418 * @see #activeWorkbook( Workbook,boolean ) 1419 */ 1420// public static void activeWorkbook( final Workbook wkbook , final List<int[]> rcList ) { 1421 public static void activeWorkbook( final Workbook wkbook , final List<Integer> rowCntList ) { 1422 final int shCnt = wkbook.getNumberOfSheets(); 1423 for( int shNo=0; shNo<shCnt; shNo++ ) { 1424 final Sheet sht = wkbook.getSheetAt( shNo ); 1425// final int[] rowcol = rcList.get(shNo); // シート内の有効行と列 1426 final int stR = rowCntList.get(shNo); // シート内の有効行と列 1427 1428// final int stR = rowcol[0]; 1429 final int edR = sht.getLastRowNum(); // 参考程度 1430 // edR~stRまでの行は、無条件に削除します。 1431 for( int rowNo=edR; rowNo>=stR && rowNo>=0; rowNo-- ) { // 逆順に処理します。 1432 final Row rowObj = sht.getRow( rowNo ); 1433 if( rowObj != null ) { 1434 sht.removeRow( rowObj ); 1435 } 1436 } 1437 1438 // カラム列の削除は保留 1439 // // stR~0までの行は、有効行なので、カラムの処理を考えます。 1440// // final int stC = rowcol[1]; // シートの中での有効カラムの最大値 1441 // final int stC = 0; 1442 // for( int rowNo=stR; rowNo>=0; rowNo-- ) { // 逆順に処理します。 1443 // final Row rowObj = sht.getRow( rowNo ); 1444 // if( rowObj != null ) { 1445 // final int edC = rowObj.getLastCellNum(); // 参考程度 1446 // // edC~stCまでのカラムは、無条件に削除します。 1447 // for( int colNo=edC; colNo>=stC && colNo>=0; colNo-- ) { // 6.0.2.5 (2014/10/31) stC,edC が、マイナスのケースがある。 1448 // final Cell colObj = rowObj.getCell( colNo ); 1449 // if( colObj != null ) { 1450 // if( colObj.getCellType() == CellType.BLANK ) { 1451 // rowObj.removeCell( colObj ); 1452 // } 1453 // else { 1454 // break; 1455 // } 1456 // } 1457 // } 1458 // } 1459 // } 1460 } 1461 } 1462 1463 /** 1464 * ファイルから、Workbookオブジェクトを新規に作成します。 1465 * 1466 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 1467 * @og.rev 6.2.0.0 (2015/02/27) ファイル引数を、String → File に変更 1468 * @og.rev 7.0.0.0 (2018/10/01) poi-4.0.0 例外InvalidFormatExceptionは対応するtry文の本体ではスローされません 1469 * 1470 * @param file 入力ファイル 1471 * @return Workbookオブジェクト 1472 * @og.rtnNotNull 1473 */ 1474 public static Workbook createWorkbook( final File file ) { 1475 InputStream fis = null; 1476 try { 1477 // File オブジェクトでcreate すると、ファイルがオープンされたままになってしまう。 1478 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 1479// fis = new BufferedInputStream( new FileInputStream( file ) ); 1480 fis = new BufferedInputStream( Files.newInputStream(file.toPath()) ); 1481 return WorkbookFactory.create( fis ); 1482 } 1483 catch( final IOException ex ) { 1484 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 1485// final String errMsg = "ファイル読込みエラー[" + file + "]" + CR + ex.getMessage() ; 1486 final String errMsg = simpleErrMsg( ERR_READ,file,ex ); // "ファイル読込みエラー" 1487 throw new OgRuntimeException( errMsg,ex ); 1488 } 1489 // 7.0.0.0 (2018/10/01) poi-4.0.0 対応するtry文の本体ではスローされません 1490// catch( final InvalidFormatException ex ) { 1491// final String errMsg = "ファイル形式エラー[" + file + "]" + CR + ex.getMessage() ; 1492// throw new OgRuntimeException( errMsg,ex ); 1493// } 1494 finally { 1495 Closer.ioClose( fis ); 1496 } 1497 } 1498 1499 /** 1500 * シート一覧を、Workbook から取得します。 1501 * 1502 * 取得元が、Workbook なので、xls , xlsx どちらの形式でも取り出せます。 1503 * 1504 * EXCEL上のシート名を、配列で返します。 1505 * 1506 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 1507 * 1508 * @param wkbook Workbookオブジェクト 1509 * @return シート名の配列 1510 */ 1511 public static String[] getSheetNames( final Workbook wkbook ) { 1512 final int shCnt = wkbook.getNumberOfSheets(); 1513 1514 final String[] shtNms = new String[shCnt]; // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 1515 1516 for( int i=0; i<shCnt; i++ ) { 1517 final Sheet sht = wkbook.getSheetAt( i ); 1518 shtNms[i] = sht.getSheetName(); 1519 } 1520 1521 return shtNms; 1522 } 1523 1524 /** 1525 * 名前定義一覧を取得します。 1526 * 1527 * EXCEL上に定義された名前を、配列で返します。 1528 * ここでは、名前とFormulaをタブで連結した文字列を配列で返します。 1529 * Name オブジェクトを削除すると、EXCELが開かなくなったりするので、 1530 * 取りあえず一覧を作成して、手動で削除してください。 1531 * なお、名前定義には、非表示というのがありますので、ご注意ください。 1532 * 1533 * ◆ 非表示になっている名前の定義を表示にする EXCEL VBA マクロ 1534 * http://dev.classmethod.jp/tool/excel-delete-name/ 1535 * Sub VisibleNames() 1536 * Dim name 1537 * For Each name In ActiveWorkbook.Names 1538 * If name.Visible = False Then 1539 * name.Visible = True 1540 * End If 1541 * Next 1542 * MsgBox "すべての名前の定義を表示しました。", vbOKOnly 1543 * End Sub 1544 * 1545 * ※ EXCEL2010 数式タブ→名前の管理 で、複数選択で、削除できます。 1546 * ただし、非表示の名前定義は、先に表示しないと、削除できません。 1547 * ◆ 名前の一括削除 EXCEL VBA マクロ 1548 * http://komitsudo.blog70.fc2.com/blog-entry-104.html 1549 * Sub DeleteNames() 1550 * Dim name 1551 * On Error Resume Next 1552 * For Each name In ActiveWorkbook.Names 1553 * If Not name.BuiltIn Then 1554 * name.Delete 1555 * End If 1556 * Next 1557 * MsgBox "すべての名前の定義を削除しました。", vbOKOnly 1558 * End Sub 1559 * 1560 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 1561 * @og.rev 7.0.0.0 (2018/10/01) 警告:[deprecation] WorkbookのgetNameAt(int)は推奨されません (POI4.0.0) 1562 * 1563 * @param wkbook Workbookオブジェクト 1564 * @return 名前定義(名前+TAB+Formula)の配列 1565 * @og.rtnNotNull 1566 */ 1567 public static String[] getNames( final Workbook wkbook ) { 1568// final int cnt = wkbook.getNumberOfNames(); 1569 1570 final Set<String> nmSet = new TreeSet<>(); 1571 1572 // 7.0.0.0 (2018/10/01) 警告:[deprecation] WorkbookのgetNameAt(int)は推奨されません (POI4.0.0) 1573// for( int i=0; i<cnt; i++ ) { 1574 for( final Name nm : wkbook.getAllNames() ) { 1575 String name = null; 1576 String ref = null; 1577 1578// final Name nm = wkbook.getNameAt(i); 1579 try { 1580 name = nm.getNameName(); 1581 ref = nm.getRefersToFormula(); 1582 } 1583 // catch( final Exception ex ) { // 6.1.0.0 (2014/12/26) refactoring 1584 catch( final RuntimeException ex ) { 1585 final String errMsg = "POIUtil:RefersToFormula Error! name=[" + name + "]" + ex.getMessage() ; 1586 System.out.println( errMsg ); 1587 // Excel97形式の場合、getRefersToFormula() でエラーが発生することがある。 1588 } 1589 1590 nmSet.add( name + "\t" + ref ); 1591 1592 // 削除するとEXCELが壊れる? なお、削除時には逆順で廻さないとアドレスがずれます。 1593 // if( nm.isDeleted() ) { wkbook.removeName(i); } 1594 } 1595 1596// return nmSet.toArray( new String[nmSet.size()] ); 1597 return nmSet.toArray( new String[0] ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 OptimizableToArrayCall 対応 1598 } 1599 1600 /** 1601 * 書式のスタイル一覧を取得します。 1602 * 1603 * EXCEL上に定義された書式のスタイルを、配列で返します。 1604 * 書式のスタイルの名称は、CellStyle にメソッドが定義されていません。 1605 * 実クラスである HSSFCellStyle にキャストして使用する 1606 * 必要があります。(XSSFCellStyle にも名称を取得するメソッドがありません。) 1607 * 1608 * ※ EXCEL2010 ホームタブ→セルのスタイル は、一つづつしか削除できません。 1609 * マクロは、開発タブ→Visual Basic で、挿入→標準モジュール を開き 1610 * テキストを張り付けてください。 1611 * 実行は、開発タブ→マクロ で、マクロ名を選択して、実行します。 1612 * 最後は、削除してください。 1613 * 1614 * ◆ スタイルの一括削除 EXCEL VBA マクロ 1615 * http://komitsudo.blog70.fc2.com/blog-entry-104.html 1616 * Sub DeleteStyle() 1617 * Dim styl 1618 * On Error Resume Next 1619 * For Each styl In ActiveWorkbook.Styles 1620 * If Not styl.BuiltIn Then 1621 * styl.Delete 1622 * End If 1623 * Next 1624 * MsgBox "すべての追加スタイルを削除しました。", vbOKOnly 1625 * End Sub 1626 * 1627 * ◆ 名前の表示、削除、スタイルの削除の一括実行 EXCEL VBA マクロ 1628 * Sub AllDelete() 1629 * Call VisibleNames 1630 * Call DeleteNames 1631 * Call DeleteStyle 1632 * MsgBox "すべての処理を完了しました。", vbOKOnly 1633 * End Sub 1634 * 1635 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 1636 * 1637 * @param wkbook Workbookオブジェクト 1638 * @return 書式のスタイル一覧 1639 * @og.rtnNotNull 1640 */ 1641 public static String[] getStyleNames( final Workbook wkbook ) { 1642 final int cnt = wkbook.getNumCellStyles(); // return 値は、short 1643 1644 final Set<String> nmSet = new TreeSet<>(); 1645 1646 for( int s=0; s<cnt; s++ ) { 1647 final CellStyle cs = wkbook.getCellStyleAt( (short)s ); 1648 if( cs instanceof HSSFCellStyle ) { 1649 final HSSFCellStyle hcs = (HSSFCellStyle)cs; 1650 final String name = hcs.getUserStyleName(); 1651 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 1652 if( name == null ) { // この処理は不要かも。 1653 final HSSFCellStyle pst = hcs.getParentStyle(); 1654 if( pst != null ) { 1655 final String pname = pst.getUserStyleName(); 1656 if( pname != null ) { nmSet.add( pname ); } 1657 } 1658 } 1659 else { 1660 nmSet.add( name ); 1661 } 1662 } 1663 } 1664 1665// return nmSet.toArray( new String[nmSet.size()] ); 1666 return nmSet.toArray( new String[0] ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 OptimizableToArrayCall 対応 1667 } 1668 1669 /** 1670 * セル情報を返します。 1671 * 1672 * エラー発生時に、どのセルでエラーが発生したかの情報を取得できるようにします。 1673 * 1674 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1675 * @og.rev 6.0.3.0 (2014/11/13) セル情報を作成する時に、値もセットします。 1676 * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるロジック変更 1677 * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします。 1678 * 1679 * @param oCell EXCELのセルオブジェクト 1680 * @return セル情報の文字列 1681 */ 1682 public static String getCellMsg( final Cell oCell ) { 1683 String lastMsg = null; 1684 1685 if( oCell != null ) { 1686 final String shtNm = oCell.getSheet().getSheetName(); 1687 final int rowNo = oCell.getRowIndex(); 1688 final int celNo = oCell.getColumnIndex(); 1689 1690 // 6.0.3.0 (2014/11/13) セル情報を作成する時に、値もセットします。 1691 lastMsg = " Sheet=" + shtNm + ", Row=" + rowNo + ", Cel=" + celNo 1692 + "(" + getCelKigo(rowNo,celNo) + "), Val=" + oCell.toString() ; 1693 } 1694 1695 return lastMsg; 1696 } 1697 1698 /** 1699 * Excelの行番号,列番号より、セル記号を求めます。 1700 * 1701 * 行番号は、0から始まる数字ですが、記号化する場合は、1から始まります。 1702 * Excelの列記号とは、A,B,C,…,Z,AA,AB,…,ZZ,AAA,AAB,… と続きます。 1703 * つまり、アルファベットだけの、26進数になります。(ゼロの扱いが少し特殊です) 1704 * 列番号は、0から始まる数字で、0=A,1=B,2=C,…,25=Z,26=AA,27=AB,…,701=ZZ,702=AAA,703=AAB,… 1705 * EXCELの行列記号にする場合は、この列記号に、行番号を、+1して付ければよいだけです。 1706 * (※ 列番号に+1するのは、内部では0から始まる列番号ですが、表示上は1から始まります) 1707 * 1708 * @og.rev 6.2.2.0 (2015/03/27) celKigo を求めるロジック変更 1709 * @og.rev 6.3.1.0 (2015/06/28) rowNo(行番号)も引数に取るようにします。 1710 * 1711 * @param rowNo 行番号(0,1,2,…) 1712 * @param colNo 列番号(0,1,2,…) 1713 * @return Excelの列記号(A1,B2,C3,…) 1714 */ 1715 public static String getCelKigo( final int rowNo,final int colNo ) { 1716 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 1717 int cnt = colNo; 1718 while( cnt >= 26 ) { 1719 buf.append( (char)('A'+cnt%26) ); 1720 cnt = cnt/26-1; 1721 } 1722 buf.append( (char)('A'+cnt%26) ) 1723 .reverse() // append で逆順に付けているので、反転して返します。 1724 .append( rowNo+1 ); 1725 1726 return buf.toString(); 1727 } 1728 1729 /** 1730 * XSSFSimpleShapeオブジェクトにリンクを設定します。 1731 * 1732 * 処理の簡素化のために、url引数が null の場合は、何もせず処理を終了します。 1733 * 1734 * @og.rev 8.1.0.1 (2022/01/07) テキストベースのリンク作成 1735 * 1736 * @param shape XSSFSimpleShapeオブジェクト 1737 * @param url リンク文字列 1738 */ 1739 public static void makeShapeLink( final XSSFSimpleShape shape , final String url ) { 1740 if( url == null ) { return; } 1741 1742 final String rid = shape.getDrawing() // XSSFDrawing XSSFShape#getDrawing() 1743 .getPackagePart() // PackagePart POIXMLDocumentPart#getPackagePart() 1744 .addExternalRelationship( // PackageRelationship PackagePart#addExternalRelationship(String,String) 1745 url, PackageRelationshipTypes.HYPERLINK_PART) 1746 .getId(); // String PackageRelationship#getId() 1747 1748 final CTHyperlink hyperlink = CTHyperlink.Factory.newInstance(); 1749 hyperlink.setId(rid); 1750 1751 shape.getCTShape() // CTShape XSSFSimpleShape#getCTShape() 1752 .getNvSpPr() // CTShapeNonVisual CTShape#getNvSpPr() 1753 .getCNvPr() // CTNonVisualDrawingProps CTShapeNonVisual#getCNvPr() 1754 .setHlinkClick( hyperlink ); // void CTNonVisualDrawingProps#setHlinkClick(CTHyperlink) 1755 } 1756 1757 /** 1758 * XSSFSimpleShapeオブジェクトにカラーを設定します。 1759 * 1760 * 処理の簡素化のために、col引数が null の場合は、何もせず処理を終了します。 1761 * col配列は、[0]:red [1]:blue [2] green です。 1762 * 1763 * ※ カラーの設定を、XSSFSimpleShape#setFillColor(int,int,int) で行うと、XSLXファイルが 1764 * 壊れるようです。POIが対応できていないのか、カラー化設定方法を間違っているのか… 1765 * 1766 * @og.rev 8.1.0.1 (2022/01/07) テキストベースのカラー作成 1767 * 1768 * @param shape XSSFSimpleShapeオブジェクト 1769 * @param col 色配列 1770 */ 1771 public static void makeShapeColor( final XSSFSimpleShape shape , final int[] col ) { 1772 if( col != null ) { 1773 shape.setFillColor(col[0],col[1],col[2]); 1774 } 1775 } 1776 1777 /** 1778 * アプリケーションのサンプルです。 1779 * 1780 * 入力ファイル名 は必須で、第一引数固定です。 1781 * 第二引数は、処理方法で、-ALL か、-LINE を指定します。何も指定しなければ、-ALL です。 1782 * 第三引数を指定した場合は、Encode を指定します。 1783 * 1784 * Usage: java org.opengion.fukurou.model.POIUtil 入力ファイル名 [処理方式] [エンコード] 1785 * -A(LL) ・・・ ALL 一括処理(初期値) 1786 * -L(INE) ・・・ LINE 行単位処理 1787 * -S(heet) ・・・ Sheet名一覧 1788 * -N(AME) ・・・ NAME:名前定義 1789 * -C(ellStyle) ・・・ CellStyle:書式のスタイル 1790 * 1791 * @og.rev 6.0.2.0 (2014/09/19) 新規作成 1792 * @og.rev 6.2.3.0 (2015/05/01) パラメータ変更、textReader → extractor に変更 1793 * @og.rev 6.2.4.2 (2015/05/29) 引数判定の true/false の処理が逆でした。 1794 * @og.rev 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1795 * @og.rev 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 1796 * 1797 * @param args コマンド引数配列 1798 */ 1799 public static void main( final String[] args ) { 1800 final String usageMsg = "Usage: java org.opengion.fukurou.model.POIUtil 入力ファイル名 [処理方式] [エンコード]" + "\n" + 1801 "\t -A(LL) ・・・ ALL 一括処理(初期値) \n" + 1802 "\t -L(INE) ・・・ LINE 行単位処理 \n" + 1803 "\t -S(heet) ・・・ Sheet名一覧 \n" + 1804 "\t -N(AME) ・・・ NAME:名前定義 \n" + 1805 "\t -C(ellStyle) ・・・ CellStyle:書式のスタイル \n" ; 1806 if( args.length == 0 ) { 1807 System.err.println( usageMsg ); 1808 return ; 1809 } 1810 1811 final File file = new File( args[0] ); 1812 final char type = args.length >= 2 ? args[1].charAt(1) : 'A' ; 1813 final String encode = args.length >= 3 ? args[2] : null ; // 6.2.4.2 (2015/05/29) true/false の処理が逆でした。 1814 1815 // 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 1816// switch( type ) { 1817// case 'A' : if( encode == null ) { 1818//// System.out.println( POIUtil.extractor( file ) ); 1819// System.out.println( extractor( file ) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1820// } 1821// else { 1822//// System.out.println( POIUtil.extractor( file,encode ) ); 1823// System.out.println( extractor( file,encode ) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1824// } 1825// break; 1826// // 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1827// case 'L' : final TextConverter<String,String> conv = 1828// ( val,cmnt ) -> { 1829// System.out.println( "val=" + val + " , cmnt=" + cmnt ); 1830// return null; 1831// }; 1832// 1833// // new TextConverter<String,String>() { 1834// // /** 1835// // * 入力文字列を、変換します。 1836// // * 1837// // * @param val 入力文字列 1838// // * @param cmnt コメント 1839// // * @return 変換文字列(変換されない場合は、null) 1840// // */ 1841// // @Override 1842// // public String change( final String val , final String cmnt ) { 1843// // System.out.println( "val=" + val + " , cmnt=" + cmnt ); 1844// // return null; 1845// // } 1846// // }; 1847// 1848// if( encode == null ) { 1849//// POIUtil.textReader( file,conv ); 1850// textReader( file,conv ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1851// } 1852// else { 1853//// POIUtil.textReader( file,conv,encode ); 1854// textReader( file,conv,encode ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1855// } 1856// break; 1857//// case 'S' : final String[] shts = POIUtil.getSheetNames( POIUtil.createWorkbook(file) ); 1858// case 'S' : final String[] shts = getSheetNames( createWorkbook(file) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1859// System.out.println( "No:\tSheetName" ); 1860// for( int i=0; i<shts.length; i++ ) { 1861// System.out.println( i + "\t" + shts[i] ); 1862// } 1863// break; 1864//// case 'N' : final String[] nms = POIUtil.getNames( POIUtil.createWorkbook(file) ); 1865// case 'N' : final String[] nms = getNames( createWorkbook(file) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1866// System.out.println( "No:\tName\tFormula" ); 1867// for( int i=0; i<nms.length; i++ ) { 1868// System.out.println( i + "\t" + nms[i] ); 1869// } 1870// break; 1871//// case 'C' : final String[] sns = POIUtil.getStyleNames( POIUtil.createWorkbook(file) ); 1872// case 'C' : final String[] sns = getStyleNames( createWorkbook(file) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1873// System.out.println( "No:\tStyleName" ); 1874// for( int i=0; i<sns.length; i++ ) { 1875// System.out.println( i + "\t" + sns[i] ); 1876// } 1877// break; 1878// default : System.err.println( usageMsg ); 1879// break; 1880// } 1881 switch( type ) { 1882 case 'A' -> { 1883 if( encode == null ) { 1884 System.out.println( extractor( file ) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1885 } 1886 else { 1887 System.out.println( extractor( file,encode ) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1888 } 1889 } 1890 // 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更 1891 case 'L' -> { 1892 final TextConverter<String,String> conv = ( val,cmnt ) -> { 1893 System.out.println( "val=" + val + " , cmnt=" + cmnt ); 1894 return null; 1895 }; 1896 if( encode == null ) { 1897 textReader( file,conv ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1898 } 1899 else { 1900 textReader( file,conv,encode ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1901 } 1902 } 1903 case 'S' -> { 1904 final String[] shts = getSheetNames( createWorkbook(file) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1905 System.out.println( "No:\tSheetName" ); 1906 for( int i=0; i<shts.length; i++ ) { 1907 System.out.println( i + "\t" + shts[i] ); 1908 } 1909 } 1910 case 'N' -> { 1911 final String[] nms = getNames( createWorkbook(file) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1912 System.out.println( "No:\tName\tFormula" ); 1913 for( int i=0; i<nms.length; i++ ) { 1914 System.out.println( i + "\t" + nms[i] ); 1915 } 1916 } 1917 case 'C' -> { 1918 final String[] sns = getStyleNames( createWorkbook(file) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 1919 System.out.println( "No:\tStyleName" ); 1920 for( int i=0; i<sns.length; i++ ) { 1921 System.out.println( i + "\t" + sns[i] ); 1922 } 1923 } 1924 default -> System.err.println( usageMsg ); 1925 } 1926 } 1927}