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