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.hayabusa.io;
017
018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
019
020import org.opengion.hayabusa.db.ColumnActionListener;           // 6.2.2.0 (2015/03/27)
021
022/**
023 * 指定の区切り記号(初期値:タブ区切り)ファイルの読み取りクラスです。
024 *
025 * 名前、データの入力部のみオーバーライドすれば、各種入力フォーマットに合わせた
026 * サブクラスを実現する事が可能です。
027 *
028 * @og.group ファイル入力
029 *
030 * @version  4.0
031 * @author   Kazuhiko Hasegawa
032 * @since    JDK5.0,
033 */
034public abstract class AbstractTableReader implements TableReader {
035        /** このプログラムのVERSION文字列を設定します。   {@value} */
036        private static final String VERSION = "8.5.4.2 (2024/01/12)" ;
037
038        /** システムの改行コードを設定します。*/
039
040        /** 区切り文字列 */
041        protected String separator = TAB_SEPARATOR;     // 6.2.0.0 (2015/02/27) 項目区切り文字。protected化
042
043        private ColumnActionListener listener ;         // 6.2.2.0 (2015/03/27)
044
045        // 3.5.4.5 (2004/01/23) カラム名の外部指定を出来る様にする。
046        /** カラム名 */
047        // 6.1.0.0 (2014/12/26) 読み取り対象外のカラム列を、外部(タグ)より指定する。
048        protected String        columns ;                               // 外部指定のカラム名 ( 4.3.4.7 (2009/01/22) protectedに変更 )
049        private boolean         useNumber = true;               // 3.7.0.5 (2005/04/11)
050        private int                     skipRowCount;                   // 5.1.6.0 (2010/05/01) データの読み飛ばし設定
051
052        // 5.2.1.0 (2010/10/01) コードリソース毎のラベル逆引きマップ
053
054        // 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
055        /** シート名 */
056        protected String  sheetName             ;               // 3.5.4.2 (2003/12/15)
057        /** シート番号 */
058        protected String  sheetNos              ;               // 5.5.7.2 (2012/10/09)
059        /** カラム名(CSV形式) */
060        protected String  constKeys             ;               // 5.5.8.2 (2012/11/09) 固定値となるカラム名(CSV形式)
061        /** 固定値となるアドレス */
062        protected String  constAdrs             ;               // 5.5.8.2 (2012/11/09) 固定値となるアドレス(行-列,行-列,・・・)
063        /** BREAK条件 */
064        protected String  nullBreakClm  ;               // 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件
065        /** 行読み飛ばし */
066        protected String  nullSkipClm   ;               // 6.2.3.0 (2015/05/01) 行読み飛ばし
067
068        private boolean   useDebug              ;               // 5.5.7.2 (2012/10/09) デバッグ情報の出力するかどうか
069
070        /**
071         * デフォルトコンストラクター
072         *
073         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
074         */
075        protected AbstractTableReader() { super(); }            // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
076
077        /**
078         * DBColumn オブジェクトをDBTable に設定します。
079         *
080         * ここでは、omitNames 属性を加味した、カラム名を作成します。
081         * その際、ゼロ文字列のカラム名は、omit します。
082         * 戻り値は、新しいカラム数(omitNames を加味、ゼロ文字列のカラムも除去)です。
083         *
084         * ※ 6.1.0.0 (2014/12/26) で、omitNames 属性を追加します。
085         *    これに伴い、従来は、EXCELのみ、#NAME で、ゼロ文字列のカラム名や
086         *    columns 指定で、a,b,,,,,,x のようなカラム名指定で、カラム飛ばしを
087         *    実装していましたが、その他の Reader でも、対応します。
088         *    これは、互換性に影響しますので、ご注意ください。
089         *    (読み込んでも、カラム名が無いので、使えなかったと思いますけど…)
090         *
091         * @og.rev 3.5.4.2 (2003/12/15) private を protected に変更。
092         * @og.rev 3.5.4.5 (2004/01/23) DBColumn 配列に値をセットします。
093         * @og.rev 5.2.1.0 (2010/10/01) useRenderer対応(コードリソース毎のラベル逆引き)
094         * @og.rev 6.1.0.0 (2014/12/26) omitNames 属性を追加
095         * @og.rev 6.2.1.0 (2015/03/13) ロングラベルから、コード値を見つける機能を、廃止します。
096         * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。
097         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
098         * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 LinguisticNaming 対応
099         *
100         * @param names カラム名配列
101//       * @return 新しいカラム数(omitNames を加味、ゼロ文字列のカラムも除去)
102         */
103//      protected int setTableDBColumn( final String[] names ) {
104        protected void setTableDBColumn( final String[] names ) {
105                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
106                if( listener == null ) {
107                        final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
108                        throw new OgRuntimeException( errMsg );
109                }
110
111                listener.columnNames( names );                  // 6.2.2.0 (2015/03/27)
112
113//              return names == null ? 0 : names.length;
114        }
115
116        /**
117         * #NAME のオリジナルカラム名配列がそろった段階で、イベントが発生します。
118         *
119         * @og.rev 7.3.1.3 (2021/03/09) #NAMEのオリジナルを取得できるようにします。
120         * @og.rev 8.0.0.0 (2021/08/20) spotbugs:Bug kind and pattern: UwF - UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR
121         *
122         * @param   names  カラム名配列
123         */
124        protected void setOriginalNames( final String[] names ) {
125                // 8.0.0.0 (2021/08/20) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(spotbugs)
126                if( listener == null ) {
127                        final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
128                        throw new OgRuntimeException( errMsg );
129                }
130
131                listener.originalNames( names );
132        }
133
134        /**
135         * DBTableModelオブジェクトに、1行分のデータを追加します。
136         * これ自体は、メソッドの共通化による 拡張をしやすくするために用意しました。
137         *
138         * @og.rev 5.2.1.0 (2010/10/01) 新規作成
139         * @og.rev 6.2.0.0 (2015/02/27) コードリソースの逆引き処理の前に、Key:Val 分解を実行する。
140         * @og.rev 6.2.1.0 (2015/03/13) ロングラベルから、コード値を見つける機能を、廃止します。
141         * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。
142         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
143         *
144         * @param values 1行分のデータ配列
145         * @param rowNo  行番号
146         */
147        protected void setTableColumnValues( final String[] values, final int rowNo ) {
148                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
149                if( listener == null ) {
150                        final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
151                        throw new OgRuntimeException( errMsg );
152                }
153
154                listener.values( values,rowNo );                                        // 6.2.2.0 (2015/03/27)
155        }
156
157        /**
158         * 新しくEXCELのシートを処理する際に、シート名をセットするときに呼び出されます。
159         * EXCEL関係の処理の場合のみ呼び出されます。
160         *
161         * @og.rev 7.3.1.1 (2021/02/25) 現在実行中のシート名をセットする
162         *
163         * @param   sheetName   現在実行中のシート名
164         */
165        protected void shtName( final String sheetName ) {
166                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
167                if( listener == null ) {
168                        final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
169                        throw new OgRuntimeException( errMsg );
170                }
171
172                listener.shtName( sheetName );
173        }
174
175        /**
176         * データを読み込む場合の区切り文字をセットします。
177         *
178         * なお、このメソッドは、サブクラスによっては使用しない場合があります。
179         * もし、使用しないサブクラスを作成する場合は、UnsupportedOperationException
180         * を throw するようにサブクラスで実装して下さい。
181         *
182         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
183         *
184         * @param   sep 区切り文字
185         */
186        @Override       // TableReader
187        public void setSeparator( final String sep ) {
188                if( sep != null ) { this.separator = sep; }
189        }
190
191        /**
192         * DBTableModelのデータとしてEXCELファイルを読み込むときのシート名を設定します。
193         * これにより、複数の形式の異なるデータを順次読み込むことや、シートを指定して
194         * 読み取ることが可能になります。
195         * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。
196         * のでご注意ください。
197         * このメソッドは、isExcel() == true の場合のみ利用されます。
198         *
199         * ※ このクラスでは実装されていません。
200         *
201         * @og.rev 3.5.4.2 (2003/12/15) 新規追加
202         * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
203         *
204         * @param   sheetName シート名
205         */
206        @Override       // TableReader
207        public void setSheetName( final String sheetName ) {
208                this.sheetName = sheetName;
209        }
210
211        /**
212         * EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。
213         *
214         * EXCEL読み込み時に複数シートをマージして取り込みます。
215         * シート番号は、0 から始まる数字で表します。
216         * ヘッダーは、最初のシートのカラム位置に合わせます。(ヘッダータイトルの自動認識はありません。)
217         * よって、指定するシートは、すべて同一レイアウトでないと取り込み時にカラムのずれが発生します。
218         *
219         * シート番号の指定は、CSV形式で、複数指定できます。また、N-M の様にハイフンで繋げることで、
220         * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。
221         * これらの組み合わせも可能です。( 0,1,3,5-8,10-* )
222         * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの
223         * どちらかです。途中には、"*" は、現れません。
224         * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。
225         * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。
226         * このメソッドは、isExcel() == true の場合のみ利用されます。
227         *
228         * 初期値は、0(第一シート) です。
229         *
230         * ※ このクラスでは実装されていません。
231         *
232         * @og.rev 5.5.7.2 (2012/10/09) 新規追加
233         * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
234         *
235         * @param   sheetNos EXCELファイルのシート番号(0から始まる)
236         * @see         #setSheetName( String )
237         */
238        @Override       // TableReader
239        public void setSheetNos( final String sheetNos ) {
240                this.sheetNos = sheetNos;
241        }
242
243        /**
244         * 固定値となるカラム名(CSV形式)と、constAdrs 固定値となるアドレス(行-列,行-列,・・・)を設定します。
245         *
246         * アドレスは、EXCEL上の行-列をCSV形式で指定します。
247         * 行列は、EXCELオブジェクトに準拠するため、0から始まる整数です。
248         * 0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。
249         * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として
250         * 設定することができます。
251         * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。
252         * このメソッドは、isExcel() == true の場合のみ利用されます。
253         *
254         * 5.7.6.3 (2014/05/23) より、
255         *   ①EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。
256         *     なお、A1,A2,B1 の記述は、必ず、英字1文字+数字 にしてください。(A~Zまで)
257         *   ②処理中のEXCELシート名をカラムに割り当てるために、"SHEET" という記号に対応します。
258         * 例えば、sheetConstKeys="CLM,LANG,NAME" とし、sheetConstAdrs="0-0,A2,SHEET" とすると、
259         * NAMEカラムには、シート名を読み込むことができます。
260         * これは、内部処理の簡素化のためです。
261         *
262         * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。
263         * このメソッドは、isExcel() == true の場合のみ利用されます。
264         *
265         * @og.rev 5.5.8.2 (2012/11/09) 新規追加
266         *
267         * @param   constKeys 固定値となるカラム名(CSV形式)
268         * @param   constAdrs 固定値となるアドレス(行-列,行-列,・・・)
269         */
270        @Override       // TableReader
271        public void setSheetConstData( final String constKeys,final String constAdrs ) {
272                this.constKeys = constKeys;
273                this.constAdrs = constAdrs;
274        }
275
276        /**
277         * ここに指定されたカラム列に NULL が現れた時点で読み取りを中止します。
278         *
279         * これは、指定のカラムは必須という事を条件に、そのレコードだけを読み取る処理を行います。
280         * 複数Sheetの場合は、次のSheetを読みます。
281         * 現時点では、Excel の場合のみ有効です。
282         *
283         * @og.rev 5.5.8.2 (2012/11/09) 新規追加
284         * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
285         *
286         * @param   clm カラム列
287         */
288        @Override       // TableReader
289        public void setNullBreakClm( final String clm ) {
290                nullBreakClm = clm;
291        }
292
293        /**
294         * ここに指定されたカラム列に NULL が現れたレコードは読み飛ばします。
295         *
296         * 例えば、更新対象カラムで、null の場合は、何もしない、などのケースで使用できます。
297         * 複数カラムの場合は、AND条件やOR条件などが、考えられるため、
298         * カラムを一つにまとめて、指定してください。
299         *
300         * @og.rev 6.2.3.0 (2015/05/01) 行読み飛ばし nullSkipClm追加
301         *
302         * @param   clm カラム列
303         */
304        @Override       // TableReader
305        public void setNullSkipClm( final String clm ) {
306                nullSkipClm = clm;
307        }
308
309        /**
310         * 読み取り元ファイルのカラム列を、外部(タグ)より指定します。
311         * ファイルに記述された #NAME より優先して使用されます。
312         *
313         * @og.rev 3.5.4.5 (2004/01/23) 新規作成
314         *
315         * @param   clms 読み取り元ファイルのカラム列(CSV形式)
316         */
317        @Override       // TableReader
318        public void setColumns( final String clms ) {
319                columns = clms ;
320        }
321
322        /**
323         * 行番号情報を指定[true:使用している/false:していない]します(初期値:true)。
324         *
325         * 通常のフォーマットでは、各行の先頭に行番号が出力されています。
326         * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。
327         * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの
328         * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、
329         * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。
330         * この場合、出力データのカラムの並び順が変更された場合、columns 属性も
331         * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように
332         * してください。
333         * なお、EXCEL 入力には、この設定は適用されません。(暫定対応)
334         * 初期値は、true(使用する) です。
335         *
336         * @og.rev 3.7.0.5 (2005/04/11) 新規追加
337         *
338         * @param       useNumber       行番号情報  [true:使用する/false:使用しない]
339         */
340        @Override       // TableReader
341        public void setUseNumber( final boolean useNumber ) {
342                this.useNumber = useNumber ;
343        }
344
345        /**
346         * データの読み始めの初期値を取得します。
347         *
348         * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。
349         * ファイルの先頭行が、0行としてカウントしますので、設定値は、読み飛ばす
350         * 件数になります。(1と指定すると、1件読み飛ばし、2行目から読み込みます。)
351         * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。
352         * #NAME属性や、columns 属性は、有効です。
353         *
354         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
355         *
356         * @return      読み始めの初期値
357         */
358        public int getSkipRowCount() {
359                return skipRowCount ;
360        }
361
362        /**
363         * データの読み飛ばし件数を設定します。
364         *
365         * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。
366         * ファイルの先頭行が、0行としてカウントしますので、設定値は、読み飛ばす
367         * 件数になります。(1と指定すると、1件読み飛ばし、2行目から読み込みます。)
368         * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。
369         * #NAME属性や、columns 属性は、有効です。
370         *
371         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
372         *
373         * @param       count 読み始めの初期値
374         */
375        @Override       // TableReader
376        public void setSkipRowCount( final int count ) {
377                skipRowCount = count;
378        }
379
380        /**
381         * 行番号情報を、使用している(true)/していない(false)を返します。
382         *
383         * 通常のフォーマットでは、各行の先頭に行番号が出力されています。
384         * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。
385         * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの
386         * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、
387         * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。
388         * この場合、出力データのカラムの並び順が変更された場合、columns 属性も
389         * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように
390         * してください。
391         * なお、EXCEL 入力には、この設定は適用されません。(暫定対応)
392         * 初期値は、true(使用する) です。
393         *
394         * @og.rev 3.7.0.5 (2005/04/11) 新規追加
395         * @og.rev 4.0.0.0 (2007/07/20) メソッド名変更(getUseNumber() ⇒  isUseNumber())
396         *
397         * @return      行番号情報を、使用している(true)/していない(false)を指定
398         */
399        protected boolean isUseNumber() {
400                return useNumber ;
401        }
402
403        /**
404         * ColumnActionListenerオブジェクトを設定します。
405         *
406         * ColumnActionListenerオブジェクトは、カラム名配列設定時と、それに対応する値配列設定時に
407         * 呼ばれるイベントリスナーです。
408         * 具体的なテーブル処理は、このイベントを使用して書き込みを行います。
409         *
410         * @og.rev 6.2.2.0 (2015/03/27) 新規作成
411         *
412         * @param       listener        ColumnActionListenerオブジェクト
413         */
414        @Override       // TableReader
415        public void setColumnActionListener( final ColumnActionListener listener ) {
416                this.listener = listener;
417        }
418
419        /**
420         * デバッグ情報を出力するかどうか[true:する/false:しない]を指定します。
421         *
422         * EXCELなどを読み取る場合、シートマージで読み取ると、エラー時の行番号が、連番になるため、
423         * どのシートなのか、判らなくなります。
424         * そこで、どうしてもわからなくなった場合に備えて、デバッグ情報を出力できるようにします。
425         * 通常は使用しませんので、設定を無視します。
426         * 初期値は、false:デバッグ情報を出力しない です。
427         *
428         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
429         *
430         * @param       useDebug        デバッグ出力するか [true:する/false:しない]
431         */
432        @Override       // TableReader
433        public void setDebug( final boolean useDebug ) {
434                this.useDebug = useDebug;
435        }
436
437        /**
438         * デバッグ情報を出力するかどうか[true:する/false:しない]を取得します。
439         *
440         * EXCELなどを読み取る場合、シートマージで読み取ると、エラー時の行番号が、連番になるため、
441         * どのシートなのか、判らなくなります。
442         * そこで、どうしてもわからなくなった場合に備えて、デバッグ情報を出力できるようにします。
443         *
444         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
445         *
446         * @return      デバッグ出力 [true:する/false:しない]
447         */
448        protected boolean isDebug() {
449                return useDebug ;
450        }
451}