    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.process;
    017
    018import org.opengion.fukurou.util.Argument;
    019import org.opengion.fukurou.util.HybsEntry ;
    020import org.opengion.fukurou.util.LogWriter;
    021import org.opengion.fukurou.util.StringUtil;            // 5.7.2.3 (2014/01/31)
    022
    023import java.util.Map ;
    024import java.util.LinkedHashMap ;
    025
    026/**
    027 * Process_TableFilter は、上流から受け取ったデータをフィルタする、
    028 * ChainProcess インターフェースの実装クラスです。
    029 *
    030 * 上流(プロセスチェインのデータは上流から下流へと渡されます。)から
    031 * 受け取ったLineModel を元に、項目のフィルタリングを行います。
    032 * 条件が成立した場合は、下流に流します。複数の条件を指定できますが、
    033 * すべて AND で判定されます。
    034 * (設定条件すべてを満たす場合のみ、下流にデータを流します。)
    035 *
    036 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。
    037 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に
    038 * 繋げてください。
    039 *
    040 * @og.formSample
    041 *  Process_TableFilter
    042 *
    043 *   [ -prefix_XXXX=接頭辞    ] ：項目名(XXXX)が、指定の接頭辞で始まる場合、条件成立。
    044 *   [ -suffix_XXXX=接尾辞    ] ：項目名(XXXX)が、指定の接尾辞で終わる場合、条件成立。
    045 *   [ -instr_XXXX=部分文字列 ] ：項目名(XXXX)が、指定の部分文字列と一致する場合、条件成立。
    046 *   [ -equals_XXXX=一致      ] ：項目名(XXXX)が、文字列と一致する場合、条件成立。文字列は、大文字小文字は区別しません(equalsIgnoreCase)。
    047 *   [ -match_XXXX=正規表現   ] ：項目名(XXXX)が、正規表現と一致する場合、条件成立。
    048 *   [ -unmatch_XXXX=正規表現 ] ：項目名(XXXX)が、正規表現と一致しない場合、条件成立。
    049 *   [ -const_XXXX=固定値     ] ：-const_FGJ=1
    050 *                                     項目名(XXXX)に、固定値を設定します。
    051 *   [ -replace_XXXX=固定値   ] ：-replace_BIKO="YYYY⇒ZZZZ" (元先指定は、⇒で区切ります。)
    052 *                                     項目名(XXXX)の文字列から、YYYY という文字を ZZZZ に置換します。
    053 *                                -replace_FGJ="_:0 A:1 B:2"    (元値:新値 のｽﾍﾟｰｽ区切り)
    054 *                                     区切り文字に ⇒ を使用しない場合は、CASE処理を行います。
    055 *                                     項目名(XXXX)の値を、元値 から 新値 に置換します。(完全一致)
    056 *   [ -display=[false/true]  ] ：結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    057 *   [ -debug=[false/true]    ] ：デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    058 *
    059 * @version  4.0
    060 * @author   Kazuhiko Hasegawa
    061 * @since    JDK5.0,
    062 */
    063public class Process_TableFilter extends AbstractProcess implements ChainProcess {
    064        /** replace_ で使用する区切り記号  {@value} */
    065        public static final char REP_SEP                = '⇒'   ;       // 4.3.1.1 (2008/08/24)
    066
    067        private static final String PREFIX_KEY  = "prefix_"     ;
    068        private static final String SUFFIX_KEY  = "suffix_"     ;
    069        private static final String INSTR_KEY   = "instr_"      ;
    070        private static final String EQUALS_KEY  = "equals_"     ;
    071        private static final String MATCH_KEY   = "match_"      ;
    072        private static final String UNMATCH_KEY = "unmatch_";
    073        private static final String CONST_KEY   = "const_"      ;
    074        private static final String REPLACE_KEY = "replace_" ;          // 4.3.1.1 (2008/08/24)
    075
    076        private final LineModelFilter filter = new LineModelFilter();
    077
    078        private boolean         display         = false;        // 表示しない
    079        private boolean         debug           = false;        // 5.7.3.0 (2014/02/07) デバッグ情報
    080
    081        private String[]        cnstClm         = null;         // 固定値を設定するカラム名
    082        private int[]           cnstClmNos      = null;         // 固定値を設定するカラム番号
    083        private String[]        constVal        = null;         // カラム番号に対応した固定値
    084
    085        // 4.3.1.1 (2008/08/24) replace 置換関係に必要なデータ
    086        private String[]        repClm          = null;         // 置換を設定するカラム名
    087        private int[]           repClmNos       = null;         // 置換を設定するカラム番号
    088        private String[]        repValFrom      = null;         // カラム番号に対応した置換元文字列
    089        private String[]        repValTo        = null;         // カラム番号に対応した置換後文字列
    090
    091        // 5.7.2.3 (2014/01/31) replace 置換の case処理関係に必要なデータ
    092        private String[]        caseVals        = null;         // カラム番号に対応した置換後文字列
    093
    094        private boolean         firstRow        = true;         // 最初の一行目
    095        private int                     count           = 0;
    096
    097        private static final Map<String,String> mustProparty   ;                // ［プロパティ］必須チェック用 Map
    098        private static final Map<String,String> usableProparty ;                // ［プロパティ］整合性チェック Map
    099
    100        static {
    101                mustProparty = new LinkedHashMap<String,String>();
    102
    103                usableProparty = new LinkedHashMap<String,String>();
    104                usableProparty.put( PREFIX_KEY  ,       "項目名(XXXX)が、指定の接頭辞で始まる場合、条件成立。" );
    105                usableProparty.put( SUFFIX_KEY  ,       "項目名(XXXX)が、指定の接尾辞で終わる場合、条件成立。" );
    106                usableProparty.put( INSTR_KEY   ,       "項目名(XXXX)が、指定の部分文字列と一致する場合、条件成立。" );
    107                usableProparty.put( EQUALS_KEY  ,       "項目名(XXXX)が、文字列と一致する場合、条件成立。" +
    108                                                                                CR + "(大文字小文字は区別しない)" );
    109                usableProparty.put( MATCH_KEY   ,       "項目名(XXXX)が、正規表現と一致する場合、条件成立。" );
    110                usableProparty.put( UNMATCH_KEY ,       "項目名(XXXX)が、正規表現と一致しない場合、条件成立。" );
    111                usableProparty.put( CONST_KEY   ,       "項目名(XXXX)に、固定値を設定します。" );
    112                // 4.3.1.1 (2008/08/24) replace 置換関係
    113                usableProparty.put( REPLACE_KEY ,       "項目名(XXXX)の文字列から、YYYY⇒ZZZZ で部分置換します。" +
    114                                                                                CR + "項目名(XXXX)の文字列から、_:0 A:1 B:2でCASE置換します。" );
    115                usableProparty.put( "display"   ,       "結果を標準出力に表示する(true)かしない(false)か" +
    116                                                                                CR + "(初期値:false:表示しない)" );
    117                usableProparty.put( "debug",    "デバッグ情報を標準出力に表示する(true)かしない(false)か" +
    118                                                                                CR + "(初期値:false:表示しない)" );             // 5.7.3.0 (2014/02/07) デバッグ情報
    119        }
    120
    121        /**
    122         * デフォルトコンストラクター。
    123         * このクラスは、動的作成されます。デフォルトコンストラクターで、
    124         * super クラスに対して、必要な初期化を行っておきます。
    125         *
    126         */
    127        public Process_TableFilter() {
    128                super( "org.opengion.fukurou.process.Process_TableFilter",mustProparty,usableProparty );
    129        }
    130
    131        /**
    132         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
    133         * 初期処理(ファイルオープン、ＤＢオープン等)に使用します。
    134         *
    135         * @og.rev 4.3.1.1 (2008/08/24) 置換関係対応
    136         * @og.rev 5.7.2.3 (2014/01/31) replace 置換の case処理。
    137         *
    138         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
    139         */
    140        public void init( final ParamProcess paramProcess ) {
    141                Argument arg = getArgument();
    142
    143                display = arg.getProparty( "display",display );
    144                debug   = arg.getProparty("debug",debug);                               // 5.7.3.0 (2014/02/07) デバッグ情報
    145
    146                HybsEntry[] entry = arg.getEntrys( PREFIX_KEY );
    147                for( int i=0; i<entry.length; i++ ) {
    148                        filter.add( FilterOperation.PREFIX, entry[i].getKey(), entry[i].getValue() );
    149                }
    150
    151                entry = arg.getEntrys( SUFFIX_KEY );
    152                for( int i=0; i<entry.length; i++ ) {
    153                        filter.add( FilterOperation.SUFFIX, entry[i].getKey(), entry[i].getValue() );
    154                }
    155
    156                entry = arg.getEntrys( INSTR_KEY );
    157                for( int i=0; i<entry.length; i++ ) {
    158                        filter.add( FilterOperation.INSTR, entry[i].getKey(), entry[i].getValue() );
    159                }
    160
    161                entry = arg.getEntrys( EQUALS_KEY );
    162                for( int i=0; i<entry.length; i++ ) {
    163                        filter.add( FilterOperation.EQUALS, entry[i].getKey(), entry[i].getValue() );
    164                }
    165
    166                entry = arg.getEntrys( MATCH_KEY );
    167                for( int i=0; i<entry.length; i++ ) {
    168                        filter.add( FilterOperation.MATCH, entry[i].getKey(), entry[i].getValue() );
    169                }
    170
    171                entry = arg.getEntrys( UNMATCH_KEY );
    172                for( int i=0; i<entry.length; i++ ) {
    173                        filter.add( FilterOperation.UNMATCH, entry[i].getKey(), entry[i].getValue() );
    174                }
    175
    176                HybsEntry[] cnstKey = arg.getEntrys( CONST_KEY );
    177                int csize       = cnstKey.length;
    178                cnstClm         = new String[csize];
    179                constVal        = new String[csize];
    180                for( int i=0; i<csize; i++ ) {
    181                        cnstClm[i]      = cnstKey[i].getKey();
    182                        constVal[i]     = cnstKey[i].getValue();
    183                }
    184
    185                // 4.3.1.1 (2008/08/24) replace 置換関係
    186                HybsEntry[] repKey = arg.getEntrys( REPLACE_KEY );
    187                int rsize       = repKey.length;
    188                repClm          = new String[rsize];
    189                repValFrom      = new String[rsize];
    190                repValTo        = new String[rsize];
    191                caseVals        = new String[rsize];                            // 5.7.2.3 (2014/01/31) replace 置換の case処理
    192                for( int i=0; i<rsize; i++ ) {
    193                        repClm[i]       = repKey[i].getKey();
    194                        String val      = repKey[i].getValue();                 // val は、YYYY⇒ZZZZ の形式
    195                        if( val != null ) {
    196                                int ad = val.indexOf( REP_SEP );                // REP_SEP は、'⇒'
    197                                if( ad >= 0 ) {
    198                                        repValFrom[i]   = val.substring( 0,ad );
    199                                        repValTo[i]             = val.substring( ad+1 );
    200                                }
    201                                else {
    202                                        // 5.7.2.3 (2014/01/31) replace 置換の case処理
    203                                        caseVals[i]             = val;                          // 5.7.2.3 (2014/01/31) replace 置換の case処理
    204                                }
    205                        }
    206                }
    207        }
    208
    209        /**
    210         * 引数の LineModel を処理するメソッドです。
    211         * 変換処理後の LineModel を返します。
    212         * 後続処理を行わない場合(データのフィルタリングを行う場合)は、
    213         * null データを返します。つまり、null データは、後続処理を行わない
    214         * フラグの代わりにも使用しています。
    215         * なお、変換処理後の LineModel と、オリジナルの LineModel が、
    216         * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。
    217         * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
    218         * 各処理ごとに自分でコピー(クローン)して下さい。
    219         *
    220         * @og.rev 4.3.1.1 (2008/08/24) 置換関係対応
    221         * @og.rev 5.7.2.3 (2014/01/31) replace 置換の case処理。
    222         *
    223         * @param   data        オリジナルのLineModel
    224         *
    225         * @return      処理変換後のLineModel
    226         */
    227        public LineModel action( final LineModel data ) {
    228                count++ ;
    229
    230        //      if( display ) { println( data.dataLine() ); }
    231
    232                if( !filter.filter( data ) ) {
    233                        return null;            // 不一致
    234                }
    235
    236                if( firstRow ) {
    237                        int csize       = cnstClm.length;
    238                        cnstClmNos      = new int[csize];
    239                        for( int i=0; i<csize; i++ ) {
    240                                cnstClmNos[i] = data.getColumnNo( cnstClm[i] );
    241                        }
    242
    243                        // 4.3.1.1 (2008/08/24) 置換関係対応
    244                        int rsize       = repClm.length;
    245                        repClmNos       = new int[rsize];
    246                        for( int i=0; i<rsize; i++ ) {
    247                                repClmNos[i] = data.getColumnNo( repClm[i] );
    248                        }
    249
    250                        firstRow = false;
    251                        if( display ) { println( data.nameLine() ); }           // 5.7.3.0 (2014/02/07) デバッグ情報
    252                }
    253
    254                if( debug ) { println( "Before:" + data.dataLine() ); }         // 5.1.2.0 (2010/01/01) display の条件変更
    255
    256                for( int i=0; i<cnstClm.length; i++ ) {
    257                        data.setValue( cnstClmNos[i],constVal[i] );
    258                }
    259
    260                // 4.3.1.1 (2008/08/24) 置換関係対応
    261                for( int i=0; i<repClm.length; i++ ) {
    262                        // 5.7.2.3 (2014/01/31) Object が null の時の処理がおかしかったので修正
    263                        String val = "";
    264                        Object obj = data.getValue( repClmNos[i] );
    265                        if( obj != null ) { val = String.valueOf( obj ); }
    266
    267                        if( caseVals[i] == null ) {             // 従来からのリプレース処理
    268                                val = val.replaceAll( repValFrom[i],repValTo[i] );
    269                        }
    270                        else {
    271                                // 5.7.2.3 (2014/01/31) replace 置換の case処理。
    272                                val = StringUtil.caseReplace( val , caseVals[i] , false );
    273                        }
    274                        data.setValue( repClmNos[i],val );
    275                }
    276
    277                if( debug ) { println( "After :" + data.dataLine() ); }         // 5.1.2.0 (2010/01/01) display の条件変更
    278                else if( display ) { println( data.dataLine() ); }              // 5.1.2.0 (2010/01/01) display の条件変更
    279                return data;
    280        }
    281
    282        /**
    283         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
    284         * 終了処理(ファイルクローズ、ＤＢクローズ等)に使用します。
    285         *
    286         * @og.rev 4.3.1.1 (2008/08/24) 置換関係対応
    287         * @og.rev 5.7.2.3 (2014/01/31) replace 置換の case処理。
    288         *
    289         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
    290         */
    291        public void end( final boolean isOK ) {
    292                cnstClm         = null;         // 固定値を設定するカラム名
    293                cnstClmNos      = null;         // 固定値を設定するカラム番号
    294                constVal        = null;         // カラム番号に対応した固定値
    295
    296                repClm          = null;         // 置換を設定するカラム名
    297                repClmNos       = null;         // 置換を設定するカラム番号
    298                repValFrom      = null;         // カラム番号に対応した置換元文字列
    299                repValTo        = null;         // カラム番号に対応した置換後文字列
    300                caseVals        = null;         // 5.7.2.3 (2014/01/31) replace 置換の case処理
    301        }
    302
    303        /**
    304         * プロセスの処理結果のレポート表現を返します。
    305         * 処理プログラム名、入力件数、出力件数などの情報です。
    306         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
    307         * 形式で出してください。
    308         *
    309         * @return   処理結果のレポート
    310         */
    311        public String report() {
    312                String report = "[" + getClass().getName() + "]" + CR
    313                                + TAB + "Model Filter : " + filter + CR
    314                                + TAB + "Output Count : " + count ;
    315
    316                return report ;
    317        }
    318
    319        /**
    320         * このクラスの使用方法を返します。
    321         *
    322         * @return      このクラスの使用方法
    323         */
    324        public String usage() {
    325                StringBuilder buf = new StringBuilder();
    326
    327                buf.append( "Process_TableFilter は、上流から受け取ったデータをフィルタする、"                                ).append( CR );
    328                buf.append( "ChainProcess インターフェースの実装クラスです。"                                                            ).append( CR );
    329                buf.append( CR );
    330                buf.append( "上流(プロセスチェインのデータは上流から下流へと渡されます。)から"                 ).append( CR );
    331                buf.append( "受け取ったLineModel を元に、項目のフィルタリングを行います。"                                       ).append( CR );
    332                buf.append( "条件が成立した場合は、下流に流します。複数の条件を指定できますが、"                 ).append( CR );
    333                buf.append( "すべて AND で判定されます。"                                                                                                  ).append( CR );
    334                buf.append( "(設定条件すべてを満たす場合のみ、下流にデータを流します。)"                                    ).append( CR );
    335                buf.append( CR );
    336                buf.append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR );
    337                buf.append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に"                ).append( CR );
    338                buf.append( "繋げてください。"                                                                                                                          ).append( CR );
    339                buf.append( CR ).append( CR );
    340                buf.append( getArgument().usage() ).append( CR );
    341
    342                return buf.toString();
    343        }
    344
    345        /**
    346         * このクラスは、main メソッドから実行できません。
    347         *
    348         * @param       args    コマンド引数配列
    349         */
    350        public static void main( final String[] args ) {
    351                LogWriter.log( new Process_TableFilter().usage() );
    352        }
    353}