    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.fukurou.util;
    017
    018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    020
    021/**
    022 * TagBuffer.java は、共通的に使用される 簡易タグ作成クラスです。
    023 * タグヘッダーは、オブジェクト作成時に登録する為、後の変更は出来ません。
    024 * BODY部や、属性は、一度登録すると書き換えできません。
    025 * また、同一属性チェックは行いません。登録した属性のキーや、値を取り出すことも出来ません。
    026 * あくまで、タグ文字列をストレートに作成することに特化したクラスです。
    027 * これらの高度な機能が必要であれば、{@link org.opengion.fukurou.util.Attributes } をご参照ください。
    028 *
    029 * makeTag() メソッドを呼び出した時点で、内部にタグ文字列をキャッシュします。
    030 * それ以降の変更は、出来ません。
    031 *
    032 * 内部的には、構造化されていません。あくまで、文字列連結(StringBuilder)の
    033 * 簡易クラスとして、使用してください。
    034 *
    035 * ※ 6.1.1.0 (2015/01/17)
    036 *    StringBuilder と同様、add メソッドの戻り値に、自分自身を戻します。
    037 *    これにより、連結処理できるようにします。
    038 *
    039 * @og.group ユーティリティ
    040 *
    041 * @version  4.0
    042 * @author       Kazuhiko Hasegawa
    043 * @since    JDK5.0,
    044 */
    045public final class TagBuffer {
    046        private final   StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    047        private final   String tagName  ;
    048        private                 String tagBody  ;
    049        private                 String cacheTag ;
    050
    051        /**
    052         * デフォルトコンストラクター
    053         * このコンストラクターを使用すると、タグ名を指定できないため、
    054         * 完成形のタグは、作成できません。属性リスト(key="value")の
    055         * 連結形式を得る場合にのみ、使用して下さい。
    056         *
    057         */
    058        public TagBuffer() {
    059                this( null );                           // 6.1.1.0 (2015/01/17) メソッドの集約
    060        }
    061
    062        /**
    063         * コンストラクター
    064         * タグ名に null を指定すると、デフォルトコンストラクターと同様に、
    065         * 完成形のタグは、作成できません。属性リスト(key="value")の
    066         * 連結形式を得る場合にのみ、タグ名 にnull を指定して下さい。
    067         *
    068         * @param       tagName  タグ名称
    069         */
    070        public TagBuffer( final String tagName ) {
    071                this.tagName = tagName;
    072                if( tagName != null ) {
    073                        buf.append( '<' ).append( tagName ).append( ' ' );              // 6.0.2.5 (2014/10/31) char を append する。
    074                }
    075        }
    076
    077        /**
    078         * タグの BODY部を追加登録します。
    079         *
    080         * すでに、登録済みの場合は、その値に、追記します。
    081         * そうでない場合(初めての場合)は、その値をセットします。
    082         * null を登録した場合は、何もしません。
    083         * ※ 従来あった #setBody(String)の様に、null を登録した場合、Body を
    084         * リセットする機能はありません。新しいオブジェクトを作成してください。
    085         *
    086         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
    087         *
    088         * @og.rev 6.1.1.0 (2015/01/17) #setBody(String) の代替えメソッドとして、新規追加
    089         *
    090         * @param       body     タグのBODY部に追加する文字列
    091         *
    092         * @return      自分自身
    093         * @og.rtnNotNull
    094         */
    095        public TagBuffer addBody( final String body ) {
    096                return addBody( body,true );
    097        }
    098
    099        /**
    100         * タグの BODY部を追加登録します。
    101         *
    102         * すでに、#setBody(String) で、登録済みの場合は、その値に、追記します。
    103         * そうでない場合は、#setBody(String) と同じ動作になります。
    104         * null を登録した場合は、何もしません。
    105         *
    106         * flag に、false を指定すると、追加登録しません。これは、if( flag ) { tagBuffer.addBody( body ); }
    107         * の様な判定処理を、tagBuffer.body( body,flag ); に簡素化するために使用できます。
    108         * TagBuffer は、StringBuilderの様に、メソッドの戻り値は、自分自身のオブジェクトなので、
    109         * 上記の flag を使用して、切れ目なく連結させることが可能になります。
    110         *
    111         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
    112         *
    113         * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。
    114         *
    115         * @param       body    タグのBODY部に追加する文字列
    116         * @param       flag    タグのBODY部に追加かどうかを決めるフラグ(true:追加する/false:何もしない)
    117         *
    118         * @return      自分自身
    119         * @og.rtnNotNull
    120         */
    121        public TagBuffer addBody( final String body,final boolean flag ) {
    122                if( cacheTag != null ) {
    123                        final String errMsg = "makeTag() 実行後に、BODY部の値を書き換えることは出来ません。" ;
    124                        throw new OgRuntimeException( errMsg );
    125                }
    126
    127                if( flag && body != null ) {
    128                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    129                        tagBody = tagBody == null ? body : tagBody + body;
    130                }
    131
    132                return this ;
    133        }
    134
    135        /**
    136         * タグの属性に、追加登録します。
    137         * 文字列として、key="value" の形式データを与えてください。連結時は、後ろに
    138         * スペースを一つ挟みますので、与える引数自体に連結用スペースを追加しておく
    139         * 必要はありません。
    140         * 通常は、tagName なしで作成した、Tagbuffer オブジェクトの makeTag() メソッドの
    141         * 返り値を渡しますが、Attributes.getAttribute() の返り値でも使用できます。
    142         * 引数が null の場合は、なにもしません。
    143         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
    144         *
    145         * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。
    146         *
    147         * @param       str     タグバッファーを追加します。
    148         *
    149         * @return      自分自身
    150         * @og.rtnNotNull
    151         * @see         #makeTag()
    152         * @see         Attributes#getAttribute()
    153         */
    154        public TagBuffer add( final String str ) {
    155                return add( null,str,true );                                    // 6.1.1.0 (2015/01/17) メソッドの集約
    156
    157        }
    158
    159        /**
    160         * タグの 属性(key="value")を登録します。
    161         * 属性は、key="value" の文字列を作成します。
    162         * key が null の場合は、値だけをそのまま連結します。
    163         * val が null の場合は、登録しません。
    164         *
    165         * value に、ダブルコーテーション(")が含まれている場合は、属性値をシングルコーテーション
    166         * でくくります。
    167         * 両方含まれている場合は、シングルコーテーションをエスケープ文字(&amp;#39;)に変換します。
    168         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
    169         *
    170         * @og.rev 3.8.6.1 (2006/10/20) シングルとダブルが混在する場合は、シングルをエスケープする
    171         * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。
    172         *
    173         * @param       key      属性キー (null の場合は、val属性値 をそのまま追加する)
    174         * @param       val      属性値 (null の場合は、なにもしない)
    175         *
    176         * @return      自分自身
    177         * @og.rtnNotNull
    178         */
    179        public TagBuffer add( final String key,final String val ) {
    180                return add( key,val,true );                                     // 6.1.1.0 (2015/01/17) メソッドの集約
    181        }
    182
    183        /**
    184         * タグの 属性(key="value")を登録します。
    185         * 属性は、key="value" の文字列を作成します。
    186         * key が null の場合は、値だけをそのまま連結します。
    187         * val が null の場合は、登録しません。
    188         *
    189         * flag に、false を指定すると、属性は追加しません。これは、if( flag ) { tagBuffer.add( key,val ); }
    190         * の様な判定処理を、tagBuffer.add( key,val,flag ); に簡素化するために使用できます。
    191         * TagBuffer は、StringBuilderの様に、メソッドの戻り値は、自分自身のオブジェクトなので、
    192         * 上記の flag を使用して、切れ目なく連結させることが可能になります。
    193         *
    194         * value に、ダブルコーテーション(")が含まれている場合は、属性値をシングルコーテーション
    195         * でくくります。
    196         * 両方含まれている場合は、シングルコーテーションをエスケープ文字に変換します。
    197         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
    198         *
    199         * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。
    200         *
    201         * @param       key             属性キー (null の場合は、val属性値 をそのまま追加する)
    202         * @param       val             属性値 (null の場合は、なにもしない)
    203         * @param       flag    属性を追加かどうかを決めるフラグ(true:追加する/false:何もしない)
    204         *
    205         * @return      自分自身
    206         * @og.rtnNotNull
    207         */
    208        public TagBuffer add( final String key,final String val,final boolean flag ) {
    209                if( cacheTag != null ) {
    210                        final String errMsg = "makeTag() 実行後に、属性を追加することは出来ません。" ;
    211                        throw new OgRuntimeException( errMsg );
    212                }
    213
    214                // 6.1.1.0 (2015/01/17) ロジックを見直します。
    215                if( flag && val != null ) {
    216                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
    217                        // 6.1.1.0 (2015/01/17) 元 add( String ) メソッドの処理
    218                        if( key == null ) {
    219                                buf.append( val ).append( ' ' );
    220                        }
    221                        else {
    222                                buf.append( key );
    223                                // ダブルコーテーションがある場合は、シングルコーテーションで囲う。
    224                                if( val.indexOf( '"' ) >= 0 ) {
    225                                        // ただし、シングルコーテーションを含む場合は、エスケープ文字に置き換える。
    226                                        buf.append( "='" )
    227                                                .append( val.indexOf( '\'' ) >= 0 ? val.replaceAll( "'","&#39;" ) : val )
    228                                                .append( "' " );
    229                                }
    230                                else {
    231                                        buf.append( "=\"" ).append( val ).append( "\" " );
    232                                }
    233                        }
    234                }
    235
    236                return this ;           // 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返す
    237        }
    238
    239        /**
    240         * タグの 整形された文字列を 作成します。
    241         * このメソッドは、tagName,body,属性より タグの完成形の文字列を作成します。
    242         * 作成された文字列は、内部でキャッシュされます。
    243         * BODY部を指定すると、&lt;tagName key="val" ･･･ &gt;body&lt;/tagName&gt; 形式になります。
    244         * BODY部が、null の場合は、 &lt;tagName key="val" ･･･ /&gt; 形式になります。
    245         * タグ名を指定しない(null)と、完成形のタグは、作成できません。
    246         * 属性リスト(key="value")の連結形式を返します。
    247         *
    248         * このメソッドの呼び出し以降では、setBody() も add() も実行できません。
    249         *
    250         * @return      整形された タグ文字列
    251         */
    252        public String makeTag() {
    253                if( cacheTag == null ) {
    254                        if( tagName != null ) {
    255                                if( tagBody == null ) {
    256                                        buf.append( "/>" );
    257                                }
    258                                else {
    259                                        buf.append( '>' ).append( tagBody )                                             // 6.0.2.5 (2014/10/31) char を append する。
    260                                           .append( "</" ).append( tagName ).append( '>' );             // 6.0.2.5 (2014/10/31) char を append する。
    261                                }
    262                        }
    263                        cacheTag = buf.toString();
    264                }
    265
    266                return cacheTag;
    267        }
    268
    269        /**
    270         * 行番号付きのタグの 整形された文字列を 作成します。
    271         * このメソッドは、makeTag() の結果より、[I] 文字列を引数の行番号と変換します。
    272         * これにより、動的に行番号つきの情報を属性に付与することが可能になります。
    273         * 内部にキャッシュされる値は、[I] 記号を変換していない状態の文字列です。
    274         * よって、このメソッドは、rowNo を変えて、何度でも呼び出すことは可能ですが、
    275         * setBody() や add() は実行できません。
    276         *
    277         * @param       rowNo   行番号([I] 文字列を変換します。)
    278         * @param       val     設定値([V] 文字列を変換します。)
    279         *
    280         * @return      整形された タグ文字列
    281         */
    282        public String makeTag( final int rowNo,final String val ) {
    283                String tag = makeTag();
    284                tag = StringUtil.replace( tag,"[I]",String.valueOf( rowNo ) );
    285                tag = StringUtil.replace( tag,"[V]",val );
    286
    287                return tag ;
    288        }
    289}