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 * ●形式:<og:switch key="・・・" >
033 *            <og:case match="A" > ・・・ </og:case>
034 *            <og:case match="B" > ・・・ </og:case>
035 *            <og:case match="C" > ・・・ </og:case>
036 *            <og:case isDefault="true" > ・・・ </og:case>
037 *         </og:switch>
038 * ●body:あり(EVAL_BODY_INCLUDE:BODYをインクルードし、{@XXXX} は解析しません)
039 *
040 * ●Tag定義:
041 *   <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 *   >   ... Body ...
048 *   </og:case>
049 *
050 * ●使用例
051 *         <og:switch key="{@PARAM}" >
052 *            <og:case match="A" > 処理A </og:case>
053 *            <og:case match="B" > 処理B </og:case>
054 *            <og:case match="C" > 処理C </og:case>
055 *            <og:case isDefault="true" > 処理X </og:case>
056 *         </og:switch>
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 *         <og:switch key="{@PARAM}" >
072 *            <og:case match="[1]"   isBreak="false" > 処理A </og:case>
073 *            <og:case match="[12]"  isBreak="false" > 処理B </og:case>
074 *            <og:case match="[123]" isBreak="false" > 処理C </og:case>
075 *            <og:case isNull="true" > 処理X </og:case>
076 *            <og:case isDefault="true" > 処理Y </og:case>
077 *         </og:switch>
078 *
079 *          ・上記指定では、isBreak="false" が指定されているため、マッチした後も継続して判定処理が実施されます。
080 *          ・上記例で言うと、PARAM が "1" の場合、上記3つともにマッチします。
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
126                        if( rtn && isBreak ) { switchTag.setBreak(); }
127                }
128
129                if( rtn ) { return EVAL_BODY_INCLUDE ; }
130                else      { return SKIP_BODY ;         }
131        }
132
133        /**
134         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
135         *
136         * @return      後続処理の指示
137         */
138        @Override
139        public int doEndTag() {
140                debugPrint();           // 4.0.0 (2005/02/28)
141
142                return EVAL_PAGE ;
143        }
144
145        /**
146         * タグリブオブジェクトをリリースします。
147         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
148         *
149         */
150        @Override
151        protected void release2() {
152                super.release2();
153                caseMatch       = null ;        // switch_key.match( case_match ) でマッチした場合は、BODY が処理されます。
154                isDefault       = false;        // trueに設定すると、どのcase にもマッチしなかった場合に処理されます(初期値:false)
155                isBreak         = true ;        // 通常、最初にマッチした段階で、switch 処理を抜けます(初期値:true)
156                isNull          = false;        // trueに設定すると、switchのkeyが、null(またはゼロ文字列)の場合、マッチします(初期値:false)
157        }
158
159        /**
160         * 【TAG】switch-case のマッチ条件(case_match)を指定します。
161         *
162         * @og.tag
163         * switch_key.match( case_match ) でマッチした場合は、BODY が処理されます。
164         *
165         * @param       mkey マッチ条件
166         */
167        public void setMatch( final String mkey ) {
168                caseMatch = nval( getRequestParameter( mkey ),caseMatch );
169        }
170
171        /**
172         * 【TAG】どのcase にもマッチしなかった場合に処理する case 文かどうかを指定します(初期値:false)。
173         *
174         * @og.tag
175         * trueに設定すると、どのcase にもマッチしなかった場合に処理されます
176         * このタグそのものがなにも出力しません。(つまり条件から消えます。)
177         * BODY 部に記述することが可能です。その場合は、value 属性になにも設定できません。
178         * 初期値は、false:default case 文ではない です。
179         *
180         * @param       flag デフォルト処理 [true:する/false:しない]
181         */
182        public void setIsDefault( final String flag ) {
183                isDefault = nval( getRequestParameter( flag ),isDefault );
184        }
185
186        /**
187         * 【TAG】マッチした以降に継続処理を行わない(ブレイクする)かどうかを指定(初期値:true)。
188         *
189         * @og.tag
190         * true に設定すると、マッチした段階で、ブレイクします。
191         * Javaの switch-case 文は、最初に処理された case 以降を処理します。通常は、break を入れて
192         * 後続処理を実行されないようにしています。
193         * この、switch-case タグは、caseタグの break 属性で制御します。初期値が break="true" に、
194         * なっているため、通常は、どれかの case が実行された段階で、switchの処理は、終了されます。
195         * break="false" にすると、switchから抜けずに、継続して case との match を実行します。
196         * この場合、Java等と異なるのは、直後のcase文が実行されるのではなく、あくまで match 作業が
197         * 継続されるということです。つまり、複数の case で処理を行いたい場合は、break="false" に
198         * すると同時に、match 条件もそれぞれで、マッチするように設定する必要があります。
199         * 初期値は、true:ブレイクする です。
200         *
201         * @param       flag ブレイク可否 [true:する/false:しない(=継続処理する)]
202         */
203        public void setIsBreak( final String flag ) {
204                isBreak = nval( getRequestParameter( flag ),isBreak );
205        }
206
207        /**
208         * 【TAG】switchのkeyが、null(またはゼロ文字列)の場合、マッチするかどうか[true/false]を設定します(初期値:false)。
209         *
210         * @og.tag
211         * trueに設定すると、switchのkeyが、null(またはゼロ文字列)の場合、マッチします。
212         * 初期値のfalse にすると、キーが null でない場合だけ、マッチ処理を実行します。
213         * case の条件判定で使用されます。
214         * 初期値は、 false (null でない) です。
215         *
216         * @param       flag nullマッチ判定 [true:する/false:しない]
217         */
218        public void setIsNull( final String flag ) {
219                isNull = nval( getRequestParameter( flag ),isNull );
220        }
221
222        /**
223         * このオブジェクトの文字列表現を返します。
224         * 基本的にデバッグ目的に使用します。
225         *
226         * @return このクラスの文字列表現
227         * @og.rtnNotNull
228         */
229        @Override
230        public String toString() {
231                return ToString.title( this.getClass().getName() )
232                                .println( "VERSION"                     ,VERSION        )
233                                .println( "caseMatch"           ,caseMatch      )
234                                .println( "isDefault"           ,isDefault      )
235                                .println( "isBreak"                     ,isBreak        )
236                                .println( "isNull"                      ,isNull         )
237                                .println( "Other..."    ,getAttributes().getAttribute() )
238                                .fixForm().toString() ;
239        }
240}