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