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