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.taglib;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.hayabusa.resource.GUIInfo;
021import org.opengion.hayabusa.resource.UserInfo;
022import org.opengion.hayabusa.resource.FavoriteGUIData;
023import org.opengion.fukurou.util.XHTMLTag;
024import org.opengion.fukurou.util.ToString;                                                      // 6.1.1.0 (2015/01/17)
025import org.opengion.fukurou.util.StringUtil ;                                           // 6.2.2.0 (2015/03/27)
026
027import static org.opengion.fukurou.util.StringUtil.nval ;
028
029import jakarta.servlet.http.HttpServletRequest ;
030import jakarta.servlet.http.HttpServletResponse;
031
032import java.util.Enumeration;
033import java.util.ArrayList;
034import java.util.Map;
035import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
036
037import java.io.IOException;
038
039/**
040 * お気に入りリンクを作成するタグです(通常はresult.jspに組込み)。
041 *
042 * 画面検索時の引数やユーザー情報を元にして、ダイレクトに再検索できる
043 * リンクを作成します。このリンクをお気に入り等にセーブしておき、次回検索時にも
044 * 使用できるようにします。
045 *
046 * @og.formSample
047 * ●形式:<og:favoriteLink direct="true" target="_blank" method="GET" />
048 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します)
049 *
050 * ●Tag定義:
051 *   <og:favoriteLink
052 *       target             【TAG】リンク先の文書を表示させるフレーム、またはウィンドウの名前を指定します(初期値:_blank)
053 *       direct             【TAG】直接アクセスできる形式のリンクを作成するかどうか[true/false]を指定します(初期値:false)
054 *       method             【TAG】リンクの作成元となるメソッド[POST/GET/ALL]を指定します(初期値:GET)
055 *       href               【TAG】リンクを作成する時の転送先アドレスを指定します(初期値:index.jsp)
056 *       lbl                【TAG】ラベルリソースのラベルIDを指定します
057 *       linkCache          【TAG】リンクをキャッシュするかどうか[true/false]を指定します(初期値:false)
058 *       lastQueryRedirect  【TAG】キャッシュされたリンク先に転送するかどうか[true/false]を指定します(初期値:false)
059 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
060 *       useIcon            【TAG】お気に入りアイコンリンクを作成するかどうか[true/false]を指定します(初期値:false)
061 *   >   ... Body ...
062 *   </og:favoriteLink>
063 *
064 * ●使用例:
065 *    直接お気に入りのリンクを作成する。
066 *    デフォルト属性を使用(direct="true" target="_blank" method="GET")
067 *    <og:favoriteLink > Favorite Link </og:favoriteLink >
068 *
069 * @og.rev 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR)
070 * @og.group 画面部品
071 *
072 * @version  4.0
073 * @author       Kazuhiko Hasegawa
074 * @since    JDK5.0,
075 */
076public class FavoriteLinkTag extends CommonTagSupport {
077        /** このプログラムのVERSION文字列を設定します。   {@value} */
078        private static final String VERSION = "7.3.2.3 (2021/04/09)" ;
079        private static final long serialVersionUID = 732320210409L ;
080
081        // 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR)
082        private static final String JSP_IMG =  HybsSystem.sys( "JSP_IMG" ) + "/" ;
083
084        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
085        private static final Map<String,String> LAST_QUERY      = new ConcurrentHashMap<>();    // 6.4.1.1 (2016/01/16) lastQuery → LAST_QUERY  refactoring
086
087        private boolean         direct          ;                               // 3.0.0.0 初期値変更
088        private String          target          = "_blank";             // 3.6.0.7 (2004/11/06)
089        private String          method          = "GET";
090        private String          href            = "index.jsp";  // 3.8.8.2 (2007/01/26)
091        private boolean         linkCache       ;                               // 3.5.5.9 (2004/06/07)
092        private boolean         redirect        ;                               // 3.5.5.9 (2004/06/07)
093        private boolean         useIcon         ;                               // 4.1.1.0 (2008/02/13)
094
095        /**
096         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
097         *
098         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。ボディが存在する場合のみボディが呼ばれる対応。
099         * @og.rev 3.5.5.9 (2004/06/07) リンクキャッシュより最終画面を転送表示します。
100         * @og.rev 5.0.0.2 (2009/09/15) xss対応⇒チェックする
101         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
102         *
103         * @return      後続処理の指示
104         */
105        @Override
106        public int doStartTag() {
107                // 5.0.0.2 (2009/09/15) 強制False
108                // useXssCheck( false );
109
110                // 3.5.5.9 (2004/06/07) リンクキャッシュより最終画面を転送表示
111                if( redirect ) {
112                        // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。key,val は、not null 制限。
113                        final String uid = getUserInfo( "ID" );
114
115                        final String page = uid == null ? null : LAST_QUERY.get( uid ) ;
116
117                        if( page != null ) {
118                                final HttpServletResponse response = (HttpServletResponse)pageContext.getResponse();
119                                final String url = response.encodeRedirectURL( page );
120                                try {
121                                        response.sendRedirect( url );
122                                }
123                                catch( final IOException ex ) {
124                                        final String errMsg = "最終画面の転送時のリダイレクトエラー" + toString();
125                                        throw new HybsSystemException( errMsg,ex );
126                                }
127
128                                return SKIP_BODY ;
129                        }
130                }
131
132                set( "body",getMsglbl() );
133                return EVAL_BODY_BUFFERED ;     // Body を評価する。( extends BodyTagSupport 時)
134        }
135
136        /**
137         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
138         *
139         * @og.rev 3.1.1.0 (2003/03/28) ボディの内容を取得する処理を、CommonTagSupport で行う。
140         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。ボディが存在する場合のみボディが呼ばれる対応。
141         *
142         * @return      後続処理の指示(SKIP_BODY)
143         */
144        @Override
145        public int doAfterBody() {
146
147                final String label = getBodyString();
148
149                if( label != null && label.length() > 0 ) {
150                        set( "body",label );
151                }
152
153                return SKIP_BODY ;
154        }
155
156        /**
157         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
158         *
159         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
160         * @og.rev 4.1.1.0 (2008/0213) お気に入りアイコンリンクの作成
161         *
162         * @return      後続処理の指示
163         */
164        @Override
165        public int doEndTag() {
166                debugPrint();           // 4.0.0 (2005/02/28)
167
168                // method による条件判断。
169                final String requestMethod = ((HttpServletRequest)getRequest()).getMethod();
170                if( method != null &&
171                        ( "ALL".equalsIgnoreCase( method ) ||
172                          method.equalsIgnoreCase( requestMethod ) ) ) {
173                                // 4.1.1.0 (2008/0213) お気に入りアイコンリンクの作成
174                                if( useIcon ) {
175                                        jspPrint( getFavoriteIcon() );
176                                }
177                                else {
178                                        jspPrint( makeTag() );
179                                }
180                }
181
182                return EVAL_PAGE ;
183        }
184
185        /**
186         * タグリブオブジェクトをリリースします。
187         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
188         *
189         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
190         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
191         * @og.rev 3.5.5.9 (2004/06/07) linkCache , redirect 属性を追加
192         * @og.rev 3.6.0.7 (2004/11/06) target 属性の初期値を _new から _blank に変更
193         * @og.rev 3.8.8.2 (2007/01/26) href 属性を追加
194         *
195         */
196        @Override
197        protected void release2() {
198                super.release2();
199                direct          = false;
200                target          = "_blank";             // 3.6.0.7 (2004/11/06)
201                method          = "GET";
202                href            = "index.jsp";  // 3.8.8.2 (2007/01/26)
203                linkCache       = false;                // 3.5.5.9 (2004/06/07)
204                redirect        = false;                // 3.5.5.9 (2004/06/07)
205        }
206
207        /**
208         * お気に入りリンクを作成します。
209         *
210         * @og.rev 3.8.8.2 (2007/01/26) href 属性を追加
211         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
212         *
213         * @return      お気に入りリンクタグ文字列
214         * @og.rtnNotNull
215         */
216        protected String makeTag() {
217                final HttpServletRequest request = (HttpServletRequest)getRequest();
218
219                // ダイレクトリンク時の設定
220                // リンクの作成
221                // http://C00000:C00000@hn50g5:8080/dbdef/jsp/index.jsp?GAMENID=xxx&command=NEW&key=val
222                final StringBuilder link = new StringBuilder( BUFFER_MIDDLE )
223                        .append( "http://" )
224                        .append( request.getServerName() ).append( ':' )        // hn50g5:              // 6.0.2.5 (2014/10/31) char を append する。
225                        .append( request.getServerPort() )                                      // 8823
226                        .append( request.getContextPath() )                                     // /dbdef
227                        .append( "/jsp/" );
228
229                // 4.0.0 (2005/01/31)
230                final String direct_jsp  = getGUIInfoAttri( "ADDRESS" ) + "/" + (String)getSessionAttribute( "JSPID" );
231
232                final String hrefUrl = link.toString() ;
233                if( direct ) {
234                        set( "href" , hrefUrl + direct_jsp );
235                }
236                else {
237                        set( "href" , hrefUrl + href );         // 3.8.8.2 (2007/01/26)
238                }
239
240                set( "target" ,target );
241                set( "title"  ,getGUIInfoAttri( "LABEL" ) );    // 4.0.0 (2005/01/31)
242
243                final String urlEnc = makeUrlEncode( request );
244
245                // linkCache による、最終リクエストのアドレスを格納しておきます。
246                if( linkCache ) {
247                        // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。key,val は、not null 制限。
248                        final String uid = getUserInfo( "ID" );
249                        if( uid != null ) {
250                                final String val = hrefUrl + direct_jsp + "?" + urlEnc ;
251                                LAST_QUERY.put( uid,val );
252                        }
253                }
254
255                return XHTMLTag.link( getAttributes(),urlEnc ) ;
256        }
257
258        /**
259         * URLエンコードを行ったリンク情報を作成します。
260         *
261         * @og.rev 3.1.0.0 (2003/03/20) Vector を使用している箇所で、非同期でも構わない箇所を、ArrayList に置換え。
262         * @og.rev 3.1.2.0 (2003/04/07) 画面IDと実画面ディレクトリとの関連見直し(DIRの代りにGAMENIDを渡すように変更)
263         * @og.rev 3.1.8.0 (2003/05/16) 内部で作成している GAMENID 属性をセットしないように変更。
264         * @og.rev 4.0.0.0 (2007/07/11) submitTag で作成されたボタンパラメータは、次ページへ転送しません。
265         * @og.rev 5.0.0.2 (2009/09/15) xssチェック対応
266         *
267         * @param       request HttpServletRequestオブジェクト
268         *
269         * @return      URLエンコードを行ったリンク情報
270         * @og.rtnNotNull
271         */
272        private String makeUrlEncode( final HttpServletRequest request ) {
273                final Enumeration<?> enume = request.getParameterNames();               // 4.3.3.6 (2008/11/15) Generics警告対応
274                final ArrayList<String> v_keys = new ArrayList<>();
275                final ArrayList<String> v_vals = new ArrayList<>();
276                while( enume.hasMoreElements() ) {
277                        final String key = String.valueOf( enume.nextElement() );
278                        if( key != null && key.startsWith( HybsSystem.NO_XFER_KEY ) ) {
279                                continue;
280                        }
281
282                        // String val = getRequestValue( key );
283                        final String val = getRequestValue( key, false ); // 5.0.0.2 (2009/09/15)
284                        if( val != null && val.length() > 0 ) {
285                                v_keys.add( key );
286                                v_vals.add( val );
287                        }
288                }
289
290                final String[] keys = v_keys.toArray( new String[v_keys.size()] );
291                final String[] vals = v_vals.toArray( new String[v_vals.size()] );
292
293                return XHTMLTag.urlEncode( keys,vals );
294        }
295
296        /**
297         * お気に入りアイコンを取得します。
298         *
299         * @og.rev 4.1.1.0 (2008/02/12) 新規追加
300         * @og.rev 6.2.2.0 (2015/03/27) BRと\nを相互に変換する処理を追加
301         * @og.rev 6.2.2.3 (2015/04/10) htmlフィルターに、BR→改行処理機能を追加。
302         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
303         * @og.rev 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR)
304         *
305         * @return      お気に入りアイコンのリンクを返します。
306         * @og.rtnNotNull
307         */
308        private String getFavoriteIcon() {
309                final GUIInfo guiInfo           = ( GUIInfo )getSessionAttribute( HybsSystem.GUIINFO_KEY );
310                final String mscVal             = getRequestValue( "MSC" );
311                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
312
313                if( guiInfo.isRead() && mscVal.length() != 0 ) {
314                        final String gamenId            = guiInfo.getKey();
315                        final UserInfo userInfo = getUser();
316                        final Map<String,FavoriteGUIData> favoriteMap = userInfo.getFavoriteMap();
317
318                        // 6.3.9.0 (2015/11/06) Use one line for each declaration, it enhances code readability.(PMD)
319                        final String fgEdit;
320                        final String image;
321                        final String updateLabel;
322                        if( favoriteMap.containsKey( gamenId ) ){
323                                fgEdit = "0";
324                                image  = "FAV_MENU_OUT.gif";
325                                updateLabel  = getLabel( "DELETE" );
326                        }
327                        else {
328                                fgEdit = "1";
329                                image  = "FAV_MENU_IN.gif";
330                                updateLabel  = getLabel( "INSERT" );
331                        }
332
333                        final String geContextName = HybsSystem.sys( "GE_CONTEXT_NAME" );
334                        if( geContextName.isEmpty() ) {
335                                final String errMsg = "システムパラメータ GE_CONTEXT_NAME が設定されていません。";
336                                throw new HybsSystemException( errMsg );
337                        }
338
339                        buf.append( "<a href=\"/").append( geContextName )
340                                .append("/jsp/GE0014/update.jsp?fgEdit=" ).append( fgEdit )
341                                .append( "&command=NEW&SYSTEM_ID="      ).append( userInfo.getParameter( "SYSTEM_ID" ) )
342                                .append( "&GUIKEY="                     ).append( gamenId )
343                                .append( "&CONTEXT_URL="        ).append( userInfo.getParameter( "CONTEXT_URL" ) )
344                                // 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR)
345//                              .append( "\"><img src=\""       ).append( userInfo.getParameter( "JSP" ) )
346//                              .append( "/image/"                      ).append( image )
347                                .append( "\"><img src=\""       ).append( JSP_IMG ).append( image )
348                                .append( "\" title=\""          ).append( StringUtil.htmlFilter( getLabel( "FAVORITE_MENU" ),true ) )   // 6.2.2.0 (2015/03/27)
349                                .append( ' '                            ).append( updateLabel )
350//                              .append( "\" /></a>"            );
351                                .append( "\" ></a>"                     );                                              // 7.0.1.0 (2018/10/15)
352                }
353
354                return buf.toString();
355        }
356
357        /**
358         * 【TAG】直接アクセスできる形式のリンクを作成するかどうか[true/false]を指定します(初期値:false)。
359         *
360         * @og.tag
361         * trueは、指定の画面のフレームレベルでの指定になります。false は、トップフレームを
362         * 含む形なので、通常の登録画面と同じ形式になります。
363         * 初期値は、false(直接リンクしない)です。
364         *
365         * @param       flag 作成可否 [true:する/false:しない]
366         */
367        public void setDirect( final String flag ) {
368                direct = nval( getRequestParameter( flag ),direct );
369        }
370
371        /**
372         * 【TAG】リンク先の文書を表示させるフレーム、またはウィンドウの名前を指定します(初期値:_blank)。
373         *
374         * @og.tag リンク先のフレーム名(ターゲット属性)を設定します。
375         *
376         * @param       name リンク先の文書のフレーム名(ターゲット属性)
377         */
378        public void setTarget( final String name ) {
379                target = nval( getRequestParameter( name ),target );
380        }
381
382        /**
383         * 【TAG】リンクを作成する時の転送先アドレスを指定します(初期値:index.jsp)。
384         *
385         * @og.tag
386         * direct="false"(初期値)に使用されるリンクの転送先アドレスを指定します。
387         * 初期値は、index.jspです。
388         *
389         * @param       url 転送先アドレス
390         */
391        public void setHref( final String url ) {
392                href = nval( getRequestParameter( url ),href );
393        }
394
395        /**
396         * 【TAG】リンクの作成元となるメソッド[POST/GET/ALL]を指定します(初期値:GET)。
397         *
398         * @og.tag
399         * ここで指定したメソッドでリクエストされた場合のみ、リンクを作成します。
400         * 初期値は、GET です。(つまり GET のみリンクを作成します。)
401         * これは、POST では、引数が付かない為です。(実際は付ける事ができますが・・・)
402         * ALL は、どちらの場合でもリンクを作成しますが、先に述べたように POST では
403         * 引数がつきません。
404         * 初期値は、GETです。
405         *
406         * @param       flag リンクの作成元となるメソッド [POST/GET/ALL]
407         */
408        public void setMethod( final String flag ) {
409                method = nval( getRequestParameter( flag ),method );
410        }
411
412        /**
413         * 【TAG】リンクをキャッシュするかどうか[true/false]を指定します(初期値:false)。
414         *
415         * @og.tag
416         * この、favoriteLink で指定された画面を、ユーザー毎にキャッシュします。
417         * キャッシュされた画面は、lastQuery を指定することで、取り出し(転送)
418         * することが出来ます。
419         * ここでのキャッシュは、direct="true" を指定した場合のアドレスです。
420         * direct="false" は、index.jsp からのフレーム形式の為、メール等で
421         * 送り、後ほど再開するような使い方(または、ワークフロー的な使い方)
422         * を想定していますが、direct="true" により単独フレームデータを、
423         * リアルタイムで使用するケース(EXCELのWebクエリーなど)で使用します。
424         * 初期値は、falseです。
425         *
426         * @og.rev 3.5.5.9 (2004/06/07) リンクキャッシュより最終画面を転送表示します。
427         *
428         * @param       flag リンクキャッシュ [true:する/false:しない]
429         */
430        public void setLinkCache( final String flag ) {
431                linkCache = nval( getRequestParameter( flag ),linkCache );
432        }
433
434        /**
435         * 【TAG】キャッシュされたリンク先に転送するかどうか[true/false]を指定します(初期値:false)。
436         *
437         * @og.tag
438         * この、favoriteLink で指定された画面を、キャッシュします。
439         * キャッシュされた画面は、lastQuery を指定することで、取り出し(転送)
440         * することが出来ます。
441         * ここでのキャッシュは、direct="true" を指定した場合のアドレスです。
442         * direct="false" は、index.jsp からのフレーム形式の為、メール等で
443         * 送り、後ほど再開するような使い方(または、ワークフロー的な使い方)
444         * を想定していますが、direct="true" により単独フレームデータを、
445         * リアルタイムで使用するケース(EXCELのWebクエリーなど)で使用します。
446         * 初期値は、falseです。
447         *
448         * @og.rev 3.5.5.9 (2004/06/07) リンクキャッシュより最終画面を転送表示します。
449         *
450         * @param       flag リンク先転送 [true:する/false:しない]
451         */
452        public void setLastQueryRedirect( final String flag ) {
453                redirect = nval( getRequestParameter( flag ),redirect );
454        }
455
456        /**
457         * 【TAG】お気に入りアイコンリンクを作成するかどうか[true/false]を指定します(初期値:false)。
458         *
459         * @og.tag
460         * 初期値は、falseです。
461         *
462         * @og.rev 4.1.1.0 (2008/02/13) 新規追加。
463         *
464         * @param       flag お気に入りアイコンリンク作成 [true:する/false:しない]
465         */
466        public void setUseIcon( final String flag ) {
467                useIcon = nval( getRequestParameter( flag ),useIcon );
468        }
469
470        /**
471         * リンクキャッシュをクリアします。
472         * この時、poolされているオブジェクトは、ResourceManager#clear() メソッドを
473         * 呼び出します。
474         *
475         * @og.rev 3.5.5.9 (2004/06/07) 新規作成
476         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
477         */
478        public static void clear() {
479                        LAST_QUERY.clear();
480        }
481
482        /**
483         * このオブジェクトの文字列表現を返します。
484         * 基本的にデバッグ目的に使用します。
485         *
486         * @return このクラスの文字列表現
487         * @og.rtnNotNull
488         */
489        @Override
490        public String toString() {
491                return ToString.title( this.getClass().getName() )
492                                .println( "VERSION"             ,VERSION        )
493                                .println( "direct"              ,direct         )
494                                .println( "target"              ,target         )
495                                .println( "method"              ,method         )
496                                .println( "linkCache"   ,linkCache      )
497                                .println( "redirect"    ,redirect       )
498                                .println( "Other..."    ,getAttributes().getAttribute() )
499                                .fixForm().toString() ;
500        }
501}