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.plugin.io; 017 018import java.io.File; // 6.2.0.0 (2015/02/27) 019import java.util.Arrays; // 6.8.2.4 (2017/11/20) 020 021import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 022import org.opengion.fukurou.util.StringUtil; 023import org.opengion.fukurou.util.FileInfo; // 6.2.4.2 (2015/05/29) 024import org.opengion.fukurou.model.TableModelHelper; // 6.2.0.0 (2015/02/27) クラス名変更 025import org.opengion.fukurou.model.EventReader_XLS; // 6.2.4.2 (2015/05/29) 026import org.opengion.fukurou.model.EventReader_XLSX; // 6.2.4.2 (2015/05/29) 027// import org.opengion.fukurou.model.FileOperation; // 8.0.0.1 (2021/10/08) 028// import org.opengion.fukurou.util.FileUtil; // 8.0.0.1 (2021/10/08) 029 030import org.opengion.hayabusa.common.HybsSystemException; 031import org.opengion.hayabusa.io.AbstractTableReader; // 6.2.0.0 (2015/02/27) 032// import org.opengion.hayabusa.io.HybsFileOperationFactory; // 8.0.0.1 (2021/10/08) 033 034import static org.opengion.fukurou.system.HybsConst.CR ; // 6.2.2.0 (2015/03/27) 035 036/** 037 * POI による、EXCELバイナリファイルを読み取る実装クラスです。 038 * 039 * ファイル名、シート名を指定して、データを読み取ることが可能です。 040 * 第一カラムが # で始まる行は、コメント行なので、読み飛ばします。 041 * カラム名の指定行で、カラム名が null の場合は、その列は読み飛ばします。 042 * 043 * 入力形式は、openXML形式にも対応しています。 044 * ファイルの内容に応じて、.xlsと.xlsxのどちらで読み取るかは、内部的に 045 * 自動判定されます。 046 * 047 * @og.rev 3.5.4.8 (2004/02/23) 新規作成 048 * @og.rev 4.3.6.7 (2009/05/22) ooxml形式対応 049 * @og.group ファイル入力 050 * 051 * @version 4.0 052 * @author Kazuhiko Hasegawa 053 * @since JDK5.0, 054 */ 055public class TableReader_Excel extends AbstractTableReader { 056 /** このプログラムのVERSION文字列を設定します。 {@value} */ 057 private static final String VERSION = "8.0.0.2 (2021/10/15)" ; 058 059 /** 060 * デフォルトコンストラクター 061 * 062 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 063 */ 064 public TableReader_Excel() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 065 066 /** 067 * DBTableModel から 各形式のデータを作成して、BufferedReader より読み取ります。 068 * コメント/空行を除き、最初の行は、必ず項目名が必要です。 069 * それ以降は、コメント/空行を除き、データとして読み込んでいきます。 070 * このメソッドは、EXCEL 読み込み時に使用します。 071 * 072 * @og.rev 4.0.0.0 (2006/09/31) 新規追加 073 * @og.rev 5.1.6.0 (2010/05/01) columns 処理 追加 074 * @og.rev 5.1.6.0 (2010/05/01) skipRowCountの追加 075 * @og.rev 5.1.8.0 (2010/07/01) Exception をきちっと記述(InvalidFormatException) 076 * @og.rev 5.2.1.0 (2010/10/01) setTableColumnValues メソッドを経由して、テーブルにデータをセットする。 077 * @og.rev 5.5.1.2 (2012/04/06) HeaderData を try の上にだし、エラーメッセージを取得できるようにする。 078 * @og.rev 5.5.7.2 (2012/10/09) sheetNos 追加による複数シートのマージ読み取りサポート 079 * @og.rev 5.5.8.2 (2012/11/09) HeaderData に デバッグフラグを渡します。 080 * @og.rev 6.0.2.5 (2014/10/31) debug=true 時に、進捗が見えるようにします。 081 * @og.rev 6.1.0.0 (2014/12/26) Excel関係改善 082 * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)。新規 083 * @og.rev 6.2.1.0 (2015/03/13) TableReaderModel を外部からセットします。 084 * @og.rev 6.2.3.0 (2015/05/01) 行読み飛ばし nullSkipClm追加 085 * @og.rev 6.2.4.2 (2015/05/29) POIUtil を使わず、EventReader_XLS、EventReader_XLSX を直接呼び出します。 086 * @og.rev 6.4.1.2 (2016/01/22) setConstData と、setNames は、内部処理が走るため、他の設定が終わってから呼び出す。 087 * @og.rev 8.0.0.1 (2021/10/08) OPCPackage が、クラウドファイルを処理できない…みたい。 088 * @og.rev 8.0.0.2 (2021/10/15) TableReader系は、クラウドから、ローカルファイルに移してから処理する。 089 * 090 * @param file 読み取り元ファイル名(ローカルファイル) 091 * @param enc ファイルのエンコード文字列(未使用) 092 */ 093 @Override 094 public void readDBTable( final File file , final String enc ) { 095 final boolean isDebug = isDebug(); // 5.5.7.2 (2012/10/09) デバッグ情報 096 097 // if( isDebug ) { System.out.println( " Filename=" + file ) ; } 098 099// // 8.0.0.1 (2021/10/08) OPCPackage が、クラウドファイルを処理できない…みたい。 100// // クラウドのファイルをローカルにコピーしています。 101// final File file = new File(inFile.toString()); // 後で修正 102// if( inFile instanceof FileOperation ) { 103// FileUtil.copy( inFile,file ); 104// } 105 106 final TableModelHelper helper = new TableModelHelper() { 107 private boolean[] useShtNo ; // 6.1.0.0 (2014/12/26) 読み取り対象のシート管理 108 109 /** 110 * シートの数のイベントが発生します。 111 * 112 * 処理の開始前に、シートの数のイベントが発生します。 113 * これを元に、処理するシート番号の選別が可能です。 114 * 初期実装は、されていません。 115 * 116 * @og.rev 6.1.0.0 (2014/12/26) Excel関係改善 117 * @og.rev 6.2.6.0 (2015/06/19) #csv2ArrayExt(String,int)の戻り値を、文字列配列から数字配列に変更。 118 * @og.rev 5.9.9.2 (2016/06/17) シート名指定が効果ないため修正 119 * 120 * @param size シートの数 121 */ 122 @Override 123 public void sheetSize( final int size ) { 124 if( isDebug ) { System.out.println( " sheetSize=" + size ) ; } 125 // 5.5.7.2 (2012/10/09) 複数シートのマージ読み取り。 sheetNos の指定が優先される。 126 useShtNo = new boolean[size]; // シート数だけ、配列を作成する。 127 if( sheetNos != null && sheetNos.length() > 0 ) { 128 // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 129 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 130// final Integer[] sheetList = StringUtil.csv2ArrayExt( sheetNos , size-1 ); // 最大シート番号は、シート数-1 131// for( int i=0; i<sheetList.length; i++ ) { 132// useShtNo[sheetList[i]] = true; // 読み取り対象のシート番号のみ、ture にセット 133// } 134 for( final int shtNo : StringUtil.csv2ArrayExt( sheetNos , size-1 ) ) { // 最大シート番号は、シート数-1 135 useShtNo[shtNo] = true; // 読み取り対象のシート番号のみ、ture にセット 136 } 137 } 138 else if ( sheetName == null || sheetName.length() == 0 ) { // 5.9.9.2 (2016/06/17) sheetNameを見ないと必ず先頭が読み込まれる 139 useShtNo[0] = true; // 一番目のシート 140 } 141 } 142 143 /** 144 * シートの読み取り開始時にイベントが発生します。 145 * 146 * 新しいシートの読み取り開始毎に、1回呼ばれます。 147 * 戻り値が、true の場合は、そのシートの読み取りを継続します。 148 * false の場合は、そのシートの読み取りは行わず、次のシートまで 149 * イベントは発行されません。 150 * 151 * @og.rev 6.0.3.0 (2014/11/13) 新規作成 152 * @og.rev 7.3.1.1 (2021/02/25) 現在実行中のシート名をセットする 153 * 154 * @param shtNm シート名 155 * @param shtNo シート番号(0~) 156 * @return true:シートの読み取り処理を継続します/false:このシートは読み取りません。 157 */ 158 @Override 159 public boolean startSheet( final String shtNm,final int shtNo ) { 160 // if( isDebug ) { System.out.println( " Sheet[" + shtNo + "]=" + shtNm ) ; } 161 super.startSheet( shtNm , shtNo ); // cnstData の呼び出しの為。無しで動くようにしなければ… 162 163// return useShtNo != null && useShtNo[shtNo] || 164// sheetName != null && sheetName.equalsIgnoreCase( shtNm ) ; // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 165 166 final boolean isUseSheet = useShtNo != null && useShtNo[shtNo] || 167 sheetName != null && sheetName.equalsIgnoreCase( shtNm ) ; // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 168 169 if( isUseSheet ) { shtName( shtNm ); } // 7.3.1.1 (2021/02/25) 170 171 return isUseSheet; 172 } 173 174 /** 175 * カラム名配列がそろった段階で、イベントが発生します。 176 * 177 * openGion での標準的な処理は、colNo==0 の時に、val の先頭が、#NAME 178 * で始まるレコードを、名前配列として認識します。 179 * #value( String,int,int ) で、この #NAME だけは、継続処理されます。 180 * その上で、#NAME レコードが終了した時点で、カラム名配列が完成するので 181 * そこで初めて、このメソッドが呼ばれます。 182 * 183 * @og.rev 6.0.3.0 (2014/11/13) 新規作成 184 * @og.rev 6.1.0.0 (2014/12/26) omitNames 属性を追加 185 * @og.rev 6.8.2.4 (2017/11/20) isDebug 処理を追加 186 * 187 * @param names カラム名配列(可変長引数) 188 * @see #value( String,int,int ) 189 */ 190 @Override 191 public void columnNames( final String[] names ) { 192 if( isDebug ) { System.out.println( " names=" + Arrays.toString( names ) ) ; } 193 setTableDBColumn( names ) ; 194 } 195 196 /** 197 * #NAME のオリジナルカラム名配列がそろった段階で、イベントが発生します。 198 * 199 * @og.rev 7.3.1.3 (2021/03/09) #NAMEのオリジナルを取得できるようにします。 200 * 201 * @param names カラム名配列 202 */ 203 @Override 204 public void originalNames( final String[] names ) { 205 setOriginalNames( names ) ; 206 } 207 208 /** 209 * row にあるセルのオブジェクト値がそろった段階で、イベントが発生します。 210 * 211 * @og.rev 6.0.3.0 (2014/11/13) 新規作成 212 * @og.rev 6.2.1.0 (2015/03/13) setTableColumnValuesに、行番号を引数に追加 213 * @og.rev 6.8.2.4 (2017/11/20) isDebug 処理を追加 214 * 215 * @param vals 文字列値の1行分の配列 216 * @param rowNo 行番号(0~) 217 */ 218 @Override 219 public void values( final String[] vals,final int rowNo ) { 220 if( isDebug && rowNo % 100 == 0 ) { System.out.println( " rowNo=" + rowNo ) ; } 221 setTableColumnValues( vals,rowNo ); // 6.2.1.0 (2015/03/13) 222 } 223 }; 224 225 // 6.4.1.2 (2016/01/22) setConstData と、setNames は、内部処理が走るため、他の設定が終わってから呼び出す。 226 helper.setSkipRowCount( getSkipRowCount() ); // 6.1.0.0 (2014/12/26) 外部からスキップ行数を指定。 227 228 helper.setNullBreakClm( nullBreakClm ); // 6.2.0.0 (2015/02/27) 外部からnullBreakClmを指定。 229 helper.setNullSkipClm( nullSkipClm ); // 6.2.3.0 (2015/05/01) 外部からnullSkipClmを指定。 230 helper.setDebug( isDebug ); // 6.2.0.0 (2015/02/27) 231 232 // 6.4.1.2 (2016/01/22) setConstData と、setNames は、内部処理が走るため、他の設定が終わってから呼び出す。 233 helper.setConstData( constKeys , constAdrs ); // 6.1.0.0 (2014/12/26) 外部から固定値情報を指定。 234 helper.setNames( columns , isUseNumber() ); // 6.1.0.0 (2014/12/26) 外部からカラム名配列を指定。 235 236 // 6.2.4.2 (2015/05/29) POIUtil を使わず、EventReader_XLS、EventReader_XLSX を直接呼び出します。 237 final String SUFIX = FileInfo.getSUFIX( file ); 238 if( "xls".equalsIgnoreCase( SUFIX ) ) { 239 new EventReader_XLS().eventReader( file,helper ); 240 } 241 else if( "xlsx".equalsIgnoreCase( SUFIX ) || "xlsm".equalsIgnoreCase( SUFIX ) ) { 242 new EventReader_XLSX().eventReader( file,helper ); 243 } 244 else { 245 final String errMsg = "拡張子は、xls,xlsx,xlsm にしてください。[" + file + "]" ; 246 throw new OgRuntimeException( errMsg ); 247 } 248 249 // 最後まで、#NAME が見つから無かった場合 250 if( !helper.isNameSet() ) { 251 final String errMsg = "最後まで、#NAME が見つかりませんでした。" 252 + CR 253 + "ファイル形式が異なるか、もしくは損傷している可能性があります。" 254 + "Class=[Excel], File=[" + file + "]" 255 + CR ; 256 throw new HybsSystemException( errMsg ); 257 } 258 259 if( isDebug ) { System.out.println( " TableReader End." ) ; } 260 } 261}