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
018/**
019 * TagBuffer.java は、共通的に使用される 簡易タグ作成クラスです。
020 * タグヘッダーは、オブジェクト作成時に登録する為、後の変更は出来ません。
021 * BODY部や、属性は、一度登録すると書き換えできません。
022 * また、同一属性チェックは行いません。登録した属性のキーや、値を取り出すことも出来ません。
023 * あくまで、タグ文字列をストレートに作成することに特化したクラスです。
024 * これらの高度な機能が必要であれば、{@link org.opengion.fukurou.util.Attributes } をご参照ください。
025 *
026 * makeTag() メソッドを呼び出した時点で、内部にタグ文字列をキャッシュします。
027 * それ以降の変更は、出来ません。
028 *
029 * 内部的には、構造化されていません。あくまで、文字列連結(StringBuilder)の
030 * 簡易クラスとして、使用してください。
031 *
032 * @og.group ユーティリティ
033 *
034 * @version  4.0
035 * @author       Kazuhiko Hasegawa
036 * @since    JDK5.0,
037 */
038public final class TagBuffer {
039        private final StringBuilder buf = new StringBuilder(100);
040        private final String tagName ;
041        private       String body     = null;
042        private       String cacheTag = null;
043
044        /**
045         * デフォルトコンストラクター
046         * このコンストラクターを使用すると、タグ名を指定できないため、
047         * 完成形のタグは、作成できません。属性リスト(key="value")の
048         * 連結形式を得る場合にのみ、使用して下さい。
049         *
050         */
051        public TagBuffer() {
052                this.tagName = null;
053        }
054
055        /**
056         * コンストラクター
057         * タグ名に null を指定すると、デフォルトコンストラクターと同様に、
058         * 完成形のタグは、作成できません。属性リスト(key="value")の
059         * 連結形式を得る場合にのみ、タグ名 にnull を指定して下さい。
060         *
061         * @param       tagName  タグ名称
062         */
063        public TagBuffer( final String tagName ) {
064                this.tagName = tagName;
065                if( tagName != null ) {
066                        buf.append( "<" ).append( tagName ).append( " " );
067                }
068        }
069
070        /**
071         * タグの BODY部を登録します。
072         * 登録しない場合は、BODY部のないタグ(空要素タグ)を生成します。
073         * BODY部を指定すると、&lt;tagName key="val" ・・・ &gt;body&lt;/tagName&gt; 形式になります。
074         * BODY部が、null の場合は、 &lt;tagName key="val" ・・・ /&gt; 形式になります。
075         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
076         *
077         * @param       body     タグのBODY部
078         */
079        public void setBody( final String body ) {
080                if( cacheTag != null ) {
081                        String errMsg = "makeTag() 実行後に、BODY部の値を書き換えることは出来ません。" ;
082                        throw new RuntimeException( errMsg );
083                }
084
085                this.body = body;
086        }
087
088        /**
089         * タグの 属性(key="value")を登録します。
090         * 属性は、key="value" の文字列を作成します。key か、value のどちらかが null
091         * の場合は、登録しません。
092         * value に、ダブルコーテーション(")が含まれている場合は、属性値をシングルコーテーション
093         * でくくります。
094         * 両方含まれている場合は、シングルコーテーションをエスケープ文字に変換します。
095         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
096         *
097         * @og.rev 3.8.6.1 (2006/10/20) シングルとダブルが混在する場合は、シングルをエスケープする
098         *
099         * @param       key      属性キー (null の場合は、なにもしない)
100         * @param       val      属性値 (null の場合は、なにもしない)
101         */
102        public void add( final String key,final String val ) {
103                if( cacheTag != null ) {
104                        String errMsg = "makeTag() 実行後に、属性を追加することは出来ません。" ;
105                        throw new RuntimeException( errMsg );
106                }
107
108                if( key != null && val != null ) {
109                        if( val.indexOf( '\"' ) >= 0 ) {
110                                String temp = val ;
111                                if( val.indexOf( "'" ) >= 0 ) { temp = val.replaceAll( "'","&#39;" ); }
112                                buf.append( key ).append( "='" ).append( temp ).append( "' " );
113                        }
114                        else {
115                                buf.append( key ).append( "=\"" ).append( val ).append( "\" " );
116                        }
117                }
118        }
119
120        /**
121         * タグの属性に、追加登録します。
122         * 文字列として、key="value" の形式データを与えてください。連結時は、後ろに
123         * スペースを一つ挟みますので、与える引数自体に連結用スペースを追加しておく
124         * 必要はありません。
125         * 通常は、tagName なしで作成した、Tagbuffer オブジェクトの makeTag() メソッドの
126         * 返り値を渡しますが、Attributes.getAttribute() の返り値でも使用できます。
127         * 引数が null の場合は、なにもしません。
128         * このメソッドは、makeTag() が呼ばれた後は、登録できなくなります。
129         *
130         * @param       str     タグバッファーを追加します。
131         * @see         #makeTag()
132         * @see         Attributes#getAttribute()
133         */
134        public void add( final String str ) {
135                if( cacheTag != null ) {
136                        String errMsg = "makeTag() 実行後に、属性を追加することは出来ません。" ;
137                        throw new RuntimeException( errMsg );
138                }
139
140                if( str != null ) {
141                        buf.append( str ).append( " " );
142                }
143        }
144
145        /**
146         * タグの 整形された文字列を 作成します。
147         * このメソッドは、tagName,body,属性より タグの完成形の文字列を作成します。
148         * 作成された文字列は、内部でキャッシュされます。
149         * BODY部を指定すると、&lt;tagName key="val" ・・・ &gt;body&lt;/tagName&gt; 形式になります。
150         * BODY部が、null の場合は、 &lt;tagName key="val" ・・・ /&gt; 形式になります。
151         * タグ名を指定しない(null)と、完成形のタグは、作成できません。
152         * 属性リスト(key="value")の連結形式を返します。
153         *
154         * このメソッドの呼び出し以降では、setBody() も add() も実行できません。
155         *
156         * @return      整形された タグ文字列
157         */
158        public String makeTag() {
159                if( cacheTag == null ) {
160                        if( tagName != null ) {
161                                if( body == null ) {
162                                        buf.append( "/>" );
163                                }
164                                else {
165                                        buf.append( ">" ).append( body );
166                                        buf.append( "</" ).append( tagName ).append( ">" );
167                                }
168                        }
169                        cacheTag = buf.toString();
170                }
171
172                return cacheTag;
173        }
174
175        /**
176         * 行番号付きのタグの 整形された文字列を 作成します。
177         * このメソッドは、makeTag() の結果より、[I] 文字列を引数の行番号と変換します。
178         * これにより、動的に行番号つきの情報を属性に付与することが可能になります。
179         * 内部にキャッシュされる値は、[I] 記号を変換していない状態の文字列です。
180         * よって、このメソッドは、rowNo を変えて、何度でも呼び出すことは可能ですが、
181         * setBody() や add() は実行できません。
182         *
183         * @param       rowNo   行番号([I] 文字列を変換します。)
184         * @param       val     設定値([V] 文字列を変換します。)
185         *
186         * @return      整形された タグ文字列
187         */
188        public String makeTag( final int rowNo,final String val ) {
189                String tag = makeTag();
190                tag = StringUtil.replace( tag,"[I]",String.valueOf( rowNo ) );
191                tag = StringUtil.replace( tag,"[V]",val );
192
193                return tag ;
194        }
195}