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.concurrent.ConcurrentMap; // 6.4.3.3 (2016/03/04) 019import java.util.concurrent.ConcurrentHashMap; // 6.4.3.1 (2016/02/12) refactoring 020import java.util.Locale; 021import java.util.Set; // 8.5.3.0 (2023/09/08) Add 022 023import org.opengion.fukurou.system.OgRuntimeException; // 8.5.3.0 (2023/09/08) Add 024 025import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 026import static org.opengion.fukurou.system.HybsConst.CR; // 8.5.3.0 (2023/09/08) Add 027 028/** 029 * Attributes.java は、String 型キーにString型値を Map するクラスです。 030 * 031 * HTMLのPOST/GET等の受け渡しや、String型の引数が多い場合に効果があります。 032 * 特に、getAttributes( String[] param ) による属性リスト作成は、 033 * HTMLタグの属性定義を行う上で、非常に便利に利用できます。 034 * 035 * ※ 6.1.1.0 (2015/01/17) 036 * StringBuilder と同様、set メソッド , add メソッドの戻り値に、自分自身を戻します。 037 * これにより、連結処理できるようにします。 038 * 039 * この実装は同期化されません。 040 * 041 * @version 4.0 042 * @author Kazuhiko Hasegawa 043 * @since JDK5.0, 044 */ 045public final class Attributes { 046 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 047 private final ConcurrentMap<String,String> attMap ; // Map系変数は、Mapと判る様に命名する。 048 /** 厳密チェックフラグ */ 049 private boolean isStrict = true; // 8.5.3.0 (2023/09/08) Add 050 051 /** 052 * デフォルトコンストラクター 053 * 054 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 055 */ 056 public Attributes() { 057 attMap = new ConcurrentHashMap<>(); 058 } 059 060 /** 061 * Attributesオブジェクト を与えて新しく作成するコンストラクター 062 * 063 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 064 * 065 * @param att Attributesオブジェクト 066 */ 067 public Attributes( final Attributes att ) { 068 attMap = new ConcurrentHashMap<>( att.attMap ); // AttributesのattMapは、ConcurrentHashMap なので、not null保障 069 isStrict = att.isStrict; // 8.5.3.0 (2023/09/08) Add 070 } 071 072 /** 073 * マップからマッピングをすべて削除します。 074 * 075 */ 076 public void clear() { 077 attMap.clear() ; 078 isStrict = true; // 8.5.3.0 (2023/09/08) Add 079 } 080 081 /** 082 * 厳密にチェックするかどうか[true/false]を指定します(初期値:true)。 083 * 084 * 内部のHTMLタグの属性リストに 085 * 指定されているキーのみ使用したい場合は、true(初期値) を指定します。 086 * 指定されていないキーを使用したい場合は、false を指定します。 087 * 088 * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応 089 * 090 * @param flag 厳密チェックフラグ [true:厳密/false:甘い] 091 */ 092 public void useStrict( final boolean flag ) { 093 isStrict = flag; 094 } 095 096 /** 097 * マップが指定のキーをマップする値を返します。 098 * マップがこのキーのマッピングを保持していない場合は null 099 * を返します。戻り値の null は、マップがキーのマッピングを 100 * 保持していないことを示すとはかぎりません。つまり、マップが 101 * 明示的にキーを null にマップすることもあります。 102 * 103 * @param key 関連付けられた値が返されるキー(大文字小文字は同値) 104 * 105 * @return マップが、指定されたキーにマッピングしている値。 106 * このキーに対するマッピングがマップにない場合は null 107 */ 108 public String get( final String key ) { 109 return attMap.get( key.toLowerCase( Locale.JAPAN ) ) ; 110 } 111 112 /** 113 * 指定された値と指定されたキーをこのマップに関連付けます 114 * 指定されたキーに、null を関連付けることはできません。 115 * (もちろん、"":ゼロストリング は登録できます。) 116 * なぜなら、getAttribute( String[] keys ) 等で値が null の 117 * キーは、取得できない為です。 118 * また、すでに何らかの値がセットされている所に、null をセットした 119 * 場合は、前の値をなにも変更しません。 120 * 通常、値をクリアしたい場合は、remove( String key ) を利用してください。 121 * 122 * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。 123 * 124 * @param key 指定される値が関連付けられるキー(大文字小文字は同値) 125 * @param value 指定されるキーに関連付けられる値 126 * 127 * @return 自分自身 128 * @og.rtnNotNull 129 */ 130 public Attributes set( final String key,final String value ) { 131 if( value != null ) { 132 attMap.put( key.toLowerCase( Locale.JAPAN ),value ) ; 133 } 134 return this ; // 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返す 135 } 136 137 /** 138 * 指定された値と指定されたキーをこのマップに関連付けます 139 * set( String key,String value ) との違いは、value が null 140 * の場合に、def を代わりにセットすることです。 141 * ただし、value が null で、def も null の場合は、 142 * なにもセットされません。 143 * 144 * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。 145 * 146 * @param key 指定される値が関連付けられるキー(大文字小文字は同値) 147 * @param value 指定されるキーに関連付けられる値 148 * @param def value が null の場合にキーに関連付けられる値 149 * 150 * @return 自分自身 151 * @og.rtnNotNull 152 */ 153 public Attributes set( final String key,final String value,final String def ) { 154 if( value != null ) { attMap.put( key.toLowerCase( Locale.JAPAN ),value ) ; } 155 else if( def != null ) { attMap.put( key.toLowerCase( Locale.JAPAN ),def ) ; } 156 157 return this ; // 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返す 158 } 159 160 /** 161 * Attributes 属性を、既存の属性に上書き追加します。 162 * 163 * 引数 att が null の場合は、何もしません。 164 * 内部の Map に直接書き込みますので、すでに同一キーの属性が存在している場合は、 165 * 上書きで置き換えます。 166 * つまり、初期値を設定する場合は、最初に呼び出します。引数で設定された最新の値を 167 * 使用する場合は、最後に設定します。 168 * ただし、#add(String,String) は、既存のキーに値を追記していきますので、これらより 169 * 後に設定しないと、上書きされてしまいますので、ご注意ください。 170 * 171 * 従来の、#addAttributes( Attributes ) の代替えメソッドです。 172 * 173 * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。 174 * 175 * @param att Attributes属性 176 * 177 * @return 自分自身 178 * @og.rtnNotNull 179 */ 180 public Attributes set( final Attributes att ) { 181 if( att != null && !att.attMap.isEmpty() ) { 182 attMap.putAll( att.attMap ); // AttributesのattMapは、ConcurrentHashMap なので、not null保障 183 isStrict = att.isStrict; // 8.5.3.0 (2023/09/08) Add 184 } 185 186 return this ; // 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返す 187 } 188 189 /** 190 * 指定された値と指定されたキーをこのマップに追加します 191 * 192 * マップ自身のキーは、ユニークである為、既存の値に対して、 193 * 新しく値を追加します。 194 * 追加する方法は、値の文字列の結合です。このメソッドでは、 195 * デフォルトのスペースで結合します。 196 * 197 * 値が null または、すでにそのキーに同一の値が関連付けられている場合は、 198 * 何もしません。 199 * 200 * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。 201 * 202 * @param key 指定される値が関連付けられるキー(大文字小文字は同値) 203 * @param value 指定されるキーの値に、追加される値 204 * 205 * @return 自分自身 206 * @og.rtnNotNull 207 */ 208 public Attributes add( final String key,final String value ) { 209 return add( key,value," " ) ; // 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返す 210 } 211 212 /** 213 * 指定された値と指定されたキーをこのマップに追加します 214 * 215 * class属性や、style属性など、同一キーに対して、複数の値をつなげる場合に 216 * 使用します。 217 * 218 * マップ自身のキーは、ユニークである為、既存の値に対して、 219 * 新しく値を追加します。 220 * 追加する方法は、値の文字列の結合です。このメソッドでは、 221 * 引数 sepa で文字列を結合します。 222 * 223 * 値が null または、sepa が null または、すでにそのキーに 224 * 同一の値が関連付けられている場合は、何もしません。 225 * 226 * @og.rev 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返すようにします。 227 * 228 * @param key 指定される値が関連付けられるキー(大文字小文字は同値) 229 * @param value 指定されるキーの値に、追加される値 230 * @param sepa 値を連結するときの文字列 231 * 232 * @return 自分自身 233 * @og.rtnNotNull 234 */ 235 public Attributes add( final String key,final String value,final String sepa ) { 236 if( value != null && sepa != null ) { 237 final String lkey = key.toLowerCase( Locale.JAPAN ); 238 239 String temp = attMap.get( lkey ); 240 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 241 if( temp == null ) { 242 attMap.put( lkey,value ); 243 } 244 else { 245 temp = temp.trim(); 246 if( temp.indexOf( value ) < 0 || // 存在しない または、 247 ! temp.equals( value ) && // 一致しない 248 ! temp.startsWith( value + sepa ) && // 先頭にない 249 ! temp.endsWith( sepa + value ) && // 最終にない 250 temp.indexOf( sepa + value + sepa ) < 0 ) { // 途中にない // 6.9.7.0 (2018/05/14) PMD 251 if( temp.endsWith( sepa ) ) { 252 attMap.put( lkey,temp + value ); 253 } 254 else { 255 attMap.put( lkey,temp + sepa + value ); 256 } 257 } 258 } 259 } 260 261 return this ; // 6.1.1.0 (2015/01/17) 戻り値に、自分自身を返す 262 } 263 264 /** 265 * このキーにマッピングがある場合に、そのマッピングをマップから削除します。 266 * 267 * @param key マッピングがマップから削除されるキー(大文字小文字は同値) 268 * 269 * @return このキーにマッピングがある場合に、そのマッピングをマップから削除します 270 * 指定されたキーに関連した以前の値。key にマッピングがなかった場合は null。 271 */ 272 public String remove( final String key ) { 273 return attMap.remove( key.toLowerCase( Locale.JAPAN ) ); 274 } 275 276 /** 277 * マップ内のキーと値のマッピングの数を返します。 278 * 279 * @return インタフェース Map 内の size 280 */ 281 public int size() { 282 return attMap.size() ; 283 } 284 285// /** 286// * マップに含まれているキーと属性のペアを タグの属性リストの形式で返します。 287// * key1="value1" key2="value2" key3="value3" .... の形式で、value が null の 288// * 場合は、key そのもののペアを出力しません。 289// * value が空文字列 "" の場合は、key="" で出力します。 290// * 291// * 引数には、key として出力したい値を配列文字列で渡します。 292// * これは、拡張性に乏しい(すべて出せば、属性項目の追加に対応できる。) 293// * 方法ですが、タグ毎に異なる属性のみを管理するには、厳格に出力 294// * タグ属性を定義したいという思いから導入しました。 295// * 296// * @og.rev 6.0.4.0 (2014/11/28) 内部処理見直し。値の取得は、Mapから直接取得する。 297// * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加 298// * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応で廃止 299// * 300// * @param keys 指定 key の文字列配列(可変長引数)(大文字小文字は同値) 301// * 302// * @return キーと属性のペアをタグの属性リストの形式で返します 303// * @og.rtnNotNull 304// */ 305////public String getAttribute( final String[] keys ) { 306// public String getAttribute( final String... keys ) { 307// final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 308// 309// if( keys != null ) { 310// for( final String key : keys ) { 311// if( key != null ) { 312// final String value = attMap.get( key.toLowerCase( Locale.JAPAN ) ); // 6.0.4.0 (2014/11/28) Mapから直接取得する。 313// if( value != null ) { 314// // 6.0.2.5 (2014/10/31) char を append する。 315// buf.append( key ).append("=\"").append( value ).append("\" "); 316// } 317// } 318// } 319// } 320// 321// return buf.toString(); 322// } 323 324 /** 325 * マップに含まれているキーと属性のペアを タグの属性リストの形式で返します。 326 * key1="value1" key2="value2" key3="value3" .... の形式で、value が null の 327 * 場合は、key そのもののペアを出力しません。 328 * value が空文字列 "" の場合は、key="" で出力します。 329 * 330 * 引数には、key として出力したい値をSetで渡します。 331 * 332 * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応 333 * 334 * @param keys 指定 key のSet 335 * @return キーと属性のペアをタグの属性リストの形式で返します 336 */ 337 public String getAttribute( final Set<String> keys ) { 338 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 339 // 先に、厳密チェックフラグ の確認結果を返す。 340 if( !isStrict ) { return getAttribute(); } // 属性リスト外の属性も返す。 341 342 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 343 344 // 内部のHTMLタグの属性リストに指定されているキーのみ対象 345// if( isStrict ) { 346 attMap.forEach( (k,v) -> { 347 if( keys.contains( k ) ) { 348 buf.append( k ).append("=\"").append( v ).append("\" "); 349 } 350 else if( !k.startsWith( "p_" ) ) { 351 final String errMsg = "[" + k + "] 属性は使用できません。" + CR 352 + "使用したい場合は、useAttStrict=\"false\" を指定してください。"; 353 throw new OgRuntimeException( errMsg ); 354 } 355 }); 356 return buf.toString(); 357// } 358// 359// return getAttribute(); 360 } 361 362 /** 363 * マップに含まれているキーと属性のペアを タグの属性リストの形式ですべて返します。 364 * なお、value が null の場合は、key そのもののペアを出力しません。 365 * value が空文字列 "" の場合は、key="" で出力します。 366 * 367 * @og.rev 6.0.4.0 (2014/11/28) 内部処理見直し。値の取得は、Mapから直接取得する。 368 * @og.rev 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 369 * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応 370 * 371 * @return キーと属性のペアをタグの属性リストの形式で返します 372 * @og.rtnNotNull 373 */ 374 public String getAttribute() { 375 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 376 377 // 内部に持っているすべてのデータを出力する。 378 // よって、toLowerCase も、null チェックも不要。 379// attMap.forEach( (k,v) -> buf.append( k ).append("=\"").append( v ).append("\" ") ); 380 // 8.5.3.0 (2023/09/08) 381 attMap.forEach( (k,v) -> { 382 if( !k.startsWith( "p_" ) ) { 383 buf.append( k ).append("=\"").append( v ).append("\" "); 384 } 385 }); 386 387 return buf.toString(); 388 } 389 390// /** 391// * マップに含まれているキーと属性のペアを タグの属性リストの形式ですべて返します。 392// * なお、value が nullや、空文字列("") の場合は、key そのもののペアを出力しません。 393// * この文字列は、key:value; 形式で、文字列を作成します。value に、ダブルコーテーション 394// * は付けません。 395// * 396// * @og.rev 7.0.1.0 (2018/10/15) 新規作成 397// * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応(未使用の為廃止) 398// * 399// * @return キーと属性のペアをCSS形式で返します 400// * @og.rtnNotNull 401// */ 402// public String getCssFormat() { 403// final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 404// 405// // 内部に持っているすべてのデータを出力する。 406// // よって、toLowerCase も、null チェックも不要。 407// attMap.forEach( (k,v) -> { if( StringUtil.isNotNull(k) ) { buf.append( k ).append(':').append( v ).append("; "); } } ); 408// 409// return buf.toString(); 410// } 411 412// /** 413// * 指定の文字列を分解して、Mapを作成します。 414// * 『;』 で分解して、ここのキーと値を、『=』で分解します。 415// * 引数の値は、そのまま適用されますので、クオーテーション等は、付けないと想定しています。 416// * 417// * @og.rev 7.4.2.1 (2021/05/08) 418// * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応(Editor_UPLOAD2.javaへ移植) 419// * 420// * @param params キー=値; キー=値 形式の文字列 421// * 422// * @return キーと属性に分解したMap 423// * @og.rtnNotNull 424// */ 425// public ConcurrentMap<String,String> getParamMap( final String params ) { 426// final ConcurrentMap<String,String> optMap = new ConcurrentHashMap<>(); 427// if( params != null ) { 428// final String[] prms = params.split( ";" ); // 各項目は、『;』で分解する。 429// for( final String prm : prms ) { 430// final String[] keyval = prm.split( "=" ); // キーと値は、『=』で分解する。 431// if( keyval.length >= 2 ) { 432// final String val = keyval[1].replaceAll( "'","" ).replaceAll( "\"","" ); 433// optMap.put( keyval[0].trim() , val ); 434// } 435// } 436// } 437// return optMap ; 438// } 439 440 /** 441 * このオブジェクトの文字列表現を返します。 442 * 基本的にデバッグ目的に使用します。 443 * 444 * @return オブジェクトの文字列表現 445 * @og.rtnNotNull 446 */ 447 @Override // Object 448 public String toString() { 449 return getAttribute() ; 450 } 451}