    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.0.0.0 (2021/08/20)" ;
    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         * @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オブジェクトに、１行分のデータを追加します。
    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 １行分のデータ配列
    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オブジェクトに準拠するため、０から始まる整数です。
    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         * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
    350         * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
    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         * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
    367         * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
    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}