    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 java.util.Locale;
    019import java.util.Set;
    020import java.util.HashSet;
    021import java.util.Arrays;
    022
    023import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;
    024
    025/**
    026 * TagBuffer.java は、共通的に使用される 簡易タグ作成クラスです。
    027 *
    028 * ここでは、４つの形式(TAG , CSS , JSON , ARRAY )をサポートします。
    029 *   TAG形式:
    030 *     BODYなし  &lt;tag key1="val1" key2="val2" ･･･/&gt;
    031 *     BODYあり  &lt;tag key1="val1" key2="val2" ･･･&gt;body&lt;/tag&gt;
    032 *
    033 *   CSS形式:
    034 *     { key1:val1; key2:val2; ･･･ }
    035 *
    036 *   JSON形式:
    037 *     文字  { "key1":"val1", "key2":"val2", ･･･ }
    038 *     数値  { "key1":num1  , "key2":num2  , ･･･ }
    039 *
    040 *   ARRAY形式:
    041 *     文字  [ "val1", "val2", ･･･ ]
    042 *     数値  [  num1 ,  num2 , ･･･ ]
    043 *
    044 * これらの形式は、#startTag( String ) , #startCss( String ) , #startJson() , #startArray()
    045 * メソッドで始まり、#make() で、完了します。
    046 * 完了とは、内部の StringBuilder に書き込まれることを意味しますので、再び、
    047 * startXXX()メソッドから始めることが可能です。
    048 * つまり、一つのインスタンスに、TAGやCSSをまとめて追記していくことが出来ます。
    049 * 最後に、#toString() メソッドで、内部の StringBuilder を文字列として返します。
    050 *
    051 * TagBuffer クラスの中身を全面的に見直しています。また、過去のTagBufferと互換性を取るための
    052 * メソッドも残していますが、順次、削除していく予定です。
    053 *
    054 * もっと高度な機能が必要であれば、{@link org.opengion.fukurou.util.Attributes } をご参照ください。
    055 *
    056 * @og.rev 7.0.1.0 (2018/10/15) 新規作成
    057 * @og.group ユーティリティ
    058 *
    059 * @version  7.0
    060 * @author       Kazuhiko Hasegawa
    061 * @since    JDK8.0,
    062 */
    063public final class TagBuffer {
    064        /** 処理中のデータ形式 */
    065        private enum DTYPE {            // 処理中のデータ形式
    066//      private static enum DTYPE {             // 処理中のデータ形式
    067                /** TAG形式 */            TAG ,
    068                /** CSS形式 */            CSS ,
    069                /** JSON形式 */           JSON ,
    070                /** ARRAY形式 */  ARY ;
    071        }
    072
    073        // 空要素となるタグ名
    074        private static final Set<String> YOSO_SET = new HashSet<>( Arrays.asList( "area","base","br","col","command","embed","hr","img","input"
    075                                                                                                                                                                ,"keygen","link","meta","param","source","track","wbr" ) );
    076
    077        private final   StringBuilder tagBuf  = new StringBuilder( BUFFER_MIDDLE );             // タグ構築用のバッファ(makeでクリア)
    078        private final   StringBuilder bodyBuf = new StringBuilder( BUFFER_MIDDLE );             // タグ構築用のバッファ(makeでクリア)
    079        private final   StringBuilder makeBuf = new StringBuilder( BUFFER_MIDDLE );             // make実行時のバッファ(makeでappend)
    080
    081        private         DTYPE   dType   = DTYPE.TAG     ;       // データ形式(初期値は、タグ)
    082        private         String  name    ;                               // タグ名か、セレクター
    083        private         char    kvsep   ;                               // key-val 分離文字( '=' か、':' )
    084        private         String  endStr  ;                               // 終端連結時文字( " " か、"; " か、", " など）
    085
    086        private         String cacheTag ;                               // TagBufferと互換性を取るための、makeTag 実行時のキャッシュ
    087        private         boolean isKaraTag ;                             // 空要素のタグ名かどうかの識別ﾌﾗｸﾞです。
    088
    089        /**
    090         * デフォルトコンストラクター
    091         *
    092         */
    093        public TagBuffer() { super(); }                 // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    094
    095        /**
    096         * タグ名を指定した、タグ構築用のコンストラクター
    097         *
    098         * これは、TagBuffer クラスとの互換性の為に用意されたコンストラクターです。
    099         *
    100         * @param       name    タグの名前
    101         */
    102        public TagBuffer( final String name ) {
    103                startTag( name );
    104        }
    105
    106        /**
    107         * TAG形式のデータ作成を宣言します。
    108         *
    109         *   TAG形式:
    110         *     BODYなし  &lt;tag key1="val1" key2="val2" ･･･/&gt;
    111         *     BODYあり  &lt;tag key1="val1" key2="val2" ･･･&gt;body&lt;/tag&gt;
    112         *
    113         * @param       name    タグの名前
    114         * @return      自分自身
    115         * @og.rtnNotNull
    116         */
    117        public TagBuffer startTag( final String name ) {
    118                return start( DTYPE.TAG , name , '='," " );
    119        }
    120
    121        /**
    122         * CSS形式のデータ作成を宣言します。
    123         *
    124         *   CSS形式:
    125         *     { key1:val1; key2:val2; ･･･ }
    126         *
    127         * @param       name    CSSのセレクター
    128         * @return      自分自身
    129         * @og.rtnNotNull
    130         */
    131        public TagBuffer startCss( final String name ) {
    132                return start( DTYPE.CSS , name , ':' , "; " );
    133        }
    134
    135        /**
    136         * JSON形式のデータ作成を宣言します。
    137         *
    138         *   JSON形式:
    139         *     文字  { "key1":"val1", "key2":"val2", ･･･ }
    140         *     数値  { "key1":num1  , "key2":num2  , ･･･ }
    141         *
    142         * @return      自分自身
    143         * @og.rtnNotNull
    144         */
    145        public TagBuffer startJson() {
    146                return start( DTYPE.JSON , null , ':' , ", " );         //name は使いません。
    147        }
    148
    149        /**
    150         * ARRAY形式のデータ作成を宣言します。
    151         *
    152         *   ARRAY形式:
    153         *     文字  [ "val1", "val2", ･･･ ]
    154         *     数値  [  num1 ,  num2 , ･･･ ]
    155         *
    156         * @return      自分自身
    157         * @og.rtnNotNull
    158         */
    159        public TagBuffer startArray() {
    160                return start( DTYPE.ARY , null , ' ' , ", " );          // name , kvsep は使いません。
    161        }
    162
    163        /**
    164         * 指定のデータ形式の開始を宣言します。
    165         *
    166         * このメソッドは、#startTag( String ) , #startCss( String ) , #startJson() , #startArray() から
    167         * 呼ばれる集約メソッドです。
    168         * 各処理に必要な情報は、呼び出し元で設定しています。
    169         *
    170         * @param       type     タグの種類を示す enum DTYPE { TAG , CSS , JSON , ARY ; }
    171         * @param       name     タグの名前か、CSSのセレクター
    172         * @param       kvsep    key-val 分離文字( '=' か、':' )
    173         * @param       endStr   終端連結時文字( " " か、"; " か、", " など）
    174         * @return      自分自身
    175         * @og.rtnNotNull
    176         */
    177        private TagBuffer start( final DTYPE type , final String name , final char kvsep , final String endStr ) {
    178                this.dType      = type;
    179                this.name       = name;
    180                this.kvsep      = kvsep;
    181                this.endStr     = endStr;
    182
    183                // 空要素かどうかを判定します。
    184                isKaraTag       = name != null && YOSO_SET.contains( name.toLowerCase( Locale.JAPAN ) );
    185
    186                return this;
    187        }
    188
    189        /**
    190         * データ本体のバッファに直接値を追加します。
    191         *
    192         * 指定の可変長文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    193         * データ形式以外の文字列を前後、間に追記したい場合に、都度呼びます。
    194         *
    195         * 引数が null や、空文字列の場合は、何もしません。
    196         *
    197         * @param       vals    データ本体のバッファに直接追加する可変長文字列
    198         * @return      自分自身
    199         * @see         #addBuffer( String , boolean )
    200         * @og.rtnNotNull
    201         */
    202        public TagBuffer addBuffer( final String... vals ) {
    203                if( vals != null ) {
    204                        for( final String val : vals ) {
    205                                if( StringUtil.isNotNull( val ) ) {
    206                                        makeBuf.append( val ).append( ' ' );
    207                                }
    208                        }
    209                }
    210
    211                return this ;
    212        }
    213
    214        /**
    215         * データ本体のバッファに直接値を追加します。
    216         *
    217         * 指定の文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    218         * データ形式以外の文字列を前後、間に追記したい場合に、都度呼びます。
    219         *
    220         * 引数が null や、空文字列の場合は、何もしません。
    221         *
    222         * @param       val     データ本体のバッファに直接追加する文字列
    223         * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    224         * @return      自分自身
    225         * @see         #addBuffer( String... )
    226         * @og.rtnNotNull
    227         */
    228        public TagBuffer addBuffer( final String val , final boolean isUse ) {
    229                return isUse ? addBuffer( val ) : this ;
    230        }
    231
    232//      /**
    233//       * データ本体のバッファに直接値を追加します。
    234//       *
    235//       * 指定の可変長文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    236//       * データ形式以外の文字列を前後、間に追記したい場合に、都度呼びます。
    237//       *
    238//       * 引数が null や、空文字列の場合は、何もしません。
    239//       *
    240//       * @param       vals    データ本体のバッファに直接追加する文字列配列
    241//       * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    242//       * @return      自分自身
    243//       * @see         #addBuffer( String... )
    244//       * @og.rtnNotNull
    245//       */
    246//      public TagBuffer addBuffer( final String[] vals , final boolean isUse ) {
    247//              if( isUse && vals != null ) {
    248//                      for( final String val : vals ) {
    249//                              if( StringUtil.isNotNull( val ) ) {
    250//                                      makeBuf.append( val ).append( ' ' );
    251//                              }
    252//                      }
    253//              }
    254//
    255//              return this ;
    256//      }
    257
    258        /**
    259         * データの登録エリアのバッファに直接値を追加します。
    260         *
    261         * これは、TagBuffer クラスとの互換性の為に用意されたメソッドです。
    262         * 近い将来、削除します。
    263         *
    264         * @param       val     データの登録エリアのバッファに直接追加する文字列
    265         * @return      自分自身
    266         * @see         #addBody( String... )
    267         * @og.rtnNotNull
    268         */
    269        public TagBuffer add( final String val ) {
    270                return addOptions( val );
    271        }
    272
    273        /**
    274         * データの登録エリアのバッファに直接値を追加します。
    275         *
    276         * 指定の可変長文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    277         * これは、データ生成中のバッファに対して、追加を行います。
    278         * 例えば、外部で、タグやCSSのデータ形式を作成済みで、その文字列を追記するなどです。
    279         *
    280         * 引数が null や、空文字列の場合は、何もしません。
    281         *
    282         * @param       vals    データの登録エリアのバッファに直接追加する可変長文字列
    283         * @return      自分自身
    284         * @see         #addOptions( String... )
    285         * @og.rtnNotNull
    286         */
    287        public TagBuffer addOptions( final String... vals ) {
    288                if( vals != null ) {
    289                        for( final String val : vals ) {
    290                                if( StringUtil.isNotNull( val ) ) {
    291                                        tagBuf.append( val ).append( ' ' );
    292                                }
    293                        }
    294                }
    295
    296                return this ;
    297        }
    298
    299        /**
    300         * データの登録エリアのバッファに直接値を追加します。
    301         *
    302         * 指定の文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    303         * これは、データ生成中のバッファに対して、追加を行います。
    304         * 例えば、外部で、タグやCSSのデータ形式を作成済みで、その文字列を追記するなどです。
    305         *
    306         * 引数が null や、空文字列の場合は、何もしません。
    307         *
    308         * @param       val     データの登録エリアのバッファに直接追加する文字列
    309         * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    310         * @return      自分自身
    311         * @see         #addOptions( String... )
    312         * @og.rtnNotNull
    313         */
    314        public TagBuffer addOptions( final String val , final boolean isUse ) {
    315                return isUse ? addOptions( val ) : this ;
    316        }
    317
    318//      /**
    319//       * データの登録エリアのバッファに直接値を追加します。
    320//       *
    321//       * 指定の可変長文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    322//       * これは、データ生成中のバッファに対して、追加を行います。
    323//       * 例えば、外部で、タグやCSSのデータ形式を作成済みで、その文字列を追記するなどです。
    324//       *
    325//       * 引数が null や、空文字列の場合は、何もしません。
    326//       *
    327//       * @param       vals    データの登録エリアのバッファに直接追加する文字列配列
    328//       * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    329//       * @return      自分自身
    330//       * @see         #addOptions( String... )
    331//       * @og.rtnNotNull
    332//       */
    333//      public TagBuffer addOptions( final String[] vals , final boolean isUse ) {
    334//              if( isUse && vals != null ) {
    335//                      for( final String val : vals ) {
    336//                              if( StringUtil.isNotNull( val ) ) {
    337//                                      tagBuf.append( val ).append( ' ' );
    338//                              }
    339//                      }
    340//              }
    341//
    342//              return this ;
    343//      }
    344
    345        /**
    346         * データの BODY部のバッファに直接値を追加します（データ形式 TAGのみ有効）。
    347         *
    348         * 指定の可変長文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    349         * これは、データの BODY部のバッファに対して、追加を行います。
    350         * BODY部は、タグのbody として使用されているデータで、BODY有り無しの判定は、
    351         * このデータが登録されたかどうかで判定しています。
    352         *
    353         * 引数が null の場合は、何もしません。
    354         *
    355         * @param       vals    データのBODY部のバッファに直接追加する可変長文字列
    356         * @return      自分自身
    357         * @see         #addOptions( String... )
    358         * @og.rtnNotNull
    359         */
    360        public TagBuffer addBody( final String... vals ) {
    361                if( dType == DTYPE.TAG && vals != null ) {
    362                        bodyBuf.append( String.join( " ",vals ) );
    363
    364        //              for( final String val : vals ) {
    365        //                      if( val != null ) {
    366        //                              bodyBuf.append( val ).append( ' ' );
    367        //                      }
    368        //              }
    369                }
    370
    371                return this ;
    372        }
    373
    374        /**
    375         * データの BODY部のバッファに直接値を追加します（データ形式 TAGのみ有効）。
    376         *
    377         * 指定の文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    378         * これは、データの BODY部のバッファに対して、追加を行います。
    379         * BODY部は、タグのbody として使用されているデータで、BODY有り無しの判定は、
    380         * このデータが登録されたかどうかで判定しています。
    381         *
    382         * 引数が null や、空文字列の場合は、何もしません。
    383         *
    384         * @param       val     データのBODY部のバッファに直接追加する文字列
    385         * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    386         * @return      自分自身
    387         * @see         #addOptions( String... )
    388         * @og.rtnNotNull
    389         */
    390        public TagBuffer addBody( final String val , final boolean isUse ) {
    391                return isUse ? addBody( val ) : this ;
    392        }
    393
    394//      /**
    395//       * データの BODY部のバッファに直接値を追加します（データ形式 TAGのみ有効）。
    396//       *
    397//       * 指定の可変長文字列は、何の加工も無く、' '(スペース)で区切られて、直接追加されます。
    398//       * これは、データの BODY部のバッファに対して、追加を行います。
    399//       * BODY部は、タグのbody として使用されているデータで、BODY有り無しの判定は、
    400//       * このデータが登録されたかどうかで判定しています。
    401//       *
    402//       * 引数が null や、空文字列の場合は、何もしません。
    403//       *
    404//       * @param       vals    データのBODY部のバッファに直接追加する文字列配列
    405//       * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    406//       * @return      自分自身
    407//       * @see         #addOptions( String... )
    408//       * @og.rtnNotNull
    409//       */
    410//      public TagBuffer addBody( final String[] vals , final boolean isUse ) {
    411//              if( isUse && dType == DTYPE.TAG && vals != null ) {
    412//                      for( final String val : vals ) {
    413//                              if( StringUtil.isNotNull( val ) ) {
    414//                                      bodyBuf.append( val ).append( ' ' );
    415//                              }
    416//                      }
    417//              }
    418//
    419//              return this ;
    420//      }
    421
    422        /**
    423         * データの登録エリアに、数値型として値を追加します（データ形式 ARRAYのみ有効）。
    424         *
    425         * データが、数値型の場合、ダブルコーテーションは付けません。
    426         * 指定の可変長文字列は、何の加工も無く、endStr(終端連結時文字)で区切られて、追加されます。
    427         * ARRAY形式のみ有効なので、endStrは、',' になります。
    428         * データが、null の場合、endStr(終端連結時文字)のみが、追加されますので、
    429         * データの個数は、変わりません。
    430         * 最後に必ず、endStr(終端連結時文字)が付きます。
    431         *
    432         * @param       vals    データの登録エリアに、数値型として追加する可変長文字列
    433         * @return      自分自身
    434         * @see         #addNum( String,boolean )
    435         * @og.rtnNotNull
    436         */
    437        public TagBuffer addNum( final String... vals ) {
    438                if( dType == DTYPE.ARY && vals != null ) {
    439                        for( final String val : vals ) {
    440                                if( val == null ) {
    441                                        tagBuf.append( endStr );
    442                                }
    443                                else {
    444                                        tagBuf.append( val ).append( endStr );
    445                                }
    446                        }
    447                }
    448
    449                return this ;
    450        }
    451
    452        /**
    453         * データの登録エリアに、数値型として値を追加します（データ形式 ARRAYのみ有効）。
    454         *
    455         * データが、数値型の場合、ダブルコーテーションは付けません。
    456         * 指定の文字列は、何の加工も無く、endStr(終端連結時文字)で区切られて、追加されます。
    457         * ARRAY形式のみ有効なので、endStrは、',' になります。
    458         * データが、null の場合、endStr(終端連結時文字)のみが、追加されます。
    459         * 最後に必ず、endStr(終端連結時文字)が付きます。
    460         *
    461         * @param       val     データの登録エリアに、数値型として追加する文字列
    462         * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    463         * @return      自分自身
    464         * @see         #addNum( String... )
    465         * @og.rtnNotNull
    466         */
    467        public TagBuffer addNum( final String val , final boolean isUse ) {
    468                return isUse ? addNum( val ) : this ;
    469        }
    470
    471//      /**
    472//       * データの登録エリアに、数値型として値を追加します（データ形式 ARRAYのみ有効）。
    473//       *
    474//       * データが、数値型の場合、ダブルコーテーションは付けません。
    475//       * 指定の可変長文字列は、何の加工も無く、endStr(終端連結時文字)で区切られて、追加されます。
    476//       * ARRAY形式のみ有効なので、endStrは、',' になります。
    477//       * データが、null の場合、endStr(終端連結時文字)のみが、追加されますので、
    478//       * データの個数は、変わりません。
    479//       * 最後に必ず、endStr(終端連結時文字)が付きます。
    480//       *
    481//       * @param       vals    データの登録エリアに、数値型として追加する文字列配列
    482//       * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    483//       * @return      自分自身
    484//       * @see         #addNum( String... )
    485//       * @og.rtnNotNull
    486//       */
    487//      public TagBuffer addNum( final String[] vals , final boolean isUse ) {
    488//              if( isUse && dType == DTYPE.ARY && vals != null ) {
    489//                      for( final String val : vals ) {
    490//                              if( val == null ) {
    491//                                      tagBuf.append( endStr );
    492//                              }
    493//                              else {
    494//                                      tagBuf.append( val ).append( endStr );
    495//                              }
    496//                      }
    497//              }
    498//
    499//              return this ;
    500//      }
    501
    502        /**
    503         * データの登録エリアに、文字型として値を追加します（データ形式 ARRAYのみ有効）。
    504         *
    505         * データが、文字型の場合、ダブルコーテーションを付けて登録します。
    506         * 指定の可変長文字列は、何の加工も無く、endStr(終端連結時文字)で区切られて、追加されます。
    507         * ARRAY形式のみ有効なので、endStrは、',' になります。
    508         * データが、null の場合、""（空文字列）と、endStr(終端連結時文字)が、追加されますので、
    509         * データの個数は、変わりません。
    510         * 最後に必ず、endStr(終端連結時文字)が付きます。
    511         *
    512         * @param       vals    データの登録エリアに、文字型として追加する可変長文字列
    513         * @return      自分自身
    514         * @see         #addStr( String,boolean )
    515         * @og.rtnNotNull
    516         */
    517        public TagBuffer addStr( final String... vals ) {
    518                if( dType == DTYPE.ARY && vals != null ) {
    519                        for( final String val : vals ) {
    520                                if( val == null ) {
    521                                        tagBuf.append( "\"\"" ).append( endStr );
    522                                }
    523                                else {
    524                                        tagBuf.append( '"' ).append( val ).append( '"' ).append( endStr );
    525                                }
    526                        }
    527                }
    528
    529                return this ;
    530        }
    531
    532        /**
    533         * データの登録エリアに、文字型として値を追加します（データ形式 ARRAYのみ有効）。
    534         *
    535         * データが、文字型の場合、ダブルコーテーションを付けて登録します。
    536         * 指定の文字列は、何の加工も無く、endStr(終端連結時文字)で区切られて、追加されます。
    537         * ARRAY形式のみ有効なので、endStrは、',' になります。
    538         * データが、null の場合、""（空文字列）と、endStr(終端連結時文字)が、追加されます。
    539         * 最後に必ず、endStr(終端連結時文字)が付きます。
    540         *
    541         * @param       val     データの登録エリアに、文字型として追加する文字列
    542         * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    543         * @return      自分自身
    544         * @see         #addStr( String... )
    545         * @og.rtnNotNull
    546         */
    547        public TagBuffer addStr( final String val , final boolean isUse ) {
    548                return isUse ? addStr( val ) : this ;
    549        }
    550
    551//      /**
    552//       * データの登録エリアに、文字型として値を追加します（データ形式 ARRAYのみ有効）。
    553//       *
    554//       * データが、文字型の場合、ダブルコーテーションを付けて登録します。
    555//       * 指定の可変長文字列は、何の加工も無く、endStr(終端連結時文字)で区切られて、追加されます。
    556//       * ARRAY形式のみ有効なので、endStrは、',' になります。
    557//       * データが、null の場合、""（空文字列）と、endStr(終端連結時文字)が、追加されますので、
    558//       * データの個数は、変わりません。
    559//       * 最後に必ず、endStr(終端連結時文字)が付きます。
    560//       *
    561//       * @param       vals    データの登録エリアに、文字型として追加する文字列配列
    562//       * @param       isUse   追加処理を行うかどうかの判定(true:処理する/false:処理しない)
    563//       * @return      自分自身
    564//       * @see         #addStr( String... )
    565//       * @og.rtnNotNull
    566//       */
    567//      public TagBuffer addStr( final String[] vals , final boolean isUse ) {
    568//              if( isUse && dType == DTYPE.ARY && vals != null ) {
    569//                      for( final String val : vals ) {
    570//                              if( val == null ) {
    571//                                      tagBuf.append( "\"\"" ).append( endStr );
    572//                              }
    573//                              else {
    574//                                      tagBuf.append( '"' ).append( val ).append( '"' ).append( endStr );
    575//                              }
    576//                      }
    577//              }
    578//
    579//              return this ;
    580//      }
    581
    582        /**
    583         * キーと値のセットを、追加します。
    584         *
    585         * key が nullか、ゼロ文字列の場合は、なにもしません。
    586         * val が null の場合は、なにもしません。
    587         * val が、ゼロ文字列の場合は、追加します
    588         *
    589         * val に、ダブルコーテーション(")が含まれている場合は、属性値をシングルコーテーション
    590         * でくくります。
    591         * 両方含まれている場合は、シングルコーテーションをエスケープ文字(&amp;#39;)に変換します。
    592         *
    593         * @param       key      属性キー(nullか、ゼロ文字列の場合は、なにもしません)
    594         * @param       val      属性値 (null の場合は、なにもしない)
    595         *
    596         * @return      自分自身
    597         * @see         #add( String , String , boolean )
    598         * @og.rtnNotNull
    599         */
    600        public TagBuffer add( final String key,final String val ) {
    601                return add( key,val,true );
    602        }
    603
    604        /**
    605         * キー配列と値配列のセットを、追加します。
    606         *
    607         * これは、配列の数だけ、#add( boolean , String , String ) を呼び出して処理を行います。
    608         * 個々の配列内のkey,valは、先のメソッドの規約に従います。
    609         * キー配列と値配列のサイズが異なる場合や、配列が null の場合は、何もしません。
    610         * （ｴﾗｰではなく何もしないのでご注意ください。）
    611         *
    612         * @param       keys     属性キー配列
    613         * @param       vals     属性値配列
    614         *
    615         * @return      自分自身
    616         * @see         #add( String , String , boolean )
    617         * @og.rtnNotNull
    618         */
    619        public TagBuffer add( final String[] keys,final String[] vals ) {
    620                if( keys != null && vals != null && keys.length == vals.length ) {
    621                        for( int i=0; i<keys.length; i++ ) {
    622                                add( keys[i] , vals[i] , true );
    623                        }
    624                }
    625
    626                return this ;
    627        }
    628
    629        /**
    630         * キーと値のセットを、追加します（データ形式 ARRAY以外有効）。
    631         *
    632         * key が nullか、ゼロ文字列の場合は、なにもしません。
    633         * val が null の場合は、なにもしません。
    634         * val が、ゼロ文字列の場合は、追加します
    635         * isUse が、false の場合は、なにもしません。
    636         *
    637         * val に、ダブルコーテーション(")が含まれている場合は、属性値をシングルコーテーション
    638         * でくくります。
    639         * 両方含まれている場合は、シングルコーテーションをエスケープ文字(&amp;#39;)に変換します。
    640         *
    641         * isUse に、false を指定すると、属性は追加しません。これは、if( isUse ) { tagBuf.add( key,val ); }
    642         * の様な判定処理を、tagBuf.add( key,val,isUse ); とすることで、StringBuilderの様に、連結して使用できます。
    643         *
    644         * 値 に、ダブルコーテーション(")が含まれている場合は、属性値をシングルコーテーション
    645         * でくくります。
    646         * 両方含まれている場合は、シングルコーテーションをエスケープ文字に変換します。
    647         *
    648         * @param       key             属性キー(nullか、ゼロ文字列の場合は、なにもしません)
    649         * @param       val             属性値 (null の場合は、なにもしない)
    650         * @param       isUse   属性を追加かどうかを決めるフラグ(true:追加する/false:何もしない)
    651         *
    652         * @return      自分自身
    653         * @see         #add( String , String )
    654         * @og.rtnNotNull
    655         */
    656        public TagBuffer add( final String key , final String val , final boolean isUse ) {
    657                if( isUse && dType != DTYPE.ARY && StringUtil.isNotNull( key ) && val != null ) {
    658                        if( dType == DTYPE.JSON ) {                                     // JSON の場合は、keyにﾀﾞﾌﾞﾙｸｵｰﾄを付けます。
    659                                tagBuf.append( '"' ).append( key ).append( '"' ).append( kvsep );
    660                        }
    661                        else {
    662                                tagBuf.append( key ).append( kvsep );   // TAG,CSS
    663                        }
    664
    665                        if( dType == DTYPE.CSS ) {                                      // CSS の場合は、属性にﾀﾞﾌﾞﾙｸｵｰﾄは付けません。
    666                                tagBuf.append( val );
    667                        }
    668                        else {
    669                                // ダブルコーテーションがある場合は、シングルコーテーションで囲う。
    670                                if( val.indexOf( '"' ) >= 0 ) {
    671                                        // ただし、シングルコーテーションを含む場合は、エスケープ文字に置き換える。
    672                                        tagBuf.append( '\'' )
    673                                                .append( val.indexOf( '\'' ) >= 0 ? val.replaceAll( "'","&#39;" ) : val )
    674                                                .append( '\'' );
    675                                }
    676                                else {
    677                                        tagBuf.append( '"' ).append( val ).append( '"' );
    678                                }
    679                        }
    680                        tagBuf.append( endStr );
    681                }
    682
    683                return this ;
    684        }
    685
    686        /**
    687         * 整形されたデータ文字列を 作成します。
    688         * このメソッドは、startXXX で開始さえたデータ形式に応じて、データ本体のバッファに追記します。
    689         * 一連のデータ作成処理は、この、#make() メソッドで終了します。
    690         *
    691         * その後、startXXX を呼び出すことで、新しいデータ形式の作成を開始できます。
    692         *
    693         * @return      自分自身
    694         * @og.rtnNotNull
    695         */
    696        public TagBuffer make() {
    697                if( dType == DTYPE.TAG ) {                                              // TAG の場合
    698                        if( name == null ) {
    699                                makeBuf.append( tagBuf );
    700                        }
    701                        else {
    702                                makeBuf.append( '<' ).append( name ).append( ' ' ).append( tagBuf );
    703
    704                                if( isKaraTag ) {                                               // 空要素の場合
    705        //                      if( bodyBuf.length() == 0 ) {                   // BODY がない
    706        //                              makeBuf.append( "/>" );
    707                                        makeBuf.append( '>' );                          // XHTML 以外の文法
    708                                }
    709                                else {
    710                                        makeBuf.append( '>' )
    711                                                .append( bodyBuf )
    712                                                .append( "</" ).append( name ).append( '>' );
    713                                }
    714                        }
    715                }
    716                else if( dType == DTYPE.CSS ) {                                 // CSS の場合
    717                        if( name == null ) {
    718                                makeBuf.append( tagBuf );
    719                        }
    720                        else {
    721                                makeBuf.append( name ).append( " { " ).append( tagBuf ).append( " } " );
    722                        }
    723                }
    724                else if( dType == DTYPE.JSON ) {                                // JSON の場合
    725                        makeBuf.append( " { " ).append( tagBuf ).append( " }, " );
    726                }
    727                else if( dType == DTYPE.ARY ) {                                 // ARY の場合
    728                        makeBuf.append( " [ " ).append( tagBuf ).append( " ], " );
    729                }
    730
    731                tagBuf.setLength( 0 );          // StringBuilder のクリア
    732                bodyBuf.setLength( 0 );         // StringBuilder のクリア
    733
    734                return this ;
    735        }
    736
    737        /**
    738         * TAG形式のデータの、要素部分(BODYの直前まで)の文字列を作成して返します。
    739         *
    740         * これは、TAG形式で、BODY部分が、別途処理が必要な場合、要素部分まで作成したい場合に使用します。
    741         * また、TAG形式以外の場合は、データの登録エリアのバッファをそのまま文字列にして返します。
    742         *
    743         * この処理を実行すると、内部のすべてのバッファがクリアされます。
    744         * 唯一、nameや、区切り文字情報は残っていますので、続きから、BODY の登録や、次のタグの登録が可能です。
    745         * 最後は、#toAfter() メソッドを実行すれば、タグとしての形式を保つことが可能です。
    746         *
    747         * @return      要素部分(BODYの直前まで)の文字列
    748         * @see         #toAfter()
    749         * @og.rtnNotNull
    750         */
    751        public String toBefore() {
    752                if( dType == DTYPE.TAG && name != null ) {
    753                        makeBuf.append( '<' ).append( name ).append( ' ' ).append( tagBuf ).append( '>' );
    754                }
    755                else {
    756                        makeBuf.append( tagBuf );
    757                }
    758
    759                final String rtn = makeBuf.toString();
    760
    761                clear();                                        // すべての内部情報をクリアします。
    762
    763                return rtn;
    764        }
    765
    766        /**
    767         * TAG形式のデータの、BODY＋終了タグの文字列を作成して返します。
    768         *
    769         * 通常は、#toBefore() を呼んだ後に、最後に実行するメソッドです。
    770         * この処理を実行すると、内部のすべてのバッファがクリアされます。
    771         *
    772         * @return      要素部分(BODYの直前まで)の文字列
    773         * @see         #toBefore()
    774         * @og.rtnNotNull
    775         */
    776        public String toAfter() {
    777                if( dType == DTYPE.TAG && name != null ) {
    778                                makeBuf.append( bodyBuf ).append( "</" ).append( name ).append( '>' );
    779                }
    780                else {
    781                        makeBuf.append( bodyBuf );
    782                }
    783
    784                final String rtn = makeBuf.toString();
    785
    786                clear();                                        // すべての内部情報をクリアします。
    787
    788                return rtn;
    789        }
    790
    791        /**
    792         * 内部データのバッファをすべてクリアします。
    793         *
    794         * 内部データには、データ登録バッファ、データBODY部バッファ、データ本体バッファと
    795         * ３種類のバッファを持っています。
    796         *
    797         * この、３種類のバッファすべてをクリアします。
    798         *
    799         * @return      自分自身
    800         * @og.rtnNotNull
    801         */
    802        public TagBuffer clear() {
    803                tagBuf.setLength( 0 );          // StringBuilder のクリア
    804                bodyBuf.setLength( 0 );         // StringBuilder のクリア
    805                makeBuf.setLength( 0 );         // StringBuilder のクリア
    806
    807                return this ;
    808        }
    809
    810        /**
    811         * 内部データの文字列を返します。
    812         *
    813         * データ本体のバッファを文字列に変換して返します。
    814         * これは、#make() か、#tagBefore() を実行後に行います。そうしないと、
    815         * データが設定されていません。
    816         * この処理を行っても、データはクリアされないため、再び処理を行うことが可能です。
    817         * 中間のデータを確認する場合や、最後にデータを取り出す場合に利用します。
    818         *
    819         * @return      データ本体の文字列
    820         */
    821        @Override
    822        public String toString() {
    823                return makeBuf.toString();
    824        }
    825
    826        /**
    827         * 内部データの文字列を返します。
    828         *
    829         * return tagBuf.make().toString(); と同じ結果を返します。
    830         *
    831         * これは、TagBuffer クラスとの互換性の為に用意されたメソッドです。
    832         * 内部にキャッシュとして持つため、最初に一度実行すると、以降、同じ値しか返されません。
    833         *
    834         * @return      データ本体の文字列
    835         */
    836        public String makeTag() {
    837                if( cacheTag == null ) {
    838                        cacheTag = make().toString();
    839                }
    840
    841                return cacheTag;
    842        }
    843
    844        /**
    845         * 行番号付きのタグの 整形された文字列を 作成します。
    846         *
    847         * 内部データから生成された文字列に含まれる、[I] に、行番号を、
    848         * [V] に、設定値を埋め込みます。
    849         *
    850         * これは、TagBuffer クラスとの互換性の為に用意されたメソッドです。
    851         * 内部にキャッシュとして持つため、最初に一度実行すると、以降、同じ値しか返されません。
    852         *
    853         * 逆に、何度実行しても、内部変数は書き換えられませんので、
    854         * 行番号と、設定値を替えて、連続で呼ぶことが可能です。
    855         *
    856         * @param       rowNo   行番号([I] 文字列を変換します)
    857         * @param       val             設定値([V] 文字列を変換します)
    858         *
    859         * @return      行番号と設定値が置き換えられた文字列
    860         */
    861        public String makeTag( final int rowNo,final String val ) {
    862                if( cacheTag == null ) {
    863                        cacheTag = make().toString();
    864                }
    865
    866                return cacheTag.replace( "[I]",String.valueOf( rowNo ) ).replace( "[V]",val );
    867
    868        //      String tag = makeBuf.toString();
    869        //      tag = StringUtil.replace( tag,"[I]",String.valueOf( rowNo ) );
    870        //      tag = StringUtil.replace( tag,"[V]",val );
    871
    872        //      return tag ;
    873        }
    874}