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.html;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.db.DBTableModel;
020import org.opengion.hayabusa.db.DBColumn;
021import org.opengion.fukurou.util.StringUtil;
022import org.opengion.fukurou.util.Attributes;
023import org.opengion.fukurou.model.Formatter;
024
025import java.util.Map;
026import java.util.HashMap;
027import java.util.List;
028import java.util.ArrayList;
029import java.util.Arrays ;
030
031/**
032 * ViewMarker インターフェース の実装オブジェクトです。
033 * これを,共通のスーパークラスとして 各種表示フォーム(例:HTML表示等)に使います。
034 *
035 * このクラスは、setter/getterメソッドのデフォルト実装を提供しています。
036 * 各種表示フォームに対応したサブクラス上で, create() をオーバーライドして下さい。
037 *
038 * @og.group 画面表示
039 *
040 * @version  4.0
041 * @author   Kazuhiko Hasegawa
042 * @since    JDK5.0,
043 */
044public class ViewMarker_MARKER implements ViewMarker {
045
046        private List<Attributes>          markData        = null;         // 4.0.0 (2005/08/31)
047//      private Map<Integer,int[]>                formMap_c       = new HashMap<Integer,int[]>();   // 3.5.6.1 (2004/06/25)
048//      private Map<Integer,String[]>     formMap_f       = new HashMap<Integer,String[]>();        // 3.5.6.1 (2004/06/25)
049        private Map<Integer,Formatter>    formMap         = new HashMap<Integer,Formatter>();
050        private DBTableModel            table       = null;
051        private int[]                           markCmlNo       = null;
052        private int[]                           isMark          = null;
053        private static final int        MARK_NULL   = -1;       // マーカー未設定
054        private static final int        MARK_TRUE   = 1;        // マーカー作成
055        private static final int        MARK_FALSE  = 0;        // マーカー作成せず
056        // 3.5.2.0 (2003/10/20)
057        private String[]                        markKey         = null;
058        private String[]                        markLists       = null;
059        private String[]                        instrVals       = null;         // 3.8.8.1 (2007/01/06)
060        private int[]                           markListNo      = null;
061        private boolean[]                       useFmtDeco      = null;         // 5.6.3.0 (2013/04/01) [$XXXX],[#XXXX]機能を有効にするかどうか(true:有効)
062
063        private Map<Integer,List<Integer>>  clmMap  = new HashMap<Integer,List<Integer>>();     // 4.0.0 (2005/08/31)
064
065        /**
066         * 内容をクリア(初期化)します。
067         *
068         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
069         * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加
070         * @og.rev 3.5.6.1 (2004/06/25) formMap属性を追加
071         * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加
072         * @og.rev 5.6.3.0 (2013/04/01) useFmtDeco属性を追加
073         *
074         */
075        public void clear() {
076                markData        = null;         // 4.0.0 (2005/08/31)
077//              formMap_c       = new HashMap<Integer,int[]>();   // 3.5.6.1 (2004/06/25)
078//              formMap_f       = new HashMap<Integer,String[]>();        // 3.5.6.1 (2004/06/25)
079                formMap         = new HashMap<Integer,Formatter>();
080                table           = null;
081                isMark          = null;
082                markKey         = null;
083                markLists       = null;
084                instrVals       = null;         // 3.8.8.1 (2007/01/06)
085                markListNo      = null;
086                clmMap          = new HashMap<Integer,List<Integer>>();     // 4.0.0 (2005/08/31)
087                useFmtDeco      = null;         // 5.6.3.0 (2013/04/01) [$XXXX],[#XXXX]機能を有効にするかどうか(true:有効)
088        }
089
090        /**
091         * カラムに対するマーカーアトリビュートをセットします。
092         *
093         * @og.rev 3.1.0.0 (2003/03/20) Hashtable を使用している箇所で、非同期でも構わない箇所を、HashMap に置換え。
094         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
095         *
096         * @param       attri   アトリビュート
097         */
098        public void addAttribute( final Attributes attri ) {
099                if( markData == null ) { markData = new ArrayList<Attributes>(); }
100                markData.add( attri );
101        }
102
103        /**
104         * 内部に DBTableModel をセットします。
105         *
106         * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
107         * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加
108         * @og.rev 3.5.6.1 (2004/06/25) DBTableModel の再設定に対応。
109         * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加
110         * @og.rev 5.6.3.0 (2013/04/01) useFmtDeco属性を追加
111         *
112         * @param  tbl DBTableModelオブジェクト
113         */
114        public void setDBTableModel( final DBTableModel tbl ) {
115                table = tbl;
116                int count = markData.size();                    // 4.0.0 (2005/08/31)
117
118                isMark          = new int[ count ];
119                markKey         = new String[ count ];
120                markCmlNo       = new int[ count ];
121                markLists       = new String[ count ];
122                instrVals       = new String[ count ];
123                markListNo      = new int[ count ];
124                useFmtDeco      = new boolean[ count ];         // 5.6.3.0 (2013/04/01) [$XXXX],[#XXXX]機能を有効にするかどうか(true:有効)
125
126                Arrays.fill( isMark,MARK_FALSE );       // マーカーの表示可否
127                Arrays.fill( markCmlNo,-1 );            // マーカーの可否を判断するカラム番号
128                Arrays.fill( useFmtDeco,false );        // [$XXXX],[#XXXX]機能を無効にする。(互換性のため)
129
130                for( int intKey=0; intKey<count; intKey++ ) {
131                        Attributes attri = markData.get( intKey );
132
133                        String column = attri.get( "column" );
134                        int clm = table.getColumnNo( column );
135                        List<Integer> list = clmMap.get( clm );
136                        if( list == null ) { list = new ArrayList<Integer>(); }
137                        list.add( intKey );
138                        clmMap.put( clm,list );
139
140                        String body = attri.get( "body" );
141//                      makeFormat( intKey,body );
142                        Formatter formatter = new Formatter( table );
143                        formatter.setFormat( body );
144                        formMap.put( intKey, formatter );
145
146                        makeOnMarkFormat( intKey,attri );
147
148                        useFmtDeco[intKey] = "true".equalsIgnoreCase( attri.get( "useFormatDeco" ) );   // 5.6.3.0 (2013/04/01)
149                }
150        }
151
152        /**
153         * 指定の行列に対するマーカー文字列を返します。
154         * この値は,すでにマーカー文字列処理されている為, RendererValue で
155         * 変換する必要はありません。
156         * 引数の value はそのカラムの値として利用されます。この値は、修飾済みの
157         * 値を与えることが可能です。
158         *
159         * @og.rev 3.5.6.1 (2004/06/25) formMap属性を使用します。
160         * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加
161         * @og.rev 5.3.9.0 (2011/09/01) カラム名の先頭に'$'を付加した場合に、URLEncodeされた値を返すように対応
162         * @og.rev 5.6.3.0 (2013/04/01) useFmtDeco属性を追加([$XXXX],[#XXXX]機能を有効にするかどうか)
163         *
164         * @param   row 指定の行
165         * @param   clm 指定の列
166         * @param   value カラムの値
167         *
168         * @return  row行,colum列 のマーカー文字列
169         */
170        public String getMarkerString( final int row,final int clm,final String value ) {
171                int intKey = isOnMark(row,clm) ;
172                if( intKey < 0 ) { return value; }
173
174                Formatter formatter = formMap.get( intKey );
175                int[]    clmNo  = formatter.getClmNos();
176                String[] format = formatter.getFormat();
177
178                char[] types = formatter.getType();
179
180//              int[] clmNo = formMap_c.get( intKey );
181//              String[] format = formMap_f.get( intKey );
182
183                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_LARGE );
184                int j=0;
185                String val ;
186                for( ; j<clmNo.length; j++ ) {
187                        if( clm == clmNo[j] ) {
188                                val = value;
189                        }
190                        else {
191//                              val = table.getValue(row,clmNo[j]);
192                                val = formatter.getValue(row,clmNo[j]);
193                        }
194
195                        // 5.6.3.0 (2013/04/01) useFmtDeco属性を追加(trueの場合は、[$XXXX],[#XXXX]機能を有効にする)
196                        if( useFmtDeco[intKey] ) {
197                                DBColumn dbClm = table.getDBColumn( clmNo[j] );
198                                if( types[j] == '$' ) {
199                                        val = dbClm.getRendererValue( row,val );
200                                }
201                                else if( types[j] == '#' ) {
202                                        val = dbClm.getLabel();
203                                }
204                        }
205                        // false が以前と同じ処理(互換処理)ただし、view などのフォーマット処理とは異なる。
206                        else {
207                                // 5.3.9.0 (2011/09/01) カラム名の先頭に'$'を付加した場合URLEncodeされた値を返すように対応
208                                if( types[j] == '$' ) {
209                                        val = StringUtil.urlEncode( val );
210                                }
211                        }
212
213                        buf.append( format[j] );
214                        buf.append( val );
215                }
216                if( j < format.length ) { buf.append( format[j] ); }
217                String rtn = StringUtil.replace( buf.toString(),"{I}",String.valueOf( row ) );
218
219                // 3.8.8.1 (2007/01/06) instrVals属性を追加
220                if( instrVals[intKey] != null ) {
221                        String[] vals = StringUtil.csv2Array( instrVals[intKey],' ' );
222                        for( int i=0; i<vals.length; i++ ) {
223                                String css = "<span class=\"instr" + i + "\">" + vals[i] + "</span>";
224                                rtn = StringUtil.replace( rtn,vals[i],css );
225                        }
226                }
227                return rtn ;
228        }
229
230        /**
231         * マーカーフォーマットを作成します。
232         *
233         * @og.rev 3.5.6.1 (2004/06/25) formMap属性を使用します。
234         * @og.rev 3.8.8.1 (2007/01/06) countTokensの方法を見直します。
235         *
236         * @param       intKey  カラムキーのカラム番号
237         * @param       fmt     マーカーフォーマット
238         */
239//      private void makeFormat( final int intKey,final String fmt ) {
240//              int start = 0;
241//              int index = fmt.indexOf( '[' );
242//              List<String> clmNoList  = new ArrayList<String>();
243//              List<String> formatList = new ArrayList<String>();
244//              while( index >= 0 ) {
245//                      int end = fmt.indexOf( ']',index );
246//                      if( end < 0 ) {
247//                              String errMsg = "[ と ] との対応関係がずれています。" + HybsSystem.CR
248//                                                      + "format=[" + fmt + "] : index=" + index ;
249//                              throw new HybsSystemException( errMsg );
250//                      }
251//
252//                      // [ より前方の文字列は、formatList へ
253//                      if( index > 0 ) { formatList.add( fmt.substring( start,index ) ); }
254//                      else                    { formatList.add( "" );                                                   }
255//
256//                      // [XXXX] の XXXX部分を処理
257//                      clmNoList.add( fmt.substring( index+1,end ) );
258//
259//                      start = end+1 ;
260//                      index = fmt.indexOf( '[',start );
261//              }
262//              // ] の後方部分は、formatList へ
263//              formatList.add( fmt.substring( start ) );
264//
265//              String[] format = formatList.toArray( new String[formatList.size()] );
266//              String[] clmNm  = clmNoList.toArray( new String[clmNoList.size()] );
267//
268//              int size = clmNm.length ;
269//              int[] clmNo = new int[ size ];
270//              for( int i=0; i<size; i++ ) {
271//                      clmNo[i]  = table.getColumnNo( clmNm[i] );
272//              }
273//
274//              formMap_c.put( intKey, clmNo );
275//              formMap_f.put( intKey, format );
276//      }
277
278        /**
279         * マーカーを作成する/作成しないの指定カラム番号を求めます。
280         * また、int[列番号] isMark を初期化します。
281         *
282         * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加
283         * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加
284         *
285         * @param       intKey  カラムキーの番号
286         * @param       attri   アトリビュート
287         */
288        private void makeOnMarkFormat( final int intKey,final Attributes attri ) {
289                String onMark   = attri.get( "onMark" );
290                String markList = attri.get( "markList" );
291                instrVals[intKey] = attri.get( "instrVals" );   // 3.8.8.1 (2007/01/06)
292
293                // 3.5.6.0 (2004/06/18) nullポインタの参照外しバグの対応
294                // このロジックで値が設定済みであれば、以下の処理は不要である。
295                isMark[intKey] = MARK_NULL;
296                if( onMark == null || onMark.length() == 0 ||
297                        markList == null || markList.length() == 0 ) {
298                                isMark[intKey] = MARK_FALSE;
299                                return ;        // 3.5.6.0 (2004/06/18)
300                }
301                else if( onMark.charAt( 0 ) != '[' && markList.charAt( 0 ) != '[' ) {
302                        isMark[intKey] = ( markList.indexOf( onMark ) >= 0 ) ? MARK_TRUE : MARK_FALSE;
303                        return ;        // 3.5.6.0 (2004/06/18)
304                }
305
306                if( onMark.charAt( 0 ) == '[' ) {
307                        markCmlNo[intKey] = table.getColumnNo( onMark.substring( 1,onMark.length()-1 ));
308                }
309                else {
310                        markCmlNo[intKey]  = -1;
311                        markKey[intKey]    = onMark ;
312                }
313
314                if( markList.charAt( 0 ) == '[' ) {
315                        markListNo[intKey] = table.getColumnNo( markList.substring( 1,markList.length()-1 ));
316                }
317                else {
318                        markListNo[intKey] = -1;
319                        markLists[intKey] = markList;
320                }
321        }
322
323        /**
324         * マーカーを作成するかどうかを判断します。
325         * int[列番号] isMark には、 未設定 FALSE TRUE の状態を持っており、
326         * 列でマーカーを作成する状態が固定の場合(例えば,onMark属性がデフォルト "true" の場合)
327         * カラムに関係なく、同じ値を返すときに、使用します。
328         *
329         * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加
330         * @og.rev 3.5.4.0 (2003/11/25) onMark ,markList が null(またはゼロストリング)の場合は、false とする。
331         * @og.rev 4.0.0.0 (2005/08/31) 同一カラムの複数登録を許可します。
332         *
333         * @param       row     列番号
334         * @param       clm     カラムキーの名称
335         *
336         * @return      処理するリスト番号、-1 の場合は、該当なし
337         */
338        private int isOnMark( final int row,final int clm ) {
339                List<Integer> list = clmMap.get( clm );
340                if( list == null ) { return -1; }
341
342                for( int i=0; i<list.size(); i++ ) {
343                        int intKey = list.get( i );
344                        if( isMark[intKey] != MARK_NULL ) {
345                                if( isMark[intKey] == MARK_TRUE ) { return intKey; }
346                                else { continue; }
347                        }
348
349                        final String onMark ;
350                        if( markCmlNo[intKey] < 0 ) { onMark = markKey[intKey] ; }
351                        else { onMark = table.getValue( row,markCmlNo[intKey] ); }
352
353                        // 3.5.4.0 (2003/11/25) 追加
354                        if( onMark == null || onMark.length() == 0 ) { continue; }
355
356                        final String markList ;
357                        if( markListNo[intKey] < 0 ) { markList = markLists[intKey] ; }
358                        else { markList = table.getValue( row,markListNo[intKey] ); }
359
360                        // 3.5.4.0 (2003/11/25) 修正
361                        if( markList == null || markList.length() == 0 ) { continue; }
362
363                        if( markList.indexOf( onMark ) >= 0 ) { return intKey; }
364                }
365                return -1;
366        }
367        
368        /**
369         * マーカーされたカラム番号の配列を返します。
370         *
371         * これは特殊処理で、Edit機能で、カラム列をキャッシュしているときに、
372         * JSPのソース等の変更時に、変更が反映されない対応を行う場合、
373         * 通常の ViewFormのサブクラスから、Edit専用の ViewForm_HTMLSeqClmTable で
374         * 制御する場合、ViewMarkerのEditMarkerでは、通常非表示(検索の場合)ですが
375         * Editのポップアップ画面に、表示されてしまうのを避けるため、noDisplay に
376         * 強制的にするカラム番号が必要です。
377         * あくまで、暫定処置です。Edit機能を改修するときに、この機能は削除します。
378         *
379         * ※ この処理は、EditMarkerでのみ有効にします。
380         *
381         * @og.rev 5.8.6.0(2015/04/03) 6.0.3.0の移植
382         *
383         * @return  マーカーされたカラム番号の配列
384         */
385        public int[] getColumnNos() {
386                int[] rtn = new int[clmMap.size()];
387                int i=0;
388                for( final Integer obj : clmMap.keySet() ) {
389                        rtn[i++] = obj.intValue();
390                }
391
392                return rtn;
393        }
394}