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.table;
017
018import org.opengion.hayabusa.common.HybsSystemException;
019import org.opengion.hayabusa.db.AbstractTableFilter;
020import org.opengion.hayabusa.db.DBTableModel;
021
022import org.opengion.fukurou.util.ErrorMessage;
023import org.opengion.fukurou.util.StringUtil;
024
025import java.util.Map;
026import java.util.HashMap;
027
028/**
029 * TableFilter_UNIQ_NAME は、TableFilter インターフェースを継承した、DBTableModel 処理用の
030 * 実装クラスです。
031 *
032 * ここでは、NAME_IN,NAME_OUT,GROUP_KEY,TYPE より、名前を最短ユニーク化します。
033 * 例えば、氏名で、姓と名で、同姓の場合、姓(名)を付けることで、区別することができます。
034 *
035 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。
036 * 【パラメータ】
037 *  {
038 *       NAME_IN   : NAME_CLM  ;    名前のオリジナルのカラムを指定します。(必須)
039 *       NAME_OUT  : RYAKU_CLM ;    変換後の名前を設定するカラムを指定します。NAME_INと同じでもかまいません。(必須)
040 *       GROUP_KEY : CDBUMON   ;    名前をユニークにするグループを指定するカラム名を指定します。(選択)
041 *                                  グループはソートされている必要があります。内部的にはキーブレイク処理します。
042 *       TYPE      : [1 or 2]  ;    処理の方法を指定します(初期値:1)
043 *                                    1:姓と名を分けます。重複分は、姓(名) 形式で、ユニークになるまで、名の文字を増やします。
044 *                                    2:姓と名を分けます。1. と異なるのは、最初に見つけた重複分は、姓 のまま残します。
045 *  }
046 *
047 * 姓名の分離は、全角または、半角のスペースで区切ります。また、重複しなければ、(名)は付きません。
048 * TYPE="2" の方式は、慣例的に、昔からいる社員は苗字そのままで、後から入社した人にだけ(名)を
049 * 付けたい場合に、名前を入社年の古い順にならべることで、実現できます。
050 *
051 * @og.formSample
052 * ●形式:
053 *      @ <og:tableFilter classId="UNIQ_NAME" keys="NAME_IN,NAME_OUT" vals="NAME_CLM,RYAKU_CLM" />
054 *
055 *      A <og:tableFilter classId="UNIQ_NAME" >
056 *                 { NAME_IN  : NAME_CLM  ; }
057 *                 { NAME_OUT : RYAKU_CLM ; }
058 *         </og:tableFilter>
059 *
060 * @og.rev 5.5.0.3(2012/03/12) 新規作成
061 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加
062 *
063 * @version  0.9.0  2000/10/17
064 * @author   Kazuhiko Hasegawa
065 * @since    JDK1.6,
066 */
067public class TableFilter_UNIQ_NAME extends AbstractTableFilter {
068        //* このプログラムのVERSION文字列を設定します。   {@value} */
069        private static final String VERSION = "5.6.6.1 (2013/07/12)" ;
070
071        /**
072         * keys の整合性チェックを行うための初期設定を行います。
073         *
074         * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェック対応
075         *
076         * @param       keysMap keys の整合性チェックを行うための Map
077         */
078        @Override
079        protected void init( final Map<String,String> keysMap ) {
080                keysMap.put( "NAME_IN"  , "名前のオリジナルのカラムを指定(必須)"         );
081                keysMap.put( "NAME_OUT" , "変換後の名前を設定するカラムを指定(必須)"       );
082                keysMap.put( "GROUP_KEY", "名前をユニークにするグループを指定するカラム名を指定"  );
083                keysMap.put( "TYPE"             , "処理方法を指定(初期値:1) [1 or 2]"     );
084        }
085
086        /**
087         * DBTableModel処理を実行します。
088         *
089         * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更
090         *
091         * @return 処理結果のDBTableModel
092         */
093        public DBTableModel execute() {
094                DBTableModel table = getDBTableModel();         // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
095
096                String nameIn   = getValue( "NAME_IN" );
097                String nameOut  = getValue( "NAME_OUT" );
098                String groupKey = getValue( "GROUP_KEY" );
099
100                int type = StringUtil.nval( getValue( "TYPE" ), 1 );    // NULL の場合、初期値:1
101
102                int inClmNo  = table.getColumnNo( nameIn,false );               // 存在しない場合は、-1 を返す。
103                int outClmNo = table.getColumnNo( nameOut,false );
104                int grpClmNo = table.getColumnNo( groupKey,false );
105
106                // 必須チェック
107                if( inClmNo < 0 || outClmNo <0 ) {
108                        String errMsg = "TableFilter_UNIQ_NAME では、NAME_IN、NAME_OUT 属性は必須です。"
109                                                + " NAME_IN =" + nameIn
110                                                + " NAME_OUT=" + nameOut ;
111                        throw new HybsSystemException( errMsg );
112                }
113
114                // 名前をユニーク化するためのマップ。キーがユニーク化する名前。値は、行番号
115                Map<String,Integer> nameMap = new HashMap<String,Integer>() ;
116
117                String[] data  = null;
118                int rowCnt = table.getRowCount();
119                String preKey = null;
120                int row = 0;
121                try {
122                        for( row=0; row<rowCnt; row++ ) {
123                                data  = table.getValues( row );
124                                String orgName = data[inClmNo];         // オリジナルの名称
125
126                                if( grpClmNo >= 0 && preKey == null ) {
127                                        preKey = data[grpClmNo];
128                                        if( preKey == null ) { preKey = ""; }
129                                }
130
131                                if( orgName != null && !orgName.isEmpty() ) {
132                                        String[] seimei = makeSeiMei( orgName );
133                                        String sei = seimei[0];
134                                        String mei = seimei[1];
135
136                                        if( nameMap.containsKey( sei ) ) {      // 存在する場合。つまり重複
137                                                if( type == 1 ) {       // 重複時に最初の分も(名)を付ける。
138                                                        Integer oldInt = nameMap.get( sei );
139                                                        if( oldInt != null ) {  // null の場合は、先に重複処理済み
140                                                                // オリジナルの姓名を取得
141                                                                String oldName = table.getValue( oldInt.intValue(),inClmNo );
142                                                                String[] oldSeimei = makeSeiMei( oldName );
143
144                                                                String key = makeKey( nameMap , oldSeimei[0] , oldSeimei[1] );
145                                                                nameMap.put( key, oldInt );             // 変更後のキーと値
146                                                                nameMap.put( sei, null );               // 比較用に元のキーは残すが値は残さない。
147                                                        }
148                                                }
149
150                                                String key = makeKey( nameMap , sei , mei );
151                                                nameMap.put( key, Integer.valueOf( row ) );
152                                        }
153                                        else {
154                                                nameMap.put( sei, Integer.valueOf( row ) );
155                                        }
156                                }
157
158                                // キーブレイクのチェック
159                                if( grpClmNo >= 0 && !preKey.equals( data[grpClmNo] ) ) {
160                                        preKey = data[grpClmNo];
161                                        if( preKey == null ) { preKey = ""; }
162
163                                        for( Map.Entry<String,Integer> nameEnt : nameMap.entrySet() ){
164                                                Integer orgInt = nameEnt.getValue();
165                                                if( orgInt != null ) {
166                                                        int outrow = orgInt.intValue();
167                                                        table.setValueAt( nameEnt.getKey() , outrow , outClmNo );
168                                                }
169                                        }
170                                        nameMap.clear();
171                                }
172                        }
173                        for( Map.Entry<String,Integer> nameEnt : nameMap.entrySet() ){
174                                Integer orgInt = nameEnt.getValue();
175                                if( orgInt != null ) {
176                                        int outrow = orgInt.intValue();
177                                        table.setValueAt( nameEnt.getKey() , outrow , outClmNo );
178                                }
179                        }
180                }
181                catch( RuntimeException ex ) {
182                        ErrorMessage errMessage = makeErrorMessage( "TableFilter_UNIQ_NAME Error",ErrorMessage.NG );
183                        errMessage.addMessage( row+1,ErrorMessage.NG,ex.getMessage() );
184                        errMessage.addMessage( row+1,ErrorMessage.NG,StringUtil.array2csv( data ) );
185                        errMessage.addMessage( row+1,ErrorMessage.NG,"NAME_IN=[" + nameIn + "]" );
186                        errMessage.addMessage( row+1,ErrorMessage.NG,"NAME_OUT=[" + nameOut + "]" );
187                        errMessage.addMessage( row+1,ErrorMessage.NG,"GROUP_KEY=[" + groupKey + "]" );
188                        errMessage.addMessage( row+1,ErrorMessage.NG,"TYPE=[" + type + "]" );
189                }
190                return table;
191        }
192
193        /**
194         * オリジナルの姓名から、姓と名を分離します。
195         *
196         * @param       orgName オリジナルのフルネーム
197         *
198         * @return 姓と名の分割結果
199         */
200        private String[] makeSeiMei( final String orgName ) {
201                String[] seimei = new String[2];
202
203                int adrs = orgName.indexOf( ' ' );
204                if( adrs < 0 ) { adrs = orgName.indexOf( ' ' ); }
205                if( adrs < 0 ) {
206                        seimei[0] = orgName.trim();
207                        seimei[1] = "";
208                }
209                else {
210                        seimei[0] = orgName.substring( 0,adrs ).trim();
211                        seimei[1] = orgName.substring( adrs+1 ).trim();
212                }
213
214                return seimei ;
215        }
216
217        /**
218         * マップに存在しないキーを作成します。マップへの登録は、行いません。
219         *
220         * @param       nameMap 過去に登録されている名前キーのマップ
221         * @param       sei             オリジナルの姓
222         * @param       mei             オリジナルの名
223         *
224         * @return      新しく作成されたキー
225         */
226        private String makeKey( final Map<String,Integer> nameMap , final String sei , final String mei ) {
227                String key = null;
228
229                boolean flag = true;    // 未処理フラグ
230                for( int i=1; i<=mei.length(); i++ ) {
231                        key = sei + "(" + mei.substring(0,i) + ")" ;
232                        if( ! nameMap.containsKey( key ) ) {    // 存在しない
233                                flag = false;
234                                break;
235                        }
236                }
237                if( flag ) {
238                        for( int i=1; i<10; i++ ) {
239                                key = sei + mei + "("  + i  + ")" ;
240                                if( ! nameMap.containsKey( key ) ) {    // 存在しない
241                                        break;
242                                }
243                        }
244                }
245
246                return key ;
247        }
248}