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 = "7.0.1.0 (2018/10/15)" ;
078        private static final long serialVersionUID = 701020181015L ;
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                return XHTMLTag.link( getAttributes(),urlEnc ) ;
252        }
253
254        /**
255         * URLエンコードを行ったリンク情報を作成します。
256         *
257         * @og.rev 3.1.0.0 (2003/03/20) Vector を使用している箇所で、非同期でも構わない箇所を、ArrayList に置換え。
258         * @og.rev 3.1.2.0 (2003/04/07) 画面IDと実画面ディレクトリとの関連見直し(DIRの代りにGAMENIDを渡すように変更)
259         * @og.rev 3.1.8.0 (2003/05/16) 内部で作成している GAMENID 属性をセットしないように変更。
260         * @og.rev 4.0.0.0 (2007/07/11) submitTag で作成されたボタンパラメータは、次ページへ転送しません。
261         * @og.rev 5.0.0.2 (2009/09/15) xssチェック対応
262         *
263         * @param       request HttpServletRequestオブジェクト
264         *
265         * @return      URLエンコードを行ったリンク情報
266         * @og.rtnNotNull
267         */
268        private String makeUrlEncode( final HttpServletRequest request ) {
269                final Enumeration<?> enume = request.getParameterNames();               // 4.3.3.6 (2008/11/15) Generics警告対応
270                final ArrayList<String> v_keys = new ArrayList<>();
271                final ArrayList<String> v_vals = new ArrayList<>();
272                while( enume.hasMoreElements() ) {
273                        final String key = String.valueOf( enume.nextElement() );
274                        if( key != null && key.startsWith( HybsSystem.NO_XFER_KEY ) ) {
275                                continue;
276                        }
277
278                        // String val = getRequestValue( key );
279                        final String val = getRequestValue( key, false ); // 5.0.0.2 (2009/09/15)
280                        if( val != null && val.length() > 0 ) {
281                                v_keys.add( key );
282                                v_vals.add( val );
283                        }
284                }
285
286                final String[] keys = v_keys.toArray( new String[v_keys.size()] );
287                final String[] vals = v_vals.toArray( new String[v_vals.size()] );
288
289                return XHTMLTag.urlEncode( keys,vals );
290        }
291
292        /**
293         * お気に入りアイコンを取得します。
294         *
295         * @og.rev 4.1.1.0 (2008/02/12) 新規追加
296         * @og.rev 6.2.2.0 (2015/03/27) BRと\nを相互に変換する処理を追加
297         * @og.rev 6.2.2.3 (2015/04/10) htmlフィルターに、BR→改行処理機能を追加。
298         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
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                                .append( "\" ></a>"                     );                                              // 7.0.1.0 (2018/10/15)
345                }
346
347                return buf.toString();
348        }
349
350        /**
351         * 【TAG】直接アクセスできる形式のリンクを作成するかどうか[true/false]を指定します(初期値:false)。
352         *
353         * @og.tag
354         * trueは、指定の画面のフレームレベルでの指定になります。false は、トップフレームを
355         * 含む形なので、通常の登録画面と同じ形式になります。
356         * 初期値は、false(直接リンクしない)です。
357         *
358         * @param       flag 作成可否 [true:する/false:しない]
359         */
360        public void setDirect( final String flag ) {
361                direct = nval( getRequestParameter( flag ),direct );
362        }
363
364        /**
365         * 【TAG】リンク先の文書を表示させるフレーム、またはウィンドウの名前を指定します(初期値:_blank)。
366         *
367         * @og.tag リンク先のフレーム名(ターゲット属性)を設定します。
368         *
369         * @param       name リンク先の文書のフレーム名(ターゲット属性)
370         */
371        public void setTarget( final String name ) {
372                target = nval( getRequestParameter( name ),target );
373        }
374
375        /**
376         * 【TAG】リンクを作成する時の転送先アドレスを指定します(初期値:index.jsp)。
377         *
378         * @og.tag
379         * direct="false"(初期値)に使用されるリンクの転送先アドレスを指定します。
380         * 初期値は、index.jspです。
381         *
382         * @param       url 転送先アドレス
383         */
384        public void setHref( final String url ) {
385                href = nval( getRequestParameter( url ),href );
386        }
387
388        /**
389         * 【TAG】リンクの作成元となるメソッド[POST/GET/ALL]を指定します(初期値:GET)。
390         *
391         * @og.tag
392         * ここで指定したメソッドでリクエストされた場合のみ、リンクを作成します。
393         * 初期値は、GET です。(つまり GET のみリンクを作成します。)
394         * これは、POST では、引数が付かない為です。(実際は付ける事ができますが・・・)
395         * ALL は、どちらの場合でもリンクを作成しますが、先に述べたように POST では
396         * 引数がつきません。
397         * 初期値は、GETです。
398         *
399         * @param       flag リンクの作成元となるメソッド [POST/GET/ALL]
400         */
401        public void setMethod( final String flag ) {
402                method = nval( getRequestParameter( flag ),method );
403        }
404
405        /**
406         * 【TAG】リンクをキャッシュするかどうか[true/false]を指定します(初期値:false)。
407         *
408         * @og.tag
409         * この、favoriteLink で指定された画面を、ユーザー毎にキャッシュします。
410         * キャッシュされた画面は、lastQuery を指定することで、取り出し(転送)
411         * することが出来ます。
412         * ここでのキャッシュは、direct="true" を指定した場合のアドレスです。
413         * direct="false" は、index.jsp からのフレーム形式の為、メール等で
414         * 送り、後ほど再開するような使い方(または、ワークフロー的な使い方)
415         * を想定していますが、direct="true" により単独フレームデータを、
416         * リアルタイムで使用するケース(EXCELのWebクエリーなど)で使用します。
417         * 初期値は、falseです。
418         *
419         * @og.rev 3.5.5.9 (2004/06/07) リンクキャッシュより最終画面を転送表示します。
420         *
421         * @param       flag リンクキャッシュ [true:する/false:しない]
422         */
423        public void setLinkCache( final String flag ) {
424                linkCache = nval( getRequestParameter( flag ),linkCache );
425        }
426
427        /**
428         * 【TAG】キャッシュされたリンク先に転送するかどうか[true/false]を指定します(初期値:false)。
429         *
430         * @og.tag
431         * この、favoriteLink で指定された画面を、キャッシュします。
432         * キャッシュされた画面は、lastQuery を指定することで、取り出し(転送)
433         * することが出来ます。
434         * ここでのキャッシュは、direct="true" を指定した場合のアドレスです。
435         * direct="false" は、index.jsp からのフレーム形式の為、メール等で
436         * 送り、後ほど再開するような使い方(または、ワークフロー的な使い方)
437         * を想定していますが、direct="true" により単独フレームデータを、
438         * リアルタイムで使用するケース(EXCELのWebクエリーなど)で使用します。
439         * 初期値は、falseです。
440         *
441         * @og.rev 3.5.5.9 (2004/06/07) リンクキャッシュより最終画面を転送表示します。
442         *
443         * @param       flag リンク先転送 [true:する/false:しない]
444         */
445        public void setLastQueryRedirect( final String flag ) {
446                redirect = nval( getRequestParameter( flag ),redirect );
447        }
448
449        /**
450         * 【TAG】お気に入りアイコンリンクを作成するかどうか[true/false]を指定します(初期値:false)。
451         *
452         * @og.tag
453         * 初期値は、falseです。
454         *
455         * @og.rev 4.1.1.0 (2008/02/13) 新規追加。
456         *
457         * @param       flag お気に入りアイコンリンク作成 [true:する/false:しない]
458         */
459        public void setUseIcon( final String flag ) {
460                useIcon = nval( getRequestParameter( flag ),useIcon );
461        }
462
463        /**
464         * リンクキャッシュをクリアします。
465         * この時、poolされているオブジェクトは、ResourceManager#clear() メソッドを
466         * 呼び出します。
467         *
468         * @og.rev 3.5.5.9 (2004/06/07) 新規作成
469         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
470         */
471        public static void clear() {
472                        LAST_QUERY.clear();
473        }
474
475        /**
476         * このオブジェクトの文字列表現を返します。
477         * 基本的にデバッグ目的に使用します。
478         *
479         * @return このクラスの文字列表現
480         * @og.rtnNotNull
481         */
482        @Override
483        public String toString() {
484                return ToString.title( this.getClass().getName() )
485                                .println( "VERSION"             ,VERSION        )
486                                .println( "direct"              ,direct         )
487                                .println( "target"              ,target         )
488                                .println( "method"              ,method         )
489                                .println( "linkCache"   ,linkCache      )
490                                .println( "redirect"    ,redirect       )
491                                .println( "Other..."    ,getAttributes().getAttribute() )
492                                .fixForm().toString() ;
493        }
494}