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 org.opengion.fukurou.security.HybsCryptography;
020import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
021import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
022
023/**
024 * XHTMLTag.java は、共通的に使用されるHTMLタグの生成メソッドを集約したクラスです。
025 *
026 * 全変数/メソッドは、public static final 宣言されています。
027 *
028 * @version  4.0
029 * @author   Kazuhiko Hasegawa
030 * @since    JDK5.0,
031 */
032public final class XHTMLTag {
033
034        /** URLチェックキー発行用 4.3.7.1 (2009/06/08) */
035        private static final HybsCryptography HYBS_CRYPTOGRAPHY = new HybsCryptography(); // 4.3.7.0 (2009/06/01)
036
037        /**
038         * BUTTON タグの属性リストです。
039         *
040         * @og.rev 5.7.1.0 (2013/12/06) HTML5関連の属性を追加
041         */
042        private static final String[]
043                BUTTON_KEY =  { "type","name","value","onClick"
044                                                ,"id","class","lang","dir","title","style","xml:lang"
045                                                ,"disabled","tabindex","accesskey"
046                                                ,"onBlur","onFocus","ondblClick","onMouseDown","onMouseUp"
047                                                ,"onMouseMove","onMouseOut","onMouseOver"
048                                                // 5.7.1.0 (2013/12/06) HTML5関連の属性
049                                                ,"autofocus"
050                                        };
051
052        /**
053         * INPUT タグの属性リストです。
054         *
055         * なお、name と value は、この属性リストに含めていません。
056         * これは、#inputAttri( Attributes ) メソッドで、name 属性や value 属性など、
057         * 一般に都度変更されるフィールド以外を、固定値として作成しておくためです。
058         *
059         * @og.rev 5.7.1.0 (2013/12/06) HTML5関連の属性を追加
060         */
061        private static final String[]
062                INPUT_KEY = { "type","size","maxlength","checked","src"
063                                                ,"alt","accept","usemap","ismap"
064                                                ,"id","class","lang","dir","title","style","xml:lang"
065                                                ,"readonly","disabled","tabindex","accesskey","onClick","onChange"
066                                                ,"onBlur","onFocus","ondblClick","onMouseDown","onMouseUp"
067                                                ,"onMouseMove","onMouseOut","onMouseOver"
068                                                ,"onSelect","onKeydown","onKeypress","onKeyup"
069                                                // 5.7.1.0 (2013/12/06) HTML5関連の属性
070                                                ,"autocomplete","autofocus","pattern","placeholder","list","min","max","step","required"
071                                        };
072
073        /**
074         * TEXTAREA タグの属性リストです。
075         *
076         * @og.rev 5.7.1.0 (2013/12/06) HTML5関連の属性を追加
077         */
078        private static final String[]
079                TEXTAREA_KEY = { "name","rows","cols"
080                                                ,"id","class","lang","dir","title","style","xml:lang"
081                                                ,"readonly","disabled","tabindex","accesskey","onClick"
082                                                ,"onBlur","onFocus","ondblClick","onMouseDown","onMouseUp"
083                                                ,"onMouseMove","onMouseOut","onMouseOver"
084                                                ,"onSelect","onKeydown","onKeypress","onKeyup"
085                                                // 5.7.1.0 (2013/12/06) HTML5関連の属性
086                                                ,"autofocus","placeholder"
087                                        };
088
089        /**
090         * LINK タグの属性リストです。
091         *
092         * href属性は定義されていません。
093         * これは、UrlEncode 等行う必要がある為、別に処理する必要がある為です。
094         */
095        private static final String[]
096                LINK_KEY = { "type","name","hreflang","rel","rev","charset"
097                                                ,"target","shape","coords","onClick"
098                                                ,"id","class","lang","dir","title","style","xml:lang"
099                                                ,"tabindex","accesskey"
100                                                ,"onBlur","onFocus","ondblClick","onMouseDown","onMouseUp"
101                                                ,"onMouseMove","onMouseOut","onMouseOver"
102                                        };
103
104        /**
105         * SELECT タグの属性リストです。
106         *
107         * name 属性が無いのは、input と同様、name 属性のみ入れ替える事がある為。
108         *
109         * @og.rev 5.7.1.0 (2013/12/06) HTML5関連の属性を追加
110         */
111        private static final String[]
112                SELECT_KEY = { "size","multiple",
113                                                "id","class","lang","dir","title","style","xml:lang"
114                                                ,"disabled","tabindex","onClick","onChange"
115                                                ,"onBlur","onFocus","ondblClick","onMouseDown","onMouseUp"
116                                                ,"onMouseMove","onMouseOut","onMouseOver"
117                                                ,"onSelect","onKeydown","onKeypress","onKeyup"
118                                                // 5.7.1.0 (2013/12/06) HTML5関連の属性
119                                                ,"autofocus"
120                                        };
121
122        /**
123         * OPTION タグの属性リストです。
124         *
125         */
126        private static final String[]
127                OPTION_KEY = { "value","label","selected"
128                                                ,"id","class","lang","dir","title","style","xml:lang"
129                                                ,"disabled"
130                                        };
131
132        /**
133         * FRAME タグの属性リストです。
134         *
135         */
136        private static final String[]
137                FRAME_KEY = { "name","longdesc","marginwidth","marginheight","noresize"
138                                                ,"scrolling","frameborder"
139                                                ,"id","class","title","style"
140                                        };
141
142        /**
143         * IFRAME タグの属性リストです。
144         *
145         * @og.rev 5.9.1.2 (2015/10/23) 新規追加
146         */
147        private static final String[]
148                IFRAME_KEY = { "name","srcdoc","seamless","sandbox","width","height"
149                                                ,"marginwidth","marginheight","noresize","scrolling","frameborder"
150                                                ,"id","class","title","style"
151                                        };
152
153        /**
154         * IMAGE タグの属性リストです。
155         *
156         */
157        private static final String[]
158                IMAGE_KEY = { "src","alt","longdesc","width","height","usemap","ismap","name","onClick"
159                                                ,"align","border","hspace","vspace"              // この行は非推奨属性です。
160                                                ,"id","class","title","style","lang","dir","xml:lang"
161                                                ,"onBlur","onFocus","ondblClick","onMouseDown","onMouseUp"
162                                                ,"onMouseMove","onMouseOut","onMouseOver"
163                                        };
164
165        /**
166         * FORM タグの属性リストです。
167         *
168         */
169        private static final String[]
170                FORM_KEY = { "action","method","enctype","accept-charset","accept","name","target"
171                                                ,"id","class","title","style","lang","dir","xml:lang"
172                                        };
173
174        /**
175         * SPAN タグの属性リストです。
176         *
177         * @og.rev 7.0.3.0 (2019/05/13) SPANの属性リストにonClick等の属性追加
178         */
179        private static final String[]
180                SPAN_KEY = { "id","class","title","style","lang","dir","xml:lang"
181                                                ,"tabindex","onClick"                                                                   // 7.0.3.0 (2019/05/13)
182                                                ,"onFocus","ondblClick","onMouseDown","onMouseUp"               // 7.0.3.0 (2019/05/13)
183                                                ,"onMouseMove","onMouseOut","onMouseOver"                               // 7.0.3.0 (2019/05/13)
184                                                ,"autofocus"                                                                                    // 7.0.3.0 (2019/05/13)
185                                        };
186
187        /**
188         * PRE タグの属性リストです。
189         *
190         */
191        private static final String[]
192                PRE_KEY = { "id","class","title","style","lang","dir","xml:lang" };
193
194        /**
195         *  デフォルトコンストラクターをprivateにして、
196         *  オブジェクトの生成をさせないようにする。
197         *
198         */
199        private XHTMLTag() { }
200
201        /**
202         * ボタンを作成します。
203         *
204         * <button type="形式" name="名前" value="送信文字" オプション・・・ >ラベル</button>
205         *
206         * <table class="plain">
207         *   <caption>Attributes に設定できる属性</caption>
208         *   <tr><td>name="名前"</td><td>オプション</td><td>LabelResource.properties のキー</td></tr>
209         *   <tr><td>type="形式"</td><td>必須</td><td>submit/reset/button</td></tr>
210         *   <tr><td>value="値"</td><td>オプション</td><td>name属性と共に送信される値</td></tr>
211         *   <tr><td>disabled="disabled"</td><td>オプション</td><td>ボタンを利用できない状態にする場合に指定</td></tr>
212         *   <tr><td>tabindex="Tab移動順"</td><td>オプション</td><td>0~32767の範囲で数字で指定(小さい順に移動)</td></tr>
213         *   <tr><td>accesskey="ショートカットキー"</td><td>オプション</td><td>文字セット中の1文字:WindowsであればAltキーと同時使用</td></tr>
214         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
215         *   <tr><td>body="表示するタグ文字列"</td><td>オリジナル</td><td>画像や文字などボタン上に表示させたいタグの文字列</td></tr>
216         * </table>
217         *
218         * 設定できる属性
219         * 形式は、
220         *  submit  送信(サブミット)
221         *  reset   リセット
222         *  button  汎用ボタン
223         * を指定します。
224         *
225         * ラベルに、HTMLテキスト(強調文字など)をはめ込むことが出来ます。
226         * また、イメージ &lt;img ・・・・&gt; を指定することも可能です。
227         * disabled="disabled" のとき、このボタンのデータはサーバーに送信されません。
228         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
229         * 汎用属性を自由に登録する事が出来ます。
230         *
231         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
232         *
233         * @param   attri 属性群
234         *
235         * @return  ボタンタグ文字列
236         * @og.rtnNotNull
237         */
238        public static String button( final Attributes attri ) {
239                final String checkedType = "|submit|reset|button|";
240
241                final String type  = attri.get( "type" );
242                if( checkedType.indexOf( "|" + type + "|" ) < 0 ) {
243                        final String errMsg = "button タイプ設定エラー [" + type + "]";
244                        throw new OgRuntimeException( errMsg );
245                }
246
247                String body = attri.get( "body" );
248                if( body == null ) { body = "" ; }
249
250                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
251                return new TagBuffer( "button" )
252                                        .add(     attri.getAttribute( BUTTON_KEY ) )
253                                        .addBody( body )
254                                        .makeTag();
255        }
256
257        /**
258         * 入力フォームを作成します。
259         *
260         * &lt;input type="text" name="名前" value="送信文字" ....&gt;
261         *
262         * <table class="plain">
263         *   <caption>Attributes に設定できる属性</caption>
264         *   <tr><td>name="名前"</td><td>オプション</td><td>LabelResource.properties のキー</td></tr>
265         *   <tr><td>type="形式"</td><td>必須</td><td>text/password/checkbox/radio/submit/reset/button/image/file/hidden</td></tr>
266         *   <tr><td>value="値"</td><td>オプション</td><td>name属性と共に送信される値</td></tr>
267         *   <tr><td>size="30"</td><td>オプション</td><td>inputタグの大きさ</td></tr>
268         *   <tr><td>maxlength="50"</td><td>オプション</td><td>type属性が「text」,「password」 のときの最大文字数</td></tr>
269         *   <tr><td>checked="checked"</td><td>オプション</td><td>type属性が「checkbox」,「radio」 の場合に選択されている状態にする。</td></tr>
270         *   <tr><td>disabled="disabled"</td><td>オプション</td><td>選択や変更の操作をできない状態にする場合に指定</td></tr>
271         *   <tr><td>accept="MIMEタイプ"</td><td>オプション</td><td>type属性が「file」の場合に処理可能なMIMEタイプを指定</td></tr>
272         *   <tr><td>tabindex="Tab移動順"</td><td>オプション</td><td>0~32767の範囲で数字で指定(小さい順に移動)</td></tr>
273         *   <tr><td>accesskey="ショートカットキー"</td><td>オプション</td><td>文字セット中の1文字:WindowsであればAltキーと同時使用</td></tr>
274         *   <tr><td>src="URL"</td><td>オプション</td><td>type属性が「image」の場合送信ボタンの画像URLを指定</td></tr>
275         *   <tr><td>alt="代替文字列"</td><td>オプション</td><td>type属性が「image」の場合、画像が表示できないときの代替文字列を指定</td></tr>
276         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
277         *   <tr><td>body="表示するタグ文字列"</td><td>オリジナル</td><td>画像や文字などボタン上に表示させたいタグの文字列</td></tr>
278         *   <tr><td>サポート外</td><td>未実装</td><td>readonly属性、usemap属性、ismap属性、align属性</td></tr>
279         * </table>
280         *
281         * 設定できるtype属性
282         *  text       1行のテキストフィールド
283         *  password   パスワード用テキストフィールド
284         *  checkbox   チェックボックス(複数選択可)
285         *  radio      ラジオボタン(複数選択不可)
286         *  file       送信ファイルの選択
287         *  hidden     表示せずにサーバーに送信する。
288         *  submit     送信(サブミット)
289         *  reset      リセット
290         *  button     汎用ボタン
291         *  image      イメージによる画像ボタン
292         *
293         * HTML5 より、以下のtypeが指定可能になりました。(ブラウザによってサポート状況は異なります。)
294         *  search     検索テキストの入力欄を作成する
295         *  tel        電話番号の入力欄を作成する
296         *  url        URLの入力欄を作成する
297         *  email      メールアドレスの入力欄を作成する
298         *  datetime   UTC(協定世界時)による日時の入力欄を作成する
299         *  date       日付の入力欄を作成する
300         *  month      月の入力欄を作成する
301         *  week       週の入力欄を作成する
302         *  time       時間の入力欄を作成する
303         *  datetime-local    UTC(協定世界時)によらないローカル日時の入力欄を作成する
304         *  number     数値の入力欄を作成する
305         *  range      レンジの入力欄を作成する
306         *  color      色の入力欄を作成する
307         *
308         * ラジオボタン/チェックボックスであらかじめ、チェックをして
309         * おきたい場合は、checked 属性に "checked" を登録します。
310         * ファイルダイアログの場合は、attributesの accept 属性に "MIMEタイプ"
311         * を登録します。
312         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
313         * 文字を自由に登録する事が出来ます。
314         * CSSでクラスを対応 class="XXXX"
315         * タブで移動順を指定する tabindex="タブ順"
316         * ショートカットキーを割り当てる accesskey="ショートカットキー"
317         *
318         * @param   attri 属性群
319         *
320         * @return  入力フォームタグ文字列
321         * @og.rtnNotNull
322         * @see     #input( Attributes attri,String name,String value,String optAtt )
323         */
324        public static String input( final Attributes attri ) {
325                final String name     = attri.get( "name" );
326                final String value    = attri.get( "value" );
327                final String optAttri = attri.get( "optionAttributes" );
328
329                return input( attri,name,value,optAttri );
330        }
331
332        /**
333         * 入力フォームを作成します。
334         *
335         * &lt;input type="text" name="名前" value="送信文字" ....&gt;
336         *
337         * <table class="plain">
338         *   <caption>Attributes に設定できる属性</caption>
339         *   <tr><td>name="名前"</td><td>オプション</td><td>LabelResource.properties のキー</td></tr>
340         *   <tr><td>type="形式"</td><td>必須</td><td>text/password/checkbox/radio/submit/reset/button/image/file/hidden</td></tr>
341         *   <tr><td>value="値"</td><td>オプション</td><td>name属性と共に送信される値</td></tr>
342         *   <tr><td>size="30"</td><td>オプション</td><td>inputタグの大きさ</td></tr>
343         *   <tr><td>maxlength="50"</td><td>オプション</td><td>type属性が「text」,「password」 のときの最大文字数</td></tr>
344         *   <tr><td>checked="checked"</td><td>オプション</td><td>type属性が「checkbox」,「radio」 の場合に選択されている状態にする。</td></tr>
345         *   <tr><td>disabled="disabled"</td><td>オプション</td><td>選択や変更の操作をできない状態にする場合に指定</td></tr>
346         *   <tr><td>accept="MIMEタイプ"</td><td>オプション</td><td>type属性が「file」の場合に処理可能なMIMEタイプを指定</td></tr>
347         *   <tr><td>tabindex="Tab移動順"</td><td>オプション</td><td>0~32767の範囲で数字で指定(小さい順に移動)</td></tr>
348         *   <tr><td>accesskey="ショートカットキー"</td><td>オプション</td><td>文字セット中の1文字:WindowsであればAltキーと同時使用</td></tr>
349         *   <tr><td>src="URL"</td><td>オプション</td><td>type属性が「image」の場合送信ボタンの画像URLを指定</td></tr>
350         *   <tr><td>alt="代替文字列"</td><td>オプション</td><td>type属性が「image」の場合、画像が表示できないときの代替文字列を指定</td></tr>
351         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
352         *   <tr><td>body="表示するタグ文字列"</td><td>オリジナル</td><td>画像や文字などボタン上に表示させたいタグの文字列</td></tr>
353         *   <tr><td>サポート外</td><td>未実装</td><td>readonly属性、usemap属性、ismap属性、align属性</td></tr>
354         * </table>
355         *
356         * 設定できるtype属性
357         *  text       1行のテキストフィールド
358         *  password   パスワード用テキストフィールド
359         *  checkbox   チェックボックス(複数選択可)
360         *  radio      ラジオボタン(複数選択不可)
361         *  file       送信ファイルの選択
362         *  hidden     表示せずにサーバーに送信する。
363         *  submit     送信(サブミット)
364         *  reset      リセット
365         *  button     汎用ボタン
366         *  image      イメージによる画像ボタン
367         *
368         * HTML5 より、以下のtypeが指定可能になりました。(ブラウザによってサポート状況は異なります。)
369         *  search     検索テキストの入力欄を作成する
370         *  tel        電話番号の入力欄を作成する
371         *  url        URLの入力欄を作成する
372         *  email      メールアドレスの入力欄を作成する
373         *  datetime   UTC(協定世界時)による日時の入力欄を作成する
374         *  date       日付の入力欄を作成する
375         *  month      月の入力欄を作成する
376         *  week       週の入力欄を作成する
377         *  time       時間の入力欄を作成する
378         *  datetime-local    UTC(協定世界時)によらないローカル日時の入力欄を作成する
379         *  number     数値の入力欄を作成する
380         *  range      レンジの入力欄を作成する
381         *  color      色の入力欄を作成する
382         *
383         * ラジオボタン/チェックボックスであらかじめ、チェックをして
384         * おきたい場合は、checked 属性に "checked" を登録します。
385         * ファイルダイアログの場合は、attributesの accept 属性に "MIMEタイプ"
386         * を登録します。
387         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
388         * 文字を自由に登録する事が出来ます。
389         * CSSでクラスを対応 class="XXXX"
390         * タブで移動順を指定する tabindex="タブ順"
391         * ショートカットキーを割り当てる accesskey="ショートカットキー"
392         *
393         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
394         * @og.rev 6.2.2.2 (2015/04/03) NO_MAXLEN キーの値が、"true" の場合、maxlength を強制削除する。
395         *
396         * @param   attri  属性群
397         * @param   name   名前
398         * @param   value  値
399         * @param   optAttri オプション文字列(タグ属性定義されていない属性の登録用文字列)
400         *
401         * @return  入力フォームタグ文字列
402         * @og.rtnNotNull
403         */
404        public static String input( final Attributes attri,final String name,final String value,final String optAttri ) {
405
406                // 6.2.2.2 (2015/04/03) NO_MAXLEN キーの値が、"true" の場合、maxlength を強制削除する。
407                if( "true".equalsIgnoreCase( attri.get( "NO_MAXLEN" ) ) ) {
408                        attri.remove( "maxlength" );
409                }
410
411                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
412                return new TagBuffer( "input" )
413                                        .add(   "name"  , name  )
414                                        .add(   "value" , value )
415                                        .add(   attri.getAttribute( INPUT_KEY ) )
416                                        .add(   optAttri )
417                                        .makeTag();
418        }
419
420        /**
421         * 入力フォームの属性情報のみの文字列を作成します。
422         * これは、name 属性や value 属性など、一般に都度変更されるフィールド
423         * 以外の固定的な属性情報を、先に作成しておく場合に、使用します。
424         *
425         * @og.rev 6.2.2.2 (2015/04/03) NO_MAXLEN キーの値が、"true" の場合、maxlength を強制削除する。
426         *
427         * @param   attri       属性リスト
428         *
429         * @return  入力フォームタグの属性情報文字列
430         * @og.rtnNotNull
431         */
432        public static String inputAttri( final Attributes attri ) {
433                // 6.2.2.2 (2015/04/03) NO_MAXLEN キーの値が、"true" の場合、maxlength を強制削除する。
434                if( "true".equalsIgnoreCase( attri.get( "NO_MAXLEN" ) ) ) {
435                        attri.remove( "maxlength" );
436                }
437                return attri.getAttribute( INPUT_KEY );
438        }
439
440        /**
441         * テキストエリアの属性情報のみの文字列を作成します。
442         * これは、name 属性や value 属性など、一般に都度変更されるフィールド
443         * 以外の固定的な属性情報を、先に作成しておく場合に、使用します。
444         *
445         * @param   attri       属性リスト
446         *
447         * @return  テキストエリアの属性情報文字列
448         * @og.rtnNotNull
449         */
450        public static String textareaAttri( final Attributes attri ) {
451                return attri.getAttribute( TEXTAREA_KEY );
452        }
453
454        /**
455         * プルダウン等のメニューの属性情報のみの文字列を作成します。
456         * これは、name 属性や value 属性など、一般に都度変更されるフィールド
457         * 以外の固定的な属性情報を、先に作成しておく場合に、使用します。
458         *
459         * @param   attri       属性リスト
460         *
461         * @return  プルダウン等のメニューの属性情報文字列
462         * @og.rtnNotNull
463         */
464        public static String selectAttri( final Attributes attri ) {
465                return attri.getAttribute( SELECT_KEY );
466        }
467
468        /**
469         * HIDDEN フォームを作成します。
470         *
471         * id属性に、name と同じ値が設定されます。
472         *
473         * @og.rev 5.5.4.0 (2012/07/02) ID属性追加
474         *
475         * @param   name  フォームの名前
476         * @param   value 値
477         *
478         * @return  HIDDENフォームタグ文字列
479         * @og.rtnNotNull
480         */
481        public static String hidden( final String name,final String value ) {
482                return hidden( name,value,name );
483        }
484
485        /**
486         * HIDDEN フォームを作成します。
487         *
488         * @og.rev 5.5.4.0 (2012/07/02) ID属性追加
489         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
490         *
491         * @param   name  フォームの名前
492         * @param   value 値
493         * @param   id    フォームのID
494         *
495         * @return  HIDDENフォームタグ文字列
496         * @og.rtnNotNull
497         */
498        public static String hidden( final String name, final String value, final String id ) {
499
500                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
501                return new TagBuffer( "input" )
502                                        .add(     "type"  , "hidden"  )
503                                        .add(     "name"  , name  )
504                                        .add(     "value" , value )
505                                        .add(     "id"    , id    )
506                                        .makeTag();
507        }
508
509        /**
510         * テキストエリアを作成します。
511         *
512         * &lt;textarea name="名前" rows="4" cols="40"  ....&gt;送信文字列 &lt;/textarea&gt;
513         *
514         * <table class="plain">
515         *   <caption>Attributes に設定できる属性</caption>
516         *   <tr><td>name="名前"</td><td>オプション</td><td>LabelResource.properties のキー</td></tr>
517         *   <tr><td>rows="行数"</td><td>オプション</td><td>入力フィールドの表示行数</td></tr>
518         *   <tr><td>cols="幅"</td><td>オプション</td><td>入力フィールドの表示幅(文字数)</td></tr>
519         *   <tr><td>disabled="disabled"</td><td>オプション</td><td>選択や変更の操作をできない状態にする場合に指定</td></tr>
520         *   <tr><td>tabindex="Tab移動順"</td><td>オプション</td><td>0~32767の範囲で数字で指定(小さい順に移動)</td></tr>
521         *   <tr><td>accesskey="ショートカットキー"</td><td>オプション</td><td>文字セット中の1文字:WindowsであればAltキーと同時使用</td></tr>
522         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
523         *   <tr><td>value="値"</td><td>オリジナル</td><td>name属性と共に送信される値</td></tr>
524         *   <tr><td>body="表示するタグ文字列"</td><td>オリジナル</td><td>画像や文字などボタン上に表示させたいタグの文字列</td></tr>
525         *   <tr><td>サポート外</td><td>未実装</td><td>readonly属性</td></tr>
526         * </table>
527         *
528         * 設定できる属性
529         *
530         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
531         * 文字を自由に登録する事が出来ます。
532         * CSSでクラスを対応 class="XXXX"
533         * タブで移動順を指定する tabindex="タブ順"
534         * ショートカットキーを割り当てる accesskey="ショートカットキー"
535         *
536         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
537         * @og.rev 8.0.0.0 (2021/07/31) optionAttributes が設定されなかったので、追加
538         *
539         * @param   attri 属性群
540         *
541         * @return  入力フォームタグ文字列
542         * @og.rtnNotNull
543         */
544        public static String textarea( final Attributes attri ) {
545                String body   = attri.get( "body" );
546                if( body == null ) { body = "" ; }
547
548                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
549                return new TagBuffer( "textarea" )
550                                        .add(     attri.getAttribute( TEXTAREA_KEY ) )
551                                        .add(     attri.get( "optionAttributes" ) )             // 8.0.0.0 (2021/07/31)
552                                        .addBody( body )
553                                        .makeTag();
554        }
555
556        /**
557         * ページリンクを作成します。
558         *
559         * &lt;A href="URL" target="ターゲット名"&gt;ラベル&lt;/A&gt;
560         *
561         * <table class="plain">
562         *   <caption>Attributes に設定できる属性</caption>
563         *   <tr><td>href="URL"</td><td>必須</td><td>リンク先のURLを指定します。</td></tr>
564         *   <tr><td>charset="文字セット"</td><td>オプション</td><td>リンク先の文字コードセットを指定します。</td></tr>
565         *   <tr><td>hreflang="言語セット"</td><td>オプション</td><td>リンク先の基本となる言語コードを指定します。</td></tr>
566         *   <tr><td>type="MIMEタイプ"</td><td>オプション</td><td>リンク先のMIMEタイプを指定します。</td></tr>
567         *   <tr><td>name="名前"</td><td>オプション</td><td>この要素をリンクの到達点とするための名前を指定します。</td></tr>
568         *   <tr><td>rel="リンクタイプ"</td><td>オプション</td><td>この文書からみた href 属性で指定されるリンク先との関係</td></tr>
569         *   <tr><td>rev="リンクタイプ"</td><td>オプション</td><td>href 属性で指定されるリンク先からみた、この文書との関係</td></tr>
570         *   <tr><td>tabindex="Tab移動順"</td><td>オプション</td><td>0~32767の範囲で数字で指定(小さい順に移動)</td></tr>
571         *   <tr><td>accesskey="ショートカットキー"</td><td>オプション</td><td>文字セット中の1文字:WindowsであればAltキーと同時使用</td></tr>
572         *   <tr><td>target="フレーム名"</td><td>オプション</td><td>リンク先のフレーム名</td></tr>
573         *   <tr><td>body="表示するタグ文字列"</td><td>オリジナル</td><td>画像や文字などをリンクにできます。</td></tr>
574         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
575         *   <tr><td>サポート外</td><td>未実装</td><td>shape属性、coords属性</td></tr>
576         * </table>
577         *
578         * 設定できる属性
579         *
580         * ラベルなしの場合、href属性の "URL" そのものを付けます。
581         *
582         * target属性のフレーム名は
583         *
584         *  _top        フレームを解除して、リンク先をフレーム全体に表示する。
585         *  _parent リンク先を親フレームに表示する。
586         *  _self   リンク先を自分自身に表示する。
587         *  _blank  新しいウインドウを開いて表示する。
588         *  その他  フレーム作成時の名前で指定可能。
589         *
590         * を指定します。
591         * なしの場合 _self (自分自身)を指定します。
592         *
593         * リンクメール機能
594         * URLを mailto:メールアドレス で設定すれば、メール送信ダイアログを
595         * 開く事が出来ます。
596         * 画像リンク機能
597         * 画像をクリックするリンクは、ラベルの個所に &lt;img&gt;タグを設定します。
598         *
599         * &lt;a href="books.html"&gt;&lt;img src="banner.gif" width="468px" height="60px" alt="関連書籍紹介" border="0"&gt;&lt;/a&gt;
600         *
601         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
602         * 文字を自由に登録する事が出来ます。
603         * CSSでクラスを対応 class="XXXX"
604         * タブで移動順を指定する tabindex="タブ順"
605         * ショートカットキーを割り当てる accesskey="ショートカットキー"
606         *
607         * @param   attri 属性群
608         *
609         * @return  ページリンクタグ文字列
610         * @og.rtnNotNull
611         */
612        public static String link( final Attributes attri ) {
613                return link( attri,"" );
614        }
615
616        /**
617         * ページリンクを作成します。
618         *
619         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
620         *
621         * @param   attri 属性群
622         * @param   urlEncode 文字列   ( ?key1=val1&amp;・・・・ という文字列 無いときは "" )
623         *
624         * @return  ページリンクタグ文字列
625         * @og.rtnNotNull
626         */
627        public static String link( final Attributes attri, final String urlEncode ) {
628                final String href = attri.get( "href" );
629                final String url  = addUrlEncode( href,urlEncode );
630                final String body = StringUtil.nval( attri.get( "body" ), href );
631
632                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
633                return new TagBuffer( "a" )
634                                        .add(     "href"  , url )
635                                        .add(     attri.getAttribute( LINK_KEY ) )
636                                        .addBody( body )                                                        // body が null の場合、href を代わりに出力
637                                        .makeTag();
638        }
639
640        /**
641         * xlink 形式のページリンクを作成します。
642         *
643         * 基本的には、link と同じです。アドレスの指定も、href で指定してください。
644         * 内部的に、xlink:href に変換します。
645         * また、URL引数を、"&amp;" で結合するのではなく、"&amp;amp;" で結合させます。
646         * これは、xlink そのものが、XML上に記述された場合に、XMLのルールで再度パース
647         * される為です。
648         *
649         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
650         *
651         * @param   attri 属性群
652         * @param   urlEncode 文字列   ( ?key1=val1&amp;・・・・ という文字列 無いときは "" )
653         *
654         * @return  ページリンクタグ文字列
655         * @og.rtnNotNull
656         */
657        public static String xlink( final Attributes attri, final String urlEncode ) {
658                final String href = attri.get( "href" );
659                final String url  = addUrlEncode( href,urlEncode,"&amp;" );
660                final String body = StringUtil.nval( attri.get( "body" ), href );
661
662                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
663                return new TagBuffer( "a" )
664                                        .add(     "xlink:href"  , url )
665                                        .add(     attri.getAttribute( LINK_KEY ) )
666                                        .addBody( body )                                                        // body が null の場合、href を代わりに出力
667                                        .makeTag();
668        }
669
670        /**
671         * メニューを作成します。
672         *
673         * @param   attri 属性群
674         * @param   opt 選択肢(オプション)
675         *
676         * @return  メニュータグ文字列
677         * @og.rtnNotNull
678         */
679        public static String select( final Attributes attri,final Options opt ) {
680                final String name     = attri.get( "name" );
681                final String optAttri = attri.get( "optionAttributes" );
682
683                return select( attri,opt,name,optAttri );
684        }
685
686        /**
687         * メニューを作成します。
688         *
689         * &lt;select size="行数" name="名前" multiple&gt;
690         *   &lt;option value="送信文字1"&gt;コメント&lt;/option&gt;
691         *   &lt;option value="送信文字2"&gt;コメント&lt;/option&gt;
692         *   &lt;option value="送信文字3" selected="selected"&gt;コメント&lt;/option&gt;
693         * &lt;/select&gt;
694         *
695         * <table class="plain">
696         *   <caption>Attributes に設定できる属性</caption>
697         *   <tr><td>name="名前"</td><td>オプション</td><td>LabelResource.properties のキー</td></tr>
698         *   <tr><td>size="行数"</td><td>オプション</td><td>select要素をリストボックスとして表示する場合の行数</td></tr>
699         *   <tr><td>multiple="multiple"</td><td>オプション</td><td>選択肢の中から複数選択出来るようにする。</td></tr>
700         *   <tr><td>disabled="disabled"</td><td>オプション</td><td>選択や変更の操作をできない状態にする場合に指定</td></tr>
701         *   <tr><td>tabindex="Tab移動順"</td><td>オプション</td><td>0~32767の範囲で数字で指定(小さい順に移動)</td></tr>
702         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
703         * </table>
704         *
705         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
706         * 文字を自由に登録する事が出来ます。
707         * CSSでクラスを対応 class="XXXX"
708         *
709         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
710         *
711         * @param   attri       属性群
712         * @param   opt         選択肢(オプション)
713         * @param   name        名前
714         * @param   optAttri オプション属性
715         *
716         * @return  メニュータグ文字列
717         * @og.rtnNotNull
718         */
719        public static String select( final Attributes attri,final Options opt,final String name,final String optAttri ) {
720
721                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
722                return new TagBuffer( "select" )
723                                        .add(     "name"  , name )
724                                        .add(     attri.getAttribute( SELECT_KEY ) )
725                                        .add(     optAttri )
726                                        .addBody( opt.getOption() )
727                                        .makeTag();
728        }
729
730        /**
731         * オプションを作成します。
732         *
733         * &lt;select size="行数" name="名前" multiple&gt;
734         *   &lt;option value="送信文字1"&gt;コメント&lt;/option&gt;
735         *   &lt;option value="送信文字2"&gt;コメント&lt;/option&gt;
736         *   &lt;option value="送信文字3" selected="selected"&gt;コメント&lt;/option&gt;
737         * &lt;/select&gt;
738         *
739         * <table class="plain">
740         *   <caption>Attributes に設定できる属性</caption>
741         *   <tr><td>value="値"</td><td>オプション</td><td>送信する値</td></tr>
742         *   <tr><td>selected="selected"</td><td>オプション</td><td>選択肢をあらかじめ選択された状態にしておく</td></tr>
743         *   <tr><td>disabled="disabled"</td><td>オプション</td><td>選択や変更の操作をできない状態にする場合に指定</td></tr>
744         *   <tr><td>body="表示するタグ文字列"</td><td>オリジナル</td><td>選択肢に表示させたいタグの文字列</td></tr>
745         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
746         * </table>
747         *
748         * セレクタとは、リストボックスやメニューなどの option引数にセットする
749         * 複数のデータをoptionタグでくるんだものです。
750         *
751         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
752         *
753         * @param   attri 属性群
754         *
755         * @return  オプションタグ文字列
756         * @og.rtnNotNull
757         * @see         #option( Attributes,String )
758         */
759        public static String option( final Attributes attri ) {
760                final String body = StringUtil.nval( attri.get( "body" ), " " );        // "No Label" を止めてスペースにする。
761
762                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
763                return new TagBuffer( "option" )
764                                        .add(     attri.getAttribute( OPTION_KEY ) )
765                                        .addBody( body )
766                                        .makeTag();
767        }
768
769        /**
770         * オプションを作成します。
771         *
772         * これは、addKeyLabel引数を考慮した、オプションタグ文字列の作成処理を行います。
773         * true を指定すると、「強制的に」キー:ラベル形式で表示します。
774         * false の場合は、「強制的に」ラベルのみで表示されます。
775         * 初期値の null の場合、指定のままのラベルで作成します。
776         *
777         * @og.rev 6.0.4.0 (2014/11/28) キー:ラベル形式で表示するかどうか。新規追加
778         *
779         * @param   attri               属性群
780         * @param   addKeyLabel true:キー:ラベル形式/false:ラベルのみ/null:指定通り
781         *
782         * @return  オプションタグ文字列
783         * @og.rtnNotNull
784         * @see         #option( Attributes )
785         */
786        public static String option( final Attributes attri , final String addKeyLabel ) {
787                if( addKeyLabel != null ) {
788                        final String val = attri.get( "value" );
789                        String lbl = attri.get( "label" );
790                        String lblKey = "label" ;
791                        if( lbl == null ) {             // label がなければ、body が表示されるので。
792                                lbl = attri.get( "body" );
793                                lblKey = "body" ;
794                        }
795
796                        if( val != null && lbl != null ) {
797                                final boolean isKeyLbl = "true".equalsIgnoreCase( addKeyLabel );
798                                final boolean useKey   = lbl.startsWith( val + ':' ) ;  // すでに、ラベルにキーが付与されている。
799
800                                // addKeyLabel するが、ラベルが付与されていない。
801                                if( isKeyLbl && !useKey ) {
802                                        attri.set( lblKey , val + ':' + lbl );
803                                }
804                                // addKeyLabel しないが、ラベルが付与されている。
805                                else if( !isKeyLbl && useKey ) {
806                                        attri.set( lblKey , lbl.substring( (val + ':').length() ) );    // ラベルからキー部分を削除する。
807                                }
808                        }
809                }
810
811                return option( attri );
812        }
813
814        /**
815         * フレームタグを作成します。
816         *
817         * &lt;frame marginheight="2px" marginwidth="2px" src="query.jsp" name="QUERY" /&gt;
818         *
819         * <table class="plain">
820         *   <caption>Attributes に設定できる属性</caption>
821         *   <tr><td>src="URL"</td><td>オプション</td><td>フレームの表示先URLを指定します。</td></tr>
822         *   <tr><td>name="フレーム名"</td><td>オプション</td><td>フレームに付ける名前を指定します。</td></tr>
823         *   <tr><td>longdesc="URI"</td><td>オプション</td><td>フレームの詳しい説明のURI</td></tr>
824         *   <tr><td>marginwidth="左右のマージン"</td><td>オプション</td><td>フレーム内の左右のマージンを指定します。</td></tr>
825         *   <tr><td>marginheight="上下のマージン"</td><td>オプション</td><td>フレーム内の上下のマージンを指定します。</td></tr>
826         *   <tr><td>noresize="noresize"</td><td>オプション</td><td>フレームサイズを変更できないようにします。</td></tr>
827         *   <tr><td>scrolling="スクロールの制御"</td><td>オプション</td><td>yes:スクロールバーを表示 no:表示しない auto:必要に応じて表示(デフォルト)</td></tr>
828         *   <tr><td>frameborder="枠の表示"</td><td>オプション</td><td>0:枠を表示しない  1:枠を表示する。(デフォルト)</td></tr>
829         *   <tr><td>keys="引数にセットするキー"</td><td>オプション</td><td>URI の引数にセットするキーを CSV形式でセットします。</td></tr>
830         *   <tr><td>value="引数にセットする値"</td><td>オプション</td><td>URI の引数にセットする値を CSV形式でセットします。</td></tr>
831         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style</td></tr>
832         * </table>
833         *
834         * 設定できる属性
835         *
836         * scrolling属性
837         *
838         *  yes:常にスクロールバーを表示
839         *  no:常にスクロールバーを表示しない
840         *  auto:必要に応じてスクロールバーを表示(デフォルト)
841         *
842         * を指定します。
843         *
844         * frameborder属性
845         *
846         *  0:枠を表示しない
847         *  1:枠を表示する。(デフォルト)
848         *
849         * を指定します。
850         *
851         * 属性群は、タグの中にCSS等で使用できる class="XXX" などの
852         * 文字を自由に登録する事が出来ます。
853         * CSSでクラスを対応 class="XXXX"
854         *
855         * @param   attri 属性群
856         *
857         * @return  フレームタグ文字列
858         * @og.rtnNotNull
859         */
860        public static String frame( final Attributes attri ) {
861                return frame( attri,"" );
862        }
863
864        /**
865         * フレームタグを作成します。
866         *
867         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
868         *
869         * @param   attri 属性群
870         * @param   urlEncode 文字列   ( ?key1=val1&amp;・・・・ という文字列 無いときは "" )
871         *
872         * @return  フレームタグ文字列
873         * @og.rtnNotNull
874         */
875        public static String frame( final Attributes attri,final String urlEncode ) {
876                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
877                return new TagBuffer( "frame" )
878                                        .add(    "src" , addUrlEncode( attri.get( "src" ),urlEncode ) )
879                                        .add(     attri.getAttribute( FRAME_KEY ) )
880                                        .makeTag();
881        }
882
883        /**
884         * インラインフレームタグを作成します。
885         *
886         * @param   attri 属性群
887         * @param   urlEncode 文字列   ( ?key1=val1&amp;・・・・ という文字列 無いときは "" )
888         *
889         * @og.rev 5.9.1.2 (2015/10/23) 新規追加
890         * @og.rev 6.3.9.0 (2015/11/06) TagBuffer を使用するように変更
891         * @og.rev 6.3.9.0 (2015/11/06) TagBuffer を使用するように変更
892         * @og.rev 7.4.2.0 (2021/05/08) optionAttributes 追加
893         *
894         * @return  インラインフレームタグ文字列
895         */
896        public static String iframe( final Attributes attri,final String urlEncode ) {
897
898                return new TagBuffer( "iframe" )
899                                        .add(    "src" , addUrlEncode( attri.get( "src" ),urlEncode ) )
900                                        .add(     attri.getAttribute( IFRAME_KEY ) )
901                                        .add(     attri.get( "optionAttributes" ) )                                     // 7.4.2.0 (2021/05/08)
902                                        .addBody( "<!-- -->" )  // iframeは自己終了できない
903                                        .makeTag();
904        }
905
906        /**
907         * URLエンコード文字列を作成します。
908         * エンコードすべき文字列が無い場合は、0ストリング("") を返します。
909         * エンコード文字列がある場合は、"?KEY1=VAL1&amp;KEY2=VAL2&amp;・・・" という文字列を
910         * 返します。
911         * つまり、どちらのケースでも、URI に 連結させればよいことになります。
912         *
913         * @param   keys   URLの引数となるキー群
914         * @param   values URLの引数となる値群
915         *
916         * @return  URLエンコード文字列
917         * @og.rtnNotNull
918         */
919        public static String urlEncode( final String keys,final String values ) {
920                return urlEncode( keys,values,"&" );
921        }
922
923        /**
924         * URLエンコード文字列を作成します。
925         * エンコードすべき文字列が無い場合は、0ストリング("") を返します。
926         * エンコード文字列がある場合は、"?KEY1=VAL1&amp;KEY2=VAL2&amp;・・・" という文字列を
927         * 返します。
928         * つまり、どちらのケースでも、URI に 連結させればよいことになります。
929         *
930         * @param   keys   URLの引数となるキー群
931         * @param   values URLの引数となる値群
932         * @param   join   URLの引数群を連結させる文字列
933         *
934         * @return  URLエンコード文字列
935         * @og.rtnNotNull
936         */
937        public static String urlEncode( final String keys,final String values,final String join ) {
938                if( keys == null || values == null ) { return ""; }
939
940                final String[] key = StringUtil.csv2Array( keys );
941                final String[] val = StringUtil.csv2Array( values );
942
943                return urlEncode( key,val,join ) ;
944        }
945
946        /**
947         * URLエンコード文字列を作成します。
948         * エンコードすべき文字列が無い場合は、0ストリング("") を返します。
949         * エンコード文字列がある場合は、"?KEY1=VAL1&amp;KEY2=VAL2&amp;・・・" という文字列を
950         * 返します。
951         * つまり、どちらのケースでも、URI に 連結させればよいことになります。
952         *
953         * @param   key   URLの引数となるキーの配列
954         * @param   val   URLの引数となる値の配列
955         *
956         * @return  URLエンコード文字列
957         * @og.rtnNotNull
958         */
959        public static String urlEncode( final String[] key,final String[] val ) {
960                return urlEncode( key,val,"&" );
961        }
962
963        /**
964         * URLエンコード文字列を作成します。
965         * エンコードすべき文字列が無い場合は、0ストリング("") を返します。
966         * エンコード文字列がある場合は、"?KEY1=VAL1&amp;KEY2=VAL2&amp;・・・" という文字列を
967         * 返します。
968         * つまり、どちらのケースでも、URI に 連結させればよいことになります。
969         *
970         * @og.rev 5.10.15.3 (2019/09/27) fenc追加対応で作成
971         *
972         * @param   key   URLの引数となるキーの配列
973         * @param   val   URLの引数となる値の配列
974         * @param   join   URLの引数群を連結させる文字列
975         *
976         * @return  URLエンコード文字列
977         */
978        public static String urlEncode( final String[] key,final String[] val,final String join ) {
979                return urlEncode( key, val, join, false);
980        }
981
982        /**
983         * URLエンコード文字列を作成します。
984         * エンコードすべき文字列が無い場合は、0ストリング("") を返します。
985         * エンコード文字列がある場合は、"?KEY1=VAL1&amp;KEY2=VAL2&amp;・・・" という文字列を
986         * 返します。
987         * つまり、どちらのケースでも、URI に 連結させればよいことになります。
988         *
989         * @og.rev 4.3.3.3 (2008/10/22) valに対して副作用を及ぼさないように修正
990         * @og.rev 5.10.12.4 (2019/06/21) keyのURIエンコードを追加
991         * @og.rev 5.10.15.3 (2019/09/27) fencフラグ追加
992         *
993         * @param   key   URLの引数となるキーの配列
994         * @param   val   URLの引数となる値の配列
995         * @param   join   URLの引数群を連結させる文字列
996         * @param   fenc  先頭が[の場合でも強制エンコードするためのフラグ(初期値false)
997         *
998         * @return  URLエンコード文字列
999         * @og.rtnNotNull
1000         */
1001//      public static String urlEncode( final String[] key,final String[] val,final String join ) {
1002        public static String urlEncode( final String[] key,final String[] val,final String join, final boolean fenc ) {
1003                if( key == null || key.length == 0 || val == null || val.length == 0 ) {
1004                        return "";
1005                }
1006                else if( key.length != val.length ) {
1007                        final String errMsg = "urlEncode のキーとバリューの個数が異なります。" + CR
1008                                                + "key.length=[" + key.length + "]  val.length=[" + val.length + "]";
1009                        throw new OgRuntimeException( errMsg );
1010                }
1011
1012                // 4.3.3.3 (2008/10/22)
1013                String[] tval = new String[val.length];
1014
1015                for( int i=0; i<val.length; i++ ) {
1016                        if( key[i] == null || key[i].isEmpty() ) { return ""; }
1017                        if( val[i] == null || val[i].isEmpty() ) { tval[i] = ""; }
1018//                      else if( val[i].charAt(0) == '[' ) {            // 暫定対応
1019                        else if( val[i].charAt(0) == '[' && !fenc ) {           // 5.10.15.3 (2019/09/27) fenc追加
1020                                tval[i] = val[i];
1021                        }
1022                        else {
1023                                tval[i] = StringUtil.urlEncode( val[i] );
1024                        }
1025                }
1026
1027                final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
1028
1029                // 6.0.2.5 (2014/10/31) char を append する。
1030//              rtn.append( key[0] ).append( '=' ).append( tval[0] );
1031                rtn.append( StringUtil.urlEncode( key[0]) ).append( '=' ).append( tval[0] );    // 5.10.12.4 (2019/06/21)
1032                for( int i=1; i<key.length; i++ ) {
1033                        rtn.append( join );
1034//                      rtn.append( key[i] ).append( '=' ).append( tval[i] );
1035                        rtn.append( StringUtil.urlEncode(key[i]) ).append( '=' ).append( tval[i] );     // 5.10.12.4 (2019/06/21)
1036                }
1037                return rtn.toString();
1038        }
1039
1040        /**
1041         * URL文字列に、URLエンコード文字列を連結します。
1042         *
1043         * URL文字列中にすでに "?" 文字が存在する場合は、URLエンコード側の
1044         * 文字列とは、 "&amp;" で連結します。
1045         * 逆に、"?" が存在しなければ、"?" で連結します。
1046         * URLエンコード文字列が null の場合は、連結しません。
1047         *
1048         * @param   url URL文字列
1049         * @param   encode URLエンコード文字列
1050         *
1051         * @return  連結文字列
1052         * @og.rtnNotNull
1053         */
1054        public static String addUrlEncode( final String url,final String encode ) {
1055                return addUrlEncode( url,encode,"&" );
1056        }
1057
1058        /**
1059         * URL文字列に、URLエンコード文字列を連結します。
1060         *
1061         * URL文字列中にすでに "?" 文字が存在する場合は、URLエンコード側の
1062         * 文字列とは、 join (例 "&amp;" ) で連結します。
1063         * 逆に、"?" が存在しなければ、"?" で連結します。
1064         * URLエンコード文字列が null の場合は、連結しません。
1065         * 連結する、encode 文字列の先頭が、join 文字列の場合、そのまま連結します。
1066         * 先頭が、そうでない場合は、join 文字列で連結します。
1067         * "?" が存在せず、encode 文字列の先頭が、join 文字列の場合は、
1068         * encode 文字列の先頭を取り除いて、"?" で連結します。
1069         *
1070         * 例:
1071         *    ①. abc.html    key1=val1&amp;key2=val2      ⇒ abc.html?key1=val1&amp;key2=val2
1072         *    ②.abc.html   &amp;key1=val1&amp;key2=val2  ⇒ abc.html?key1=val1&amp;key2=val2
1073         *    ③.abc.html?key1=val1    key2=val2          ⇒ abc.html?key1=val1&amp;key2=val2
1074         *    ④.abc.html?key1=val1   &amp;key2=val2      ⇒ abc.html?key1=val1&amp;key2=val2
1075         *
1076         * @og.rev 5.2.1.0 (2010/10/01) urlがnullの場合に、NullPointerExceptionが発生するバグを修正
1077         *
1078         * @param   url URL文字列
1079         * @param   encode URLエンコード文字列
1080         * @param   join   URLの引数群を連結させる文字列
1081         *
1082         * @return  連結文字列
1083         * @og.rtnNotNull
1084         */
1085        public static String addUrlEncode( final String url,final String encode,final String join ) {
1086                // 5.2.1.0 (2010/10/01) urlがnullの場合に、NullPointerExceptionが発生するバグを修正
1087                final String tmpUrl = ( url == null ? "" : url );
1088
1089                if( encode == null || encode.isEmpty() ) { return tmpUrl; }
1090
1091                final String rtn ;
1092                if( tmpUrl.indexOf( '?' ) < 0 ) {
1093                        if( encode.startsWith( join ) ) {
1094                                rtn = tmpUrl + "?" + encode.substring(join.length());           // ②
1095                        }
1096                        else {
1097                                rtn = tmpUrl + "?" + encode;                                                            // ①
1098                        }
1099                }
1100                else {
1101                        if( encode.startsWith( join ) ) {
1102                                rtn = tmpUrl + encode;                                          // ④
1103                        }
1104                        else {
1105                                rtn = tmpUrl + join + encode;                           // ③
1106                        }
1107                }
1108                return rtn ;
1109        }
1110
1111        /**
1112         * 指定位置に画像を配置します。
1113         *
1114         * alt属性だけ設定されて、title属性が未設定の場合、title属性にもalt属性と同じ値を設定しておきます。
1115         * 本来は、それぞれに役割があるので、同じ値を設定するのは行儀が悪いのですが、
1116         * ブラウザの互換性など、色々な関係で、このように設定しています。
1117         * alt属性   :「その画像が表している”文脈”」を書く
1118         * title属性 :「画像」の補足説明を書く
1119         *
1120         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1121         * @og.rev 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。
1122         *
1123         * @param   attri 属性群
1124         *
1125         * @return  イメージタグ文字列
1126         * @og.rtnNotNull
1127         */
1128        public static String img( final Attributes attri ) {
1129                // 6.4.2.0 (2016/01/29) title属性がなければ、alt属性を設定しておく。
1130                final String title = attri.get( "title" );
1131                if( title == null ) {
1132                        attri.set( "title" , attri.get( "alt" ) );
1133                }
1134
1135                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1136                return new TagBuffer( "img" )
1137                                        .add(     attri.getAttribute( IMAGE_KEY ) )
1138                                        .makeTag();
1139        }
1140
1141        /**
1142         * フォームを作成します。
1143         *
1144         * &lt;form action="URI" method="HTTPメソッド" enctype="MIMEタイプ" target="フレーム名" ・・・ &gt;フォーム等&lt;/form&gt;
1145         *
1146         * <table class="plain">
1147         *   <caption>Attributes に設定できる属性</caption>
1148         *   <tr><td>action="URI"</td><td>必須</td><td>送信されたフォームデータを処理するプログラムURI</td></tr>
1149         *   <tr><td>method="HTTPメソッド"</td><td>オプション</td><td>get/post</td></tr>
1150         *   <tr><td>enctype="MIMEタイプ"</td><td>オプション</td><td>フォームデータ送信時のMIMEタイプ</td></tr>
1151         *   <tr><td>accept-charset="文字セット"</td><td>オプション</td><td>データとして受付可能な文字セットの指定</td></tr>
1152         *   <tr><td>accept="MIMEタイプ"</td><td>オプション</td><td>データとして処理可能なMIMEタイプを指定</td></tr>
1153         *   <tr><td>name="名前"</td><td>オプション</td><td>スクリプト等から参照する場合の名前</td></tr>
1154         *   <tr><td>target="フレーム名"</td><td>オプション</td><td>フォームを送信した結果を表示させるフレーム</td></tr>
1155         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
1156         *   <tr><td>body="フォーム等の文字列"</td><td>必須</td><td>input 等のフォーム要素</td></tr>
1157         * </table>
1158         *
1159         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1160         *
1161         * @param   attri 属性群
1162         *
1163         * @return  フォームタグ文字列
1164         * @og.rtnNotNull
1165         */
1166        public static String form( final Attributes attri ) {
1167                String body   = attri.get( "body" );
1168                if( body == null ) { body = "" ; }
1169
1170                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1171                return new TagBuffer( "form" )
1172                                        .add(     attri.getAttribute( FORM_KEY ) )
1173                                        .addBody( body )
1174                                        .makeTag();
1175        }
1176
1177        /**
1178         * 汎用インライン要素(SPAN)を作成します。
1179         *
1180         * &lt;span class="XXXX" ・・・ &gt;テキスト等&lt;/span&gt;
1181         *
1182         * <table class="plain">
1183         *   <caption>Attributes に設定できる属性</caption>
1184         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
1185         *   <tr><td>body="テキスト等の文字列"</td><td>オプション</td><td>このテキストを修飾します。</td></tr>
1186         * </table>
1187         *
1188         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1189         *
1190         * @param   attri 属性群
1191         *
1192         * @return  SPANタグ文字列
1193         * @og.rtnNotNull
1194         */
1195        public static String span( final Attributes attri ) {
1196                String body   = attri.get( "body" );
1197                if( body == null ) { body = "" ; }
1198
1199                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1200                return new TagBuffer( "span" )
1201                                        .add(     attri.getAttribute( SPAN_KEY ) )
1202                                        .add(     attri.get( "optionAttributes" ) )
1203                                        .addBody( body )
1204                                        .makeTag();
1205        }
1206
1207        /**
1208         * 整形済みテキスト(PRE)を作成します。
1209         *
1210         * &lt;pre class="XXXX" ・・・ &gt;テキスト等&lt;/pre&gt;
1211         *
1212         * <table class="plain">
1213         *   <caption>Attributes に設定できる属性</caption>
1214         *   <tr><td>汎用属性</td><td>オプション</td><td>class,id,title,style,lang,dir,xml:lang</td></tr>
1215         *   <tr><td>body="テキスト等の文字列"</td><td>オプション</td><td>このテキストを修飾します。</td></tr>
1216         * </table>
1217         *
1218         * @og.rev 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1219         *
1220         * @param   attri 属性群
1221         *
1222         * @return  PREタグ文字列
1223         * @og.rtnNotNull
1224         */
1225        public static String pre( final Attributes attri ) {
1226                String body   = attri.get( "body" );
1227                if( body == null ) { body = "" ; }
1228
1229                // 6.2.0.0 (2015/02/27) TagBuffer を使用するように変更。
1230                return new TagBuffer( "pre" )
1231                                        .add(     attri.getAttribute( PRE_KEY ) )
1232                                        .add(     attri.get( "optionAttributes" ) )
1233                                        .addBody( body )
1234                                        .makeTag();
1235        }
1236
1237        /**
1238         * URLチェック用のキーを返します。
1239         *
1240         * 引数に指定されたhrefに対して、時間とユーザーIDを付加した暗号化文字列を
1241         * 引数に追加します。
1242         *
1243         * 暗号化は、org.opengion.fukurou.security.HybsCryptographyを使用します。
1244         * 暗号化を行う文字列のフォーマットは、[href],time=[checkTime],userid=[loginUser]です。
1245         *
1246         * @og.rev 4.3.7.1 (2009/06/08) 新規追加
1247         * @og.rev 4.3.7.4 (2009/07/01) 循環参照を解消
1248         * @og.rev 5.8.8.0 (2015/06/05) キー指定対応で別メソッドに処理を委譲
1249         *
1250         * @param   href チェック対象のURL
1251         * @param   key チェックキーのパラメーターキー
1252         * @param   userid ユーザーID
1253         * @param   time 有効時間
1254         *
1255         * @return  チェックキー
1256         * @og.rtnNotNull
1257         * @see org.opengion.fukurou.security.HybsCryptography
1258         */
1259        public static String addURLCheckKey( final String href, final String key, final String userid, final long time ) {
1260
1261                return addURLCheckKey( href, key, userid, time, null );
1262        }
1263
1264        /**
1265         * URLチェック用のキーを返します。
1266         *
1267         * 引数に指定されたhrefに対して、時間とユーザーIDを付加した暗号化文字列を
1268         * 引数に追加します。
1269         *
1270         * 暗号化は、org.opengion.fukurou.util.HybsCryptographyを使用します。
1271         * cryptを渡した場合はそのキーを利用して変換をかけますが、NULLの場合は標準キーで暗号化されます。
1272         * 暗号化を行う文字列のフォーマットは、[href],time=[checkTime],userid=[loginUser]です。
1273         *
1274         * @og.rev 5.8.8.0 (2015/06/05) 新規作成
1275         *
1276         * @param   href チェック対象のURL
1277         * @param   key チェックキーのパラメーターキー
1278         * @param   userid ユーザーID
1279         * @param   time 有効時間
1280         * @param       crypt 暗号化クラス
1281         *
1282         * @return  チェックキー
1283         * @see org.opengion.fukurou.security.HybsCryptography
1284         */
1285        public static String addURLCheckKey( final String href, final String key, final String userid, final long time, final HybsCryptography crypt ) {
1286                // 6.4.2.1 (2016/02/05) PMD refactoring. Prefer StringBuffer over += for concatenating strings
1287                final String orgChKey = href + ",time=" + time + ",userid=" + userid;
1288
1289                final String checkKey = key + "=" +
1290                                                crypt == null ? HYBS_CRYPTOGRAPHY.encrypt( orgChKey )
1291                                                                          : crypt.encrypt( orgChKey ) ;
1292
1293                return addUrlEncode( href , checkKey );
1294        }
1295
1296        /**
1297         * Aタグの文字列を解析して、href属性にURLチェック用の暗号化文字列を付加した形で、
1298         * Aタグを再構築し、返します。
1299         *
1300         * @og.rev 4.3.7.1 (2009/06/08) 新規追加
1301         * @og.rev 4.3.7.4 (2009/07/01) 循環参照を解消
1302         * @og.rev 5.8.8.0 (2015/06/05) キー指定対応で別メソッドに処理を委譲
1303         *
1304         * @param   tag Aタグ文字列
1305         * @param   key チェックキーのパラメーターキー
1306         * @param   userid ユーザーID
1307         * @param   time 有効時間
1308         *
1309         * @return  URLチェックキーが付加されたAタグ文字列
1310         * @og.rtnNotNull
1311         */
1312        public static String embedURLCheckKey( final String tag, final String key, final String userid, final long time  ) {
1313                return embedURLCheckKey( tag, key, userid, time, null  );
1314        }
1315
1316        /**
1317         * Aタグの文字列を解析して、href属性にURLチェック用の暗号化文字列を付加した形で、
1318         * Aタグを再構築し、返します。
1319         *
1320         * @og.rev 5.8.8.0 (2015/06/05) 新規作成
1321         *
1322         * @param   tag Aタグ文字列
1323         * @param   key チェックキーのパラメーターキー
1324         * @param   userid ユーザーID
1325         * @param   time 有効時間
1326         * @param       crypt 暗号化クラス
1327         *
1328         * @return  URLチェックキーが付加されたAタグ文字列
1329         */
1330        public static String embedURLCheckKey( final String tag, final String key, final String userid, final long time, final HybsCryptography crypt ) {
1331                String rtn = tag;
1332                final int hrefStr = rtn.indexOf( "href=\"" );
1333                if( hrefStr >= 0 ) {
1334                        final int hrefEnd = rtn.indexOf( "\"",hrefStr + 6 );
1335                        if( hrefEnd >= 0 ) {
1336                                String href = rtn.substring( hrefStr + 6, hrefEnd );
1337                                href = XHTMLTag.addURLCheckKey( href, key, userid, time, crypt );
1338                                rtn = rtn.substring( 0,  hrefStr ) + "href=\"" + href + rtn.substring( hrefEnd );
1339                        }
1340                }
1341                return rtn;
1342        }
1343}