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 * ② <og:tableFilter classId="UNIQ_NAME" > 056 * { 057 * NAME_IN : NAME_CLM ; 058 * NAME_OUT : RYAKU_CLM ; 059 * } 060 * </og:tableFilter> 061 * 062 * @og.rev 5.5.0.3(2012/03/12) 新規作成 063 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加 064 * 065 * @version 0.9.0 2000/10/17 066 * @author Kazuhiko Hasegawa 067 * @since JDK1.6, 068 */ 069public class TableFilter_UNIQ_NAME extends AbstractTableFilter { 070 /** このプログラムのVERSION文字列を設定します。 {@value} */ 071 private static final String VERSION = "6.5.0.1 (2016/10/21)" ; 072 073 /** 074 * デフォルトコンストラクター 075 * 076 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。 077 */ 078 public TableFilter_UNIQ_NAME() { 079 super(); 080 initSet( "NAME_IN" , "名前のオリジナルのカラムを指定(必須)" ); 081 initSet( "NAME_OUT" , "変換後の名前を設定するカラムを指定(必須)" ); 082 initSet( "GROUP_KEY", "名前をユニークにするグループを指定するカラム名を指定" ); 083 initSet( "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 * @og.rev 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 091 * @og.rev 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。 092 * 093 * @return 処理結果のDBTableModel 094 */ 095 public DBTableModel execute() { 096 final DBTableModel table = getDBTableModel(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 097 098 final String nameIn = getValue( "NAME_IN" ); 099 final String nameOut = getValue( "NAME_OUT" ); 100 101 final int inClmNo = table.getColumnNo( nameIn,false ); // 存在しない場合は、-1 を返す。 102 final int outClmNo = table.getColumnNo( nameOut,false ); 103 104 // 必須チェック 105 if( inClmNo < 0 || outClmNo <0 ) { 106 final String errMsg = "TableFilter_UNIQ_NAME では、NAME_IN、NAME_OUT 属性は必須です。" 107 + " NAME_IN =" + nameIn 108 + " NAME_OUT=" + nameOut ; 109 throw new HybsSystemException( errMsg ); 110 } 111 112 // 名前をユニーク化するためのマップ。キーがユニーク化する名前。値は、行番号 113 final Map<String,Integer> nameMap = new HashMap<>() ; 114 115 String[] data = null; 116 final int rowCnt = table.getRowCount(); 117 String preKey = null; 118 int row = 0; 119 120 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 121 final int type = StringUtil.nval( getValue( "TYPE" ), 1 ); // NULL の場合、初期値:1 122 final String groupKey = getValue( "GROUP_KEY" ); 123 final int grpClmNo = table.getColumnNo( groupKey,false ); 124 try { 125 for( row=0; row<rowCnt; row++ ) { 126 data = table.getValues( row ); 127 final String orgName = data[inClmNo]; // オリジナルの名称 128 129 if( grpClmNo >= 0 && preKey == null ) { 130 preKey = data[grpClmNo]; 131 if( preKey == null ) { preKey = ""; } 132 } 133 134 if( orgName != null && !orgName.isEmpty() ) { 135 final String[] seimei = makeSeiMei( orgName ); 136 final String sei = seimei[0]; 137 final String mei = seimei[1]; 138 139 if( nameMap.containsKey( sei ) ) { // 存在する場合。つまり重複 140 if( type == 1 ) { // 重複時に最初の分も(名)を付ける。 141 final Integer oldInt = nameMap.get( sei ); 142 if( oldInt != null ) { // null の場合は、先に重複処理済み 143 // オリジナルの姓名を取得 144 final String oldName = table.getValue( oldInt.intValue(),inClmNo ); 145 final String[] oldSeimei = makeSeiMei( oldName ); 146 147 final String key = makeKey( nameMap , oldSeimei[0] , oldSeimei[1] ); 148 nameMap.put( key, oldInt ); // 変更後のキーと値 149 nameMap.put( sei, null ); // 比較用に元のキーは残すが値は残さない。 150 } 151 } 152 153 final String key = makeKey( nameMap , sei , mei ); 154 nameMap.put( key, Integer.valueOf( row ) ); 155 } 156 else { 157 nameMap.put( sei, Integer.valueOf( row ) ); 158 } 159 } 160 161 // キーブレイクのチェック 162 if( grpClmNo >= 0 && !preKey.equals( data[grpClmNo] ) ) { 163 preKey = data[grpClmNo]; 164 if( preKey == null ) { preKey = ""; } 165 166 // 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 167 nameMap.forEach( (key,orow) -> { 168 if( orow != null ) { table.setValueAt( key , orow , outClmNo ); } 169 } ); 170 nameMap.clear(); 171 } 172 } 173 // 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 174 nameMap.forEach( (key,orow) -> { 175 if( orow != null ) { table.setValueAt( key , orow , outClmNo ); } 176 } ); 177 } 178 catch( final RuntimeException ex ) { 179 // 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。 180 makeErrorMessage( "TableFilter_UNIQ_NAME Error",ErrorMessage.NG ) 181 .addMessage( row+1,ErrorMessage.NG,"UNIQ_NAME" 182 , StringUtil.array2csv( data ) 183 , "NAME_IN=[" + nameIn + "]" 184 , "NAME_OUT=[" + nameOut + "]" 185 , "GROUP_KEY=[" + groupKey + "]" 186 , "TYPE=[" + type + "]" 187 ) 188 .addMessage( ex ); 189 } 190 return table; 191 } 192 193 /** 194 * オリジナルの姓名から、姓と名を分離します。 195 * 196 * @param orgName オリジナルのフルネーム 197 * 198 * @return 姓と名の分割結果 199 */ 200 private String[] makeSeiMei( final String orgName ) { 201 final 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}