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.db;
017
018import java.util.HashMap;
019import java.util.HashSet;
020import java.util.Map;
021import java.util.Set;
022import java.util.regex.Pattern;
023
024import org.opengion.fukurou.util.StringUtil;
025
026/**
027 * エディット設定情報を管理するためのデータ管理クラスです。
028 * ここで管理される各パラメーターの意味は以下の通りです。
029 * (各インデックス番号は、内部的に管理されているインデックス番号を意味します)
030 *
031 * ・0:エディット名
032 *       このエディット設定オブジェクトの名称です。
033 * ・1:表示カラム
034 *       表示対象となるカラム一覧です。カンマ区切りで指定します。
035 *       この一覧には、非表示のカラムも合わせて管理され、非表示カラムについては、
036 *       カラム名の先頭に"!"をつけます。
037 *       例) AAA,!BBB,CCC ⇒ AAA,CCCの順に表示(BBBは非表示)
038 * ・2:集計カラム
039 *       各値をSUMする対象となるカラムです。(カンマ区切りで複数指定が可能)
040 *       ここで指定されたカラムは数値型である必要があります。
041 *       SQL構文における、SUM関数の引数として指定するカラムに相当します。
042 * ・3:グループカラム
043 *       集計カラムの各値をグルーピングするためのカラムです。(カンマ区切りで複数指定が可能)
044 *       SQL構文における、GROUP BYに指定するカラムに相当します。
045 * ・4:小計カラム
046 *       集計カラムの各値に対し、小計行を付加するためのブレイクキーを指定します。(カンマ区切りで複数指定が可能)
047 * ・5:合計カラム
048 *       集計カラムの各値に対し、合計行を付加するためのブレイクキーを指定します。(カンマ区切りで複数指定が可能)
049 * ・6:総合計フラグ
050 *       集計カラムの各値に対し、総合計行を付加するかどうかを指定します。(0以外:追加する 0:追加しない)
051 * ・7:表示順カラム
052 *       データの表示順をその順番にカンマ区切りで指定します。
053 *       カラム名の先頭に"!"をつけた場合は、そのカラムは降順で表示されます。
054 *       SQL構文における、orderby句に相当します。
055 * ・8:共通フラグ
056 *       このエディット設定オブジェクトが、共通(全ユーザー公開)エディットかどうかを
057 *       指定します。(0以外:共通 0:個人のみ)
058 *
059 * @og.rev 5.3.6.0 (2011/06/01) 新規追加
060 *
061 * @version  5.0
062 * @author   Hiroki Nakamura
063 * @since    JDK6.0,
064 */
065public class DBEditConfig {
066
067        private static final int EDIT_KEY_NAME          = 0;
068        private static final int EDIT_KEY_VIEW          = 1;
069        private static final int EDIT_KEY_SUM           = 2;
070        private static final int EDIT_KEY_GROUP         = 3;
071        private static final int EDIT_KEY_SUBTOTAL      = 4;
072        private static final int EDIT_KEY_TOTAL         = 5;
073        private static final int EDIT_KEY_GRANDTOTAL= 6;
074        private static final int EDIT_KEY_ORDERBY       = 7;
075        private static final int EDIT_KEY_COMMON        = 8;
076
077        private static final String[] EDIT_KEYS
078                = { "NAME", "VIEW", "SUM", "GROUP", "SUBTOTAL", "TOTAL", "GRANDTOTAL", "ORDERBY", "COMMON" };
079
080        private static final int EDIT_KEYS_LENGTH       = EDIT_KEYS.length;
081
082        private final String[] editVals = new String[EDIT_KEYS_LENGTH];
083
084        private int sumClmCount;
085        private int groupClmCount;
086        private int subTotalClmCount;
087        private int totalClmCount;
088        private final Map<String,String> orderMap = new HashMap<String,String>();
089        private String orderByDescClms;
090
091        /**
092         * コンストラクタ
093         *
094         * 空のエディット設定オブジェクトを構築します。
095         */
096        public DBEditConfig() {
097        }
098
099        /**
100         * コンストラクタ
101         *
102         * 各種パラメーターを指定してエディット設定オブジェクトを構築します。
103         *
104         * @param editName エディット名称
105         * @param viewClms 画面表示カラム
106         * @param sumClms 集計カラム
107         * @param groupClms グループカラム
108         * @param subTotalClms 小計カラム
109         * @param totalClms 合計カラム
110         * @param useGrandTotal 総合計行を追加するか(1:追加する 1以外:追加しない)
111         * @param orderByClms 表示順
112         * @param isCommon 共通エディットかどうか(1:共通 1以外:個人のみ)
113         */
114        public DBEditConfig( final String editName, final String viewClms
115                                                , final String sumClms, final String groupClms
116                                                , final String subTotalClms, final String totalClms
117                                                , final String useGrandTotal, final String orderByClms
118                                                , final String isCommon ) {
119
120                editVals[EDIT_KEY_NAME]                 = editName;
121                editVals[EDIT_KEY_VIEW]                 = viewClms;
122                editVals[EDIT_KEY_SUM]                  = sumClms;
123                editVals[EDIT_KEY_GROUP]                = groupClms;
124                editVals[EDIT_KEY_SUBTOTAL]             = subTotalClms;
125                editVals[EDIT_KEY_TOTAL]                = totalClms;
126                editVals[EDIT_KEY_GRANDTOTAL]   = useGrandTotal;
127                editVals[EDIT_KEY_ORDERBY]              = orderByClms;
128                editVals[EDIT_KEY_COMMON]               = isCommon;
129
130                init();
131        }
132
133        /**
134         * コンストラクタ
135         *
136         * 各種パラメーターを配列で指定してエディット設定オブジェクトを構築します。
137         * 各パラメータの配列インデックスは、{@link #getEditKeys(String,String)}で返される
138         * キー一覧の配列インデックスと一致します。
139         * 各パラメーターの意味については、クラスのJavadoc{@link DBEditConfig}を参照して下さい。
140         *
141         * @param editVals 設定値(配列)
142         * @see DBEditConfig
143         * @see #getEditKeys(String,String)
144         **/
145        public DBEditConfig( final String[] editVals ) {
146                System.arraycopy( editVals, 0, this.editVals, 0, editVals.length );
147                init();
148        }
149
150        /**
151         * エディット設定オブジェクト作成時の初期化処理です。
152         * コンストラクタの引数に基づき内部変数の初期設定を行います。
153         */
154        private void init() {
155                sumClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_SUM] ).length;
156                groupClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_GROUP] ).length;
157                subTotalClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_SUBTOTAL] ).length;
158                totalClmCount = StringUtil.csv2Array( editVals[EDIT_KEY_TOTAL] ).length;
159
160                if( editVals[EDIT_KEY_ORDERBY] != null ) {
161                        StringBuilder buf = new StringBuilder();
162                        String[] ary = StringUtil.csv2Array( editVals[EDIT_KEY_ORDERBY] );
163                        for( int i=0; i<ary.length ;i++ ) {
164                                String str = ary[i];
165                                if( str.startsWith( "!" ) ) {
166                                        str = str.substring( 1 );
167                                        if( buf.length() > 0 ) { buf.append( "," ); }
168                                        buf.append( str );
169                                }
170                                orderMap.put( str, String.valueOf( i+1 ) );
171                        }
172                        orderByDescClms = buf.toString();
173                }
174                else {
175                        orderByDescClms = null;
176                }
177        }
178
179        /**
180         * キー配列から画面IDとエディット名称のペアの一覧を取り出します。
181         *
182         * キー配列から"EDIT_NAME_"で始まるキーを検索し、"EDIT_NAME_(画面ID)_(エディット名)"
183         * と言う形式に基づき、画面IDとエディット名称のペアを取り出します。
184         *
185         * 画面IDとエディット名称は配列として保存され(インデックス番号は 0:画面ID、1:エディット名)
186         * その一覧がされらに配列に格納されて返されます。
187         *
188         * @param keys キー配列
189         *
190         * @return 画面IDとエディット名称のペアの一覧
191         */
192        public static String[][] getKeySet( final String[] keys ) {
193                if( keys == null || keys.length == 0 ) { return null; }
194
195                Set<String[]> keySet = new HashSet<String[]>();
196                for( String key : keys ) {
197                        if ( key != null && key.startsWith( "EDIT_NAME_" ) ) {
198                                String guikeyEditName = key.substring( ( "EDIT_NAME_" ).length() );
199                                if( guikeyEditName.indexOf( '_' ) >= 0 ) {
200                                        String guikey = guikeyEditName.substring( 0, guikeyEditName.indexOf( '_' ) );
201                                        String editName = guikeyEditName.substring( ( guikey + "_" ).length() );
202                                        if( guikey != null && guikey.length() > 0 && editName != null && editName.length() > 0 ) {
203                                                String[] set = { guikey, editName };
204                                                keySet.add( set );
205                                        }
206                                }
207                        }
208                }
209//              return keySet.toArray( new String[0][] );
210                return keySet.toArray( new String[keySet.size()][] );
211        }
212
213        /**
214         * 画面ID、エディット名をキーに、エディット設定オブジェクトの各設定値の
215         * 管理キーを指定します。
216         *
217         * エディット設定オブジェクトで管理される各キーに対して、
218         * "EDIT_[KEY]_(画面ID)_(エディット名)"というキーを生成し、これを配列にして返します。
219         *
220         * @param guikey 画面ID
221         * @param editName エディット名
222         *
223         * @return エディット設定を管理するためのキー一覧
224         */
225        public static String[] getEditKeys( final String guikey, final String editName ) {
226                String[] rtn = new String[EDIT_KEYS_LENGTH];
227                for( int i=0; i<EDIT_KEYS_LENGTH; i++ ) {
228                        rtn[i] = "EDIT_" + EDIT_KEYS[i] + "_" + guikey + "_" + editName;
229                }
230                return rtn;
231        }
232
233        /**
234         * エディット設定オブジェクトの各設定値を配列にして返します。
235         *
236         * 配列のインデックス番号は、{@link #getEditKeys(String,String)}で生成されるキーの
237         * インデックス番号と一致します。
238         *
239         * @return エディット設定オブジェクトの設定値一覧(配列)
240         * @see #getEditKeys(String,String)
241         */
242        public String[] getEditVals() {
243                String[] rtn = new String[editVals.length];
244                System.arraycopy( editVals, 0, rtn, 0, editVals.length );
245                return rtn;
246        }
247
248        /**
249         * エディット名を返します。
250         *
251         * @return エディット名
252         */
253        public String getEditName() {
254                return editVals[EDIT_KEY_NAME];
255        }
256
257        /**
258         * 表示カラム名の一覧をカンマ区切りで返します。
259         * 非表示カラムについては、カラム名の先頭に"!"をつけて返されます。
260         * 例) AAA,!BBB,CCC ⇒ AAA,CCCの順に表示(BBBは非表示)
261         *
262         * @return 表示カラム名一覧(カンマ区切り)
263         */
264        public String getViewClms() {
265                return editVals[EDIT_KEY_VIEW];
266        }
267
268        /**
269         * 表示カラム(CSV形式)をチェックし、変更があれば、反映したカラムを作成します。
270         *
271         * 表示カラムは、並び順や非表示マーカー(!)などが加味され、ユーザー、画面ごとに
272         * データベースに記録されています。JSPソースを修正した場合、データベースに
273         * 書き込まれた表示カラムは、反映されないため、カラム選択画面等に表示されません。
274         * そこで、オリジナルのカラムに追加された場合は、カラムを比較することで、
275         * 追加分のカラムを、非表示カラムとして、後ろに追記します。
276         * 削除された場合は、ViewForm で警告表示することで、ユーザーに変更を促します。
277         *
278         * @og.rev 5.8.2.0 (2014/12/05) JSP修正時の追加カラム対応
279         * @og.rev 5.9.32.0 (2018/05/02) spritView使用時の対応
280         *
281         * @param       orgClms         オリジナルのカラム(CSV形式)
282         *
283         * @return      変更後の表示カラム(CSV形式)
284         */
285        public String getViewClms( final String orgClms ) {
286                String viewClms = editVals[EDIT_KEY_VIEW];
287
288                if( orgClms == null || orgClms.isEmpty() ) { return viewClms; }         // orgClms がなければ、viewClms を返す。
289                // 基本的には、両者のカラムは、一致するはず。
290//              String[] vclms = viewClms.split( "," );         // 表示順、非表示処理を行ったカラム 5.9.32.0 DELETE
291                Pattern pattern1 = Pattern.compile("[,|]");     // spritView使用を考慮して、「 , or | 」で分割します。 5.9.32.0 ADD
292                String[] vclms = pattern1.split(viewClms);
293
294//              String[] fclms = orgClms.split( "," );          // 元々の表示可能カラムすべて(fullClms) 5.9.32.0 DELETE
295                String[] fclms = vclms.clone();                         // spritView使用時は未固定の列情報のみ渡されるため、vclmsの値から取得するように変更。5.9.32.0 ADD
296                for(int i=0; i<fclms.length; i++) {
297                        fclms[i] = fclms[i].charAt(0) == '!' ? fclms[i].substring(1) : fclms[i]; // 非表示の(!)は削除します。
298                }
299
300                // 表示可能カラムすべての Set を作成します。
301                Set<String> fset = new HashSet<String>();
302                for( int i=0; i<fclms.length; i++ ) {                // orgClms をSet に追加します。
303                        fset.add( fclms[i] );
304                }
305
306                // 非表示カラムの内、表示可能カラムに存在しない分だけの Set を作成します。
307                // また、表示可能カラムから、順番に、viewClms の値を削除していきます。
308                Set<String> vset = new HashSet<String>();
309                StringBuilder vbuf = new StringBuilder();       // 新しい viewClms 作成用
310                for( int i=0; i<vclms.length; i++ ) {                // viewClms をSet に追加します。
311                        String clm = vclms[i];
312                        if( clm == null || clm.isEmpty() ) { continue; }                        // 6.0.2.5 (2014/10/31) 潜在バグ? 先頭に"," が来るとアベンドする。
313                        clm = clm.charAt(0) == '!' ? clm.substring(1) : clm ;           // 非表示の (!) は削除します。
314                        if( fset.remove( clm ) ) {                              // fullSet にあれば、削除するとともに、新viewClmsを作成する。
315                                if( vbuf.length() > 0 ) { vbuf.append(','); }        // 最初以降は、カンマで連結する。              // 6.0.2.5 (2014/10/31) char を append する。
316                                vbuf.append( vclms[i] );                        // append するのは、(!) 付のカラム
317                        }
318                        else {
319                                vset.add( clm );                                        // fullSet になければ、viewSet に追加
320                        }
321                }
322
323                // この段階で、fset、vset ともに、それぞれ独自のカラムが残っている。
324                // どちらも、残っていなければ、正常なので、viewClms を返す。
325                if( vset.isEmpty() && fset.isEmpty() ) { return viewClms; }
326
327                // fullSet にカラムが残っていれば、非表示で、新viewClmsに、追加する。
328                if( !fset.isEmpty() ) {
329                        String[] defClms = fset.toArray( new String[fset.size()] );
330                        for( int i=0; i<defClms.length; i++ ) {
331                                if( vbuf.length() > 0 ) { vbuf.append(','); }        // 6.0.2.5 (2014/10/31) 最初以降は、カンマで連結する。
332                                vbuf.append('!').append( defClms[i] );                                          // 非表示カラムとして、後ろに追加する。
333                        }
334                }
335                viewClms = vbuf.toString();
336
337                editVals[EDIT_KEY_VIEW] = viewClms;
338                return viewClms;
339        }
340
341        /**
342         * 集計カラムの一覧をカンマ区切りで返します。
343         *
344         * @return 集計カラムの一覧(カンマ区切)
345         */
346        public String getSumClms() {
347                return editVals[EDIT_KEY_SUM];
348        }
349
350        /**
351         * 集計処理を行うかどうかを返します。
352         * これは、集計カラムが指定されているか、と同じ意味です。
353         *
354         * @return true:対象 false:非対象
355         */
356        public boolean useSum() {
357                return ( editVals[EDIT_KEY_SUM] != null && editVals[EDIT_KEY_SUM].length() > 0 );
358        }
359
360        /**
361         * 指定されたカラムが集計対象のカラムかどうかを返します。
362         *
363         * @param clm カラム
364         *
365         * @return true:対象 false:非対象
366         */
367        public boolean isSumClm( final String clm ) {
368                if( clm == null || editVals[EDIT_KEY_SUM] == null ) { return false; }
369                return ( ( ","+editVals[EDIT_KEY_SUM]+"," ).indexOf( ","+clm+"," ) >= 0 );
370        }
371
372        /**
373         * 集計カラムのカラム数を返します。
374         *
375         * @return 集計カラムのカラム数
376         */
377        public int getSumClmCount() {
378                return sumClmCount;
379        }
380
381        /**
382         * グループカラムの一覧をカンマ区切りで返します。
383         *
384         * @return グループカラムの一覧(カンマ区切)
385         */
386        public String getGroupClms() {
387                return editVals[EDIT_KEY_GROUP];
388        }
389
390        /**
391         * グループ処理を行うかどうかを返します。
392         * これは、グループカラムが指定されているか、と同じ意味です。
393         *
394         * @return true:対象 false:非対象
395         */
396        public boolean useGroup() {
397                return ( editVals[EDIT_KEY_GROUP] != null && editVals[EDIT_KEY_GROUP].length() > 0 );
398        }
399
400        /**
401         * 指定されたカラムがグループ対象のカラムかどうかを返します。
402         *
403         * @param clm カラム
404         *
405         * @return true:対象 false:非対象
406         */
407        public boolean isGroupClm( final String clm ) {
408                if( clm == null || editVals[EDIT_KEY_GROUP] == null ) { return false; }
409                return ( ( ","+editVals[EDIT_KEY_GROUP]+"," ).indexOf( ","+clm+"," ) >= 0 );
410        }
411
412        /**
413         * グループカラムのカラム数を返します。
414         *
415         * @return グループカラムのカラム数
416         */
417        public int getGroupClmCount() {
418                return groupClmCount;
419        }
420
421        /**
422         * 小計カラムの一覧をカンマ区切りで返します。
423         *
424         * @return 小計カラムの一覧(カンマ区切)
425         */
426        public String getSubTotalClms() {
427                return editVals[EDIT_KEY_SUBTOTAL];
428        }
429
430        /**
431         * 小計処理を行うかどうかを返します。
432         * これは、小計カラムが指定されているか、と同じ意味です。
433         *
434         * @return true:対象 false:非対象
435         */
436        public boolean useSubTotal() {
437                return ( editVals[EDIT_KEY_SUBTOTAL] != null && editVals[EDIT_KEY_SUBTOTAL].length() > 0 );
438        }
439
440        /**
441         * 指定されたカラムが小計対象のカラムかどうかを返します。
442         *
443         * @param clm カラム
444         *
445         * @return true:対象 false:非対象
446         */
447        public boolean isSubTotalClm( final String clm ) {
448                if( clm == null || editVals[EDIT_KEY_SUBTOTAL] == null ) { return false; }
449                return ( ( ","+editVals[EDIT_KEY_SUBTOTAL]+"," ).indexOf( ","+clm+"," ) >= 0 );
450        }
451
452        /**
453         * 小計カラムのカラム数を返します。
454         *
455         * @return グループカラムのカラム数
456         */
457        public int getSubTotalClmCount() {
458                return subTotalClmCount;
459        }
460
461        /**
462         * 合計カラムの一覧をカンマ区切りで返します。
463         *
464         * @return 合計カラムの一覧(カンマ区切)
465         */
466        public String getTotalClms() {
467                return editVals[EDIT_KEY_TOTAL];
468        }
469
470        /**
471         * 合計処理を行うかどうかを返します。
472         * これは、合計カラムが指定されているか、と同じ意味です。
473         *
474         * @return true:対象 false:非対象
475         */
476        public boolean useTotal() {
477                return ( editVals[EDIT_KEY_TOTAL] != null && editVals[EDIT_KEY_TOTAL].length() > 0 );
478        }
479
480        /**
481         * 指定されたカラムが合計対象のカラムかどうかを返します。
482         *
483         * @param clm カラム
484         *
485         * @return true:対象 false:非対象
486         */
487        public boolean isTotalClm( final String clm ) {
488                if( clm == null || editVals[EDIT_KEY_TOTAL] == null ) { return false; }
489                return ( ( ","+editVals[EDIT_KEY_TOTAL]+"," ).indexOf( ","+clm+"," ) >= 0 );
490        }
491
492        /**
493         * 合計カラムのカラム数を返します。
494         *
495         * @return グループカラムのカラム数
496         */
497        public int getTotalClmCount() {
498                return totalClmCount;
499        }
500
501        /**
502         * 総合計行を付加するかどうかを返します。
503         *
504         * @return true:対象 false:非対象
505         */
506        public boolean useGrandTotal() {
507                return StringUtil.nval( editVals[EDIT_KEY_GRANDTOTAL], false );
508        }
509
510        /**
511         * 表示順カラムをカンマ区切りで返します。
512         * カラムの並び順が表示順としての優先順になります。
513         * また、降順で表示するカラムについては、カラム名の先頭に"!"が付加されます。
514         *
515         * @return 標準順カラムの一覧(カンマ区切)
516         */
517        public String getOrderByClms() {
518                return editVals[EDIT_KEY_ORDERBY];
519        }
520
521        /**
522         * 指定されたカラムの表示順の優先番号を返します。
523         * 指定カラムが標準として指定されていない場合は、""(ゼロストリング)を返します。
524         *
525         * @param clm カラム
526         *
527         * @return 表示順の優先番号
528         */
529        public String getOrder( final String clm ) {
530                if( clm == null || editVals[EDIT_KEY_ORDERBY] == null ) { return ""; }
531
532                String rtn = orderMap.get( clm );
533                return ( rtn == null ? "" : rtn );
534        }
535
536        /**
537         * 指定されたカラムの表示順指定が降順であるかどうかを返します。
538         * 標準と指定されていない場合は、falseを返します。
539         *
540         * @param clm カラム
541         *
542         * @return true:降順 false:昇順
543         */
544        public boolean isOrderByDesc( final String clm ) {
545                if( clm == null || orderByDescClms == null ) { return false; }
546                return ( ( ","+orderByDescClms+"," ).indexOf( ","+clm+"," ) >= 0 );
547        }
548
549        /**
550         * 並び替え処理を行うかどうかを返します。
551         * これは、表示順カラムが指定されているか、と同じ意味です。
552         *
553         * @return true:対象 false:非対象
554         */
555        public boolean useOrderBy() {
556                return ( editVals[EDIT_KEY_ORDERBY] != null && editVals[EDIT_KEY_ORDERBY].length() > 0 );
557        }
558
559        /**
560         * このエディット設定オブジェクトが、共通(全ユーザー公開)エディットか
561         * どうかを返します。
562         *
563         * @return 0以外:共通 0:個人のみ
564         */
565        public boolean isCommon() {
566                return StringUtil.nval( editVals[EDIT_KEY_COMMON], false );
567        }
568}