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