    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.HybsSystemException;
    019import org.opengion.fukurou.util.ToString;                                              // 6.1.1.0 (2015/01/17)
    020import static org.opengion.fukurou.util.StringUtil.nval ;
    021
    022/**
    023 * case タグは、上位の switch タグの key とマッチした場合に、処理を行います。
    024 *
    025 * case タグの以下の属性について、説明します。
    026 *   match    ： switch_key.match( case_match ) でマッチした場合は、BODY が処理されます。
    027 *   isDefault： trueに設定すると、どのcase にもマッチしなかった場合に処理されます(初期値:false)
    028 *   isBreak  ： 通常、最初にマッチした段階で、switch 処理を抜けます(初期値:true)
    029 *   isNull   ： trueに設定すると、switchのkeyが、null(またはゼロ文字列)の場合、マッチします(初期値:false)
    030 *
    031 * @og.formSample
    032 * ●形式：&lt;og:switch key="･･･" &gt;
    033 *            &lt;og:case match="A" &gt; ･･･ &lt;/og:case&gt;
    034 *            &lt;og:case match="B" &gt; ･･･ &lt;/og:case&gt;
    035 *            &lt;og:case match="C" &gt; ･･･ &lt;/og:case&gt;
    036 *            &lt;og:case isDefault="true" &gt; ･･･ &lt;/og:case&gt;
    037 *         &lt;/og:switch&gt;
    038 * ●body：あり(EVAL_BODY_INCLUDE:BODYをインクルードし、{&#064;XXXX} は解析しません)
    039 *
    040 * ●Tag定義：
    041 *   &lt;og:case
    042 *       match              【TAG】switch-case のマッチ条件(case_match)を指定します
    043 *       isDefault          【TAG】どのcase にもマッチしなかった場合に処理する case 文かどうかを指定します(初期値:false)
    044 *       isBreak            【TAG】マッチした以降に継続処理を行わない(ブレイクする)かどうかを指定(初期値:true)
    045 *       isNull             【TAG】switchのkeyが、null(またはゼロ文字列)の場合、マッチするかどうか[true/false]を設定します(初期値:false)
    046 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    047 *   &gt;   ... Body ...
    048 *   &lt;/og:case&gt;
    049 *
    050 * ●使用例
    051 *         &lt;og:switch key="{&#064;PARAM}" &gt;
    052 *            &lt;og:case match="A" &gt; 処理A &lt;/og:case&gt;
    053 *            &lt;og:case match="B" &gt; 処理B &lt;/og:case&gt;
    054 *            &lt;og:case match="C" &gt; 処理C &lt;/og:case&gt;
    055 *            &lt;og:case isDefault="true" &gt; 処理X &lt;/og:case&gt;
    056 *         &lt;/og:switch&gt;
    057 *
    058 *          ・switch の key に対して、case の match に指定された値が、マッチ(switch_key.match( case_match ))
    059 *            した場合に、case の BODY 部分が処理されます。
    060 *            マッチしなければ、BODY部は、スキップされます。
    061 *          ・isDefault="true" の場合は、どれとも マッチしなかった場合に、実行されます。
    062 *          ・Javaの switch-case 文は、最初に処理された case 以降を処理します。通常は、break を入れて
    063 *            後続処理を実行されないようにしています。
    064 *            この、switch-case タグは、caseタグの isBreak 属性で制御します。初期値が isBreak="true" に、
    065 *            なっているため、通常は、どれかの case が実行された段階で、switchの処理は、終了されます。
    066 *            isBreak="false" にすると、switchから抜けずに、継続して case との match を実行します。
    067 *            この場合、Java等と異なるのは、直後のcase文が実行されるのではなく、あくまで match 作業が
    068 *            継続されるということです。つまり、複数の case で処理を行いたい場合は、isBreak="false" に
    069 *            すると同時に、match 条件もそれぞれで、マッチするように設定する必要があります。
    070 *
    071 *         &lt;og:switch key="{&#064;PARAM}" &gt;
    072 *            &lt;og:case match="[1]"   isBreak="false" &gt; 処理A &lt;/og:case&gt;
    073 *            &lt;og:case match="[12]"  isBreak="false" &gt; 処理B &lt;/og:case&gt;
    074 *            &lt;og:case match="[123]" isBreak="false" &gt; 処理C &lt;/og:case&gt;
    075 *            &lt;og:case isNull="true" &gt; 処理X &lt;/og:case&gt;
    076 *            &lt;og:case isDefault="true" &gt; 処理Y &lt;/og:case&gt;
    077 *         &lt;/og:switch&gt;
    078 *
    079 *          ・上記指定では、isBreak="false" が指定されているため、マッチした後も継続して判定処理が実施されます。
    080 *          ・上記例で言うと、PARAM が "1" の場合、上記３つともにマッチします。
    081 *          ・isNull="true" は、switch の key が null の場合に成立します。(null とは、ゼロ文字列も含む)
    082 *
    083 * @og.group 画面制御
    084 * @og.rev 5.2.3.0 (2010/12/01) 新規追加
    085 *
    086 * @version  5.2.3.0 (2010/12/01)
    087 * @author       Kazuhiko Hasegawa
    088 * @since    JDK1.6,
    089 */
    090public class CaseTag extends CommonTagSupport {
    091        /** このプログラムのVERSION文字列を設定します。   {@value} */
    092        private static final String VERSION = "6.4.2.0 (2016/01/29)" ;
    093        private static final long serialVersionUID = 642020160129L ;
    094
    095        private String  caseMatch       ;                       // switch_key.match( case_match ) でマッチした場合は、BODY が処理されます。
    096        private boolean isDefault       ;                       // trueに設定すると、どのcase にもマッチしなかった場合に処理されます(初期値:false)
    097        private boolean isBreak         = true ;        // 通常、最初にマッチした段階で、switch 処理を抜けます(初期値:true)
    098        private boolean isNull          ;                       // trueに設定すると、switchのkeyが、null(またはゼロ文字列)の場合、マッチします(初期値:false)
    099
    100        /**
    101         * デフォルトコンストラクター
    102         *
    103         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    104         */
    105        public CaseTag() { super(); }           // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    106
    107        /**
    108         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
    109         *
    110         * @return      後続処理の指示
    111         */
    112        @Override
    113        public int doStartTag() {
    114                final SwitchTag switchTag = (SwitchTag)findAncestorWithClass( this,SwitchTag.class );
    115                if( switchTag == null ) {
    116                        final String errMsg = "<b>" + getTagName() + "タグは、switch タグの内部におく必要があります。</b>";
    117                        throw new HybsSystemException( errMsg );
    118                }
    119
    120                boolean rtn = false;
    121                if( switchTag.isMatch() ) {
    122                        final String key = switchTag.getKey();
    123//                      rtn =   ( key != null && caseMatch != null && key.matches( caseMatch ) ) ||
    124//                                      ( isNull && key == null ) || isDefault ;
    125                        rtn =   key != null && caseMatch != null && key.matches( caseMatch )            // 6.9.7.0 (2018/05/14) PMD Useless parentheses.
    126                                ||      isNull && key == null
    127                                ||      isDefault ;
    128
    129                        if( rtn && isBreak ) { switchTag.setBreak(); }
    130                }
    131
    132                if( rtn ) { return EVAL_BODY_INCLUDE ; }
    133                else      { return SKIP_BODY ;         }
    134        }
    135
    136        /**
    137         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    138         *
    139         * @return      後続処理の指示
    140         */
    141        @Override
    142        public int doEndTag() {
    143                debugPrint();           // 4.0.0 (2005/02/28)
    144
    145                return EVAL_PAGE ;
    146        }
    147
    148        /**
    149         * タグリブオブジェクトをリリースします。
    150         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    151         *
    152         */
    153        @Override
    154        protected void release2() {
    155                super.release2();
    156                caseMatch       = null ;        // switch_key.match( case_match ) でマッチした場合は、BODY が処理されます。
    157                isDefault       = false;        // trueに設定すると、どのcase にもマッチしなかった場合に処理されます(初期値:false)
    158                isBreak         = true ;        // 通常、最初にマッチした段階で、switch 処理を抜けます(初期値:true)
    159                isNull          = false;        // trueに設定すると、switchのkeyが、null(またはゼロ文字列)の場合、マッチします(初期値:false)
    160        }
    161
    162        /**
    163         * 【TAG】switch-case のマッチ条件(case_match)を指定します。
    164         *
    165         * @og.tag
    166         * switch_key.match( case_match ) でマッチした場合は、BODY が処理されます。
    167         *
    168         * @param       mkey マッチ条件
    169         */
    170        public void setMatch( final String mkey ) {
    171                caseMatch = nval( getRequestParameter( mkey ),caseMatch );
    172        }
    173
    174        /**
    175         * 【TAG】どのcase にもマッチしなかった場合に処理する case 文かどうかを指定します(初期値:false)。
    176         *
    177         * @og.tag
    178         * trueに設定すると、どのcase にもマッチしなかった場合に処理されます
    179         * このタグそのものがなにも出力しません。(つまり条件から消えます。)
    180         * BODY 部に記述することが可能です。その場合は、value 属性になにも設定できません。
    181         * 初期値は、false：default case 文ではない です。
    182         *
    183         * @param       flag デフォルト処理 [true:する/false:しない]
    184         */
    185        public void setIsDefault( final String flag ) {
    186                isDefault = nval( getRequestParameter( flag ),isDefault );
    187        }
    188
    189        /**
    190         * 【TAG】マッチした以降に継続処理を行わない(ブレイクする)かどうかを指定(初期値:true)。
    191         *
    192         * @og.tag
    193         * true に設定すると、マッチした段階で、ブレイクします。
    194         * Javaの switch-case 文は、最初に処理された case 以降を処理します。通常は、break を入れて
    195         * 後続処理を実行されないようにしています。
    196         * この、switch-case タグは、caseタグの break 属性で制御します。初期値が break="true" に、
    197         * なっているため、通常は、どれかの case が実行された段階で、switchの処理は、終了されます。
    198         * break="false" にすると、switchから抜けずに、継続して case との match を実行します。
    199         * この場合、Java等と異なるのは、直後のcase文が実行されるのではなく、あくまで match 作業が
    200         * 継続されるということです。つまり、複数の case で処理を行いたい場合は、break="false" に
    201         * すると同時に、match 条件もそれぞれで、マッチするように設定する必要があります。
    202         * 初期値は、true：ブレイクする です。
    203         *
    204         * @param       flag ブレイク可否 [true:する/false:しない(=継続処理する)]
    205         */
    206        public void setIsBreak( final String flag ) {
    207                isBreak = nval( getRequestParameter( flag ),isBreak );
    208        }
    209
    210        /**
    211         * 【TAG】switchのkeyが、null(またはゼロ文字列)の場合、マッチするかどうか[true/false]を設定します(初期値:false)。
    212         *
    213         * @og.tag
    214         * trueに設定すると、switchのkeyが、null(またはゼロ文字列)の場合、マッチします。
    215         * 初期値のfalse にすると、キーが null でない場合だけ、マッチ処理を実行します。
    216         * case の条件判定で使用されます。
    217         * 初期値は、 false (null でない) です。
    218         *
    219         * @param       flag nullマッチ判定 [true:する/false:しない]
    220         */
    221        public void setIsNull( final String flag ) {
    222                isNull = nval( getRequestParameter( flag ),isNull );
    223        }
    224
    225        /**
    226         * このオブジェクトの文字列表現を返します。
    227         * 基本的にデバッグ目的に使用します。
    228         *
    229         * @return このクラスの文字列表現
    230         * @og.rtnNotNull
    231         */
    232        @Override
    233        public String toString() {
    234                return ToString.title( this.getClass().getName() )
    235                                .println( "VERSION"                     ,VERSION        )
    236                                .println( "caseMatch"           ,caseMatch      )
    237                                .println( "isDefault"           ,isDefault      )
    238                                .println( "isBreak"                     ,isBreak        )
    239                                .println( "isNull"                      ,isNull         )
    240                                .println( "Other..."    ,getAttributes().getAttribute() )
    241                                .fixForm().toString() ;
    242        }
    243}