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.hayabusa.taglib; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.fukurou.util.XHTMLTag; 021import org.opengion.fukurou.util.Attributes; 022import org.opengion.fukurou.util.ToString; // 6.1.1.0 (2015/01/17) 023import org.opengion.fukurou.util.ArraySet; // 6.4.3.4 (2016/03/11) 024 025import static org.opengion.fukurou.util.StringUtil.nval ; 026 027import java.io.File; 028import java.io.FileFilter; 029import java.io.Serializable; 030import java.util.Set; // 6.4.3.4 (2016/03/11) 031import java.util.Arrays; 032import java.util.Comparator; 033 034/** 035 * ファイルのプルダウンリストの作成するタグです。 036 * 037 * SelectタグのBODY部に指定します。 038 * 並び替えについては、このタグで指定しますが、ファイルの選別は、 039 * BODY 部に記述する fileWhere タグで指定します。 040 * 041 * @og.formSample 042 * ●形式:<og:fileOption from="…" value="[…]" ・・・ >・・・</og:fileOption> 043 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 044 * 045 * ●Tag定義: 046 * <og:fileOption 047 * from 【TAG】ファイルの検索元となるディレクトリを指定します (初期値:FILE_URL[=filetemp/]) 048 * value 【TAG】Optionの初期値で選ばれる値を指定します 049 * useDir 【廃止】(廃止:7.2.6.0 (2020/06/30))optionリストの作成を、ディレクトリの値で行います。 050 * nameOnly 【TAG】ファイルの拡張子を除いた名前部分のみの値で行います。7.2.4.0 (2020/05/11) 051 * groupDir 【TAG】optgroupを、ディレクトリの値で作成します(1レベルのみ)。 052 * orderBy 【TAG】検索した結果を表示する表示順をファイル属性名で指定します(初期値:自然順序) 053 * desc 【TAG】表示順を逆転するかどうか[true/false]を指定します(初期値:false) 054 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 6.8.0.0 (2017/06/02) 055 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 6.8.0.0 (2017/06/02) 056 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 6.8.0.0 (2017/06/02) 057 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 6.8.0.0 (2017/06/02) 058 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 6.8.0.0 (2017/06/02) 059 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 060 * > ... Body ... 061 * </og:fileOption> 062 * 063 * ●使用例 064 * ・<og:fileOption val1="ABCD" val2="{@value}" > 065 * <og:fileWhere startsWith="ABCD" ・・・ /> 066 * </og:fileOption> 067 * 068 * @og.rev 2.1.1.0 (2002/11/11) 新規作成 069 * @og.rev 4.0.0.0 (2005/01/31) 内部ロジック改定 070 * @og.group その他入力 071 * 072 * @version 4.0 073 * @author Kazuhiko Hasegawa 074 * @since JDK5.0, 075 */ 076public class FileOptionTag extends CommonTagSupport { 077 /** このプログラムのVERSION文字列を設定します。 {@value} */ 078 private static final String VERSION = "7.2.6.0 (2020/06/30)" ; 079 private static final long serialVersionUID = 726020200630L ; 080 081 private String orderBy ; // ソート項目 082// private boolean useDir ; // 6.3.4.0 (2015/08/01) 7.2.6.0 (2020/06/30) useDIR 属性 廃止 083 private boolean nameOnly ; // 7.2.4.0 (2020/05/11) 084 private boolean groupDir ; // 6.3.4.0 (2015/08/01) 085 private boolean desc ; // 降順フラグ 086 private String from = HybsSystem.sys( "FILE_URL" ); // 検索起点ファイル 087 private String selValue ; // 選択済み初期値にする場合 088 private transient FileFilter filter ; // FileWhere で指定したフィルター 089 090 // 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。 091 private static final Set<String> ORDER_BY_SET = new ArraySet<>( "NAME","LASTMODIFIED","FILE_LENGTH","LENGTH" ); 092 093 /** 094 * デフォルトコンストラクター 095 * 096 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 097 */ 098 public FileOptionTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 099 100 /** 101 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 102 * 103 * @og.rev 6.8.0.0 (2017/06/02) caseKey,caseVal,caseNN,caseNull 属性を追加 104 * 105 * @return 後続処理の指示( EVAL_BODY_BUFFERED ) 106 */ 107 @Override 108 public int doStartTag() { 109 // 6.8.0.0 (2017/06/02) caseKey,caseVal,caseNN,caseNull 属性を追加 110 return useTag() ? EVAL_BODY_BUFFERED : SKIP_BODY ; 111 } 112 113 /** 114 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。 115 * 116 * @return 後続処理の指示(SKIP_BODY) 117 */ 118 @Override 119 public int doAfterBody() { 120 return SKIP_BODY ; 121 } 122 123 /** 124 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 125 * 126 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 127 * @og.rev 6.3.4.0 (2015/08/01) useDir 属性と、groupDir 属性 を追加します。 128 * @og.rev 6.8.0.0 (2017/06/02) caseKey,caseVal,caseNN,caseNull 属性を追加 129 * 130 * @return 後続処理の指示 131 */ 132 @Override 133 public int doEndTag() { 134 debugPrint(); // 4.0.0 (2005/02/28) 135 // 6.8.0.0 (2017/06/02) caseKey,caseVal,caseNN,caseNull 属性を追加 136 if( useTag() ) { 137 final OptionAncestorIF select = (OptionAncestorIF)findAncestorWithClass( this, OptionAncestorIF.class ); 138 if( select == null ) { 139 final String errMsg = "<b>" + getTagName() + "タグは、SelectTag または、DatalistTag のBODY に記述する必要があります。</b>"; 140 throw new HybsSystemException( errMsg ); 141 } 142 final Comparator<File> comp = makeComparator( orderBy,desc ); 143 // makeLabel( select,comp ); 144 makeLabel( new File( from ) , select , comp , groupDir ); 145 } 146 147 return EVAL_PAGE ; 148 } 149 150 /** 151 * タグリブオブジェクトをリリースします。 152 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 153 * 154 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。 155 * @og.rev 6.3.4.0 (2015/08/01) useDir 属性と、groupDir 属性 を追加します。 156 * @og.rev 7.2.4.0 (2020/05/11) nameOnly 属性 を追加します。 157 * @og.rev 7.2.6.0 (2020/06/30) ディレクトリ処理を統一します。 158 */ 159 @Override 160 protected void release2() { 161 super.release2(); 162 orderBy = null; // ソート項目 163// useDir = false; // 6.3.4.0 (2015/08/01) 7.2.6.0 (2020/06/30) 164 nameOnly = false; // 7.2.4.0 (2020/05/11) 165 groupDir = false; // 6.3.4.0 (2015/08/01) 166 desc = false; // 降順フラグ 167 from = HybsSystem.sys( "FILE_URL" ); 168 filter = null; 169 selValue = null; 170 } 171 172 /** 173 * オプションを作成します。 174 * 175 * ファイル名を "value" に、 176 * BODY属性 に登録するOptionを作成します。 177 * 178 * @og.rev 5.3.4.0 (2011/04/01) FILE_LENGTH 追加 179 * 180 * @param orderBy ソートする属性 [NAME/LASTMODIFIED/FILE_LENGTH/LENGTH] 181 * @param desc 並び順 [true:昇順/false:降順] 182 * 183 * @return ファイル比較用のComparatorオブジェクト 184 */ 185 private Comparator<File> makeComparator( final String orderBy,final boolean desc ) { 186 if( orderBy == null ) { return null; } 187 188 Comparator<File> comp = null ; 189 190 if( "NAME".equalsIgnoreCase( orderBy ) ) { 191 comp = new NameComparator( desc ); 192 } 193 else if( "LASTMODIFIED".equalsIgnoreCase( orderBy ) ) { 194 comp = new ModifiedComparator( desc ); 195 } 196 // "LENGTH" を残すのは、互換性のため 197 else if( "FILE_LENGTH".equalsIgnoreCase( orderBy ) || "LENGTH".equalsIgnoreCase( orderBy ) ) { 198 comp = new LengthComparator( desc ); 199 } 200 201 return comp ; 202 } 203 204 /** 205 * オプションを作成します。 206 * 207 * ファイル名を "value" と、BODY属性 に登録するOptionを作成します。 208 * 209 * @og.rev 3.8.0.9 (2005/10/17) 複数選択可能時に全選択を設定する。 210 * @og.rev 6.3.4.0 (2015/08/01) useDir 属性と、groupDir 属性 を追加します。 211 * @og.rev 6.8.0.0 (2017/06/02) フォルダ内のファイルが無い場合は、階層に加えない。 212 * @og.rev 7.2.4.0 (2020/05/11) nameOnly 属性 を追加します。 213 * 214 * @param path 処理の開始パス名(ディレクトリ) 215 * @param select SelectTagオブジェクト 216 * @param comp 並び順を指定するためのComparatorオブジェクト 217 * @param grpDir フォルダ階層を、optgroup タグで表すかどうか [true:階層/false:1層] 218 * @return フォルダ内のファイルが無い場合は階層に加えないためのフラグ [true:階層/false:ファイルなし] 219 */ 220 private boolean makeLabel( final File path , final OptionAncestorIF select , final Comparator<File> comp , final boolean grpDir ) { 221 222 final File[] list = path.listFiles( filter ); 223 224 final boolean multipleAll = select.isMultipleAll(); // 3.8.0.9 (2005/10/17) 225 if( list != null ) { 226 Arrays.sort( list, comp ); 227 boolean isSet = false; // 6.8.0.0 (2017/06/02) 228 for( int i=0; i<list.length; i++ ) { 229// final String value = list[i].getName(); 230 String value = list[i].getName(); // 7.2.4.0 (2020/05/11) 書き換える 231 // 6.3.4.0 (2015/08/01) useDir 属性と、groupDir 属性 を追加 232 if( grpDir && list[i].isDirectory() ) { 233 select.addOption( "<optgroup label=\"" + value + "\">" ); 234 if( makeLabel( list[i] , select , comp , false ) ) { // リストに追加されたかどうかを判定します。 235 select.addOption( "</optgroup>" ); 236 isSet = true; // 6.8.0.0 (2017/06/02) 237 } 238 else { 239 select.removeLast(); // 6.8.0.0 (2017/06/02) リストに追加されなかった場合は、最後に追加した optgroup を削除します。 240 } 241 242 continue; 243 } 244// // 7.2.6.0 (2020/06/30) useDIR 属性 廃止 245// else { 246// // ディレクトリ時のuseDir=true と、ファイル時useDir=false でない場合( XOR )時は、処理しない。 247// if( list[i].isDirectory() ^ useDir ) { continue; } 248// } 249 250 // 7.2.4.0 (2020/05/11) nameOnly 属性 を追加します。 251 if( nameOnly ) { 252 final int ad = value.lastIndexOf( '.' ); 253 if( ad >= 0 ) { value = value.substring( 0,ad ); } 254 } 255 256 // 6.1.1.0 (2015/01/17) Attributesの連結記述 257 final String selected = ( selValue != null && selValue.equalsIgnoreCase( value ) ) || multipleAll 258 ? "selected" : null ; 259 260 select.addOption( 261 XHTMLTag.option( 262 new Attributes() 263 .set( "value" , value ) 264 .set( "selected", selected ) 265 .set( "body" , value ) 266 ) 267 ); 268 } 269 return isSet; // 6.8.0.0 (2017/06/02) 270 } 271 return false; // 6.8.0.0 (2017/06/02) フォルダ内のファイルが無い場合は、階層に加えない。 272 } 273 274 /** 275 * 【TAG】Optionの初期値で選ばれる値を指定します。 276 * 277 * @og.tag 278 * キーになるのは、ファイル属性の NAME です。(ディレクトリなしのファイル名) 279 * ここで value属性に指定した場合、このファイル名と(大文字小文字を無視して) 280 * 一致する場合に、プルダウンの初期値に表示されます。(selected 属性が設定される。) 281 * 282 * @param val 初期値で選ばれる値 283 */ 284 public void setValue( final String val ) { 285 selValue = nval( getRequestParameter( val ),selValue ); 286 } 287 288 /** 289 * 【廃止】optionリストの作成を、ディレクトリの値で行います(初期値:false)。 290 * 291 * @og.tag 292 * ファイル検索で、ディレクトリ名のリストで、オプションを作成します。 293 * 初期値は、false (ファイル名でリスト) です。 294 * 295 * @og.rev 6.3.4.0 (2015/08/01) useDir 属性の追加 296 * @og.rev 7.2.6.0 (2020/06/30) ディレクトリ処理を統一します。 297 * 298 * @param flag ディレクトリ名のリストで、オプションを作成するかどうか [true:ディレクトリ/false:ファイル] 299 */ 300 public void setUseDir( final String flag ) { 301// useDir = nval( getRequestParameter( flag ),useDir ); 302 } 303 304 /** 305 * 【TAG】ファイルの拡張子を除いた名前部分のみの値で行います(初期値:false)。 306 * 307 * @og.tag 308 * ファイル検索の値を、ファイルの拡張子を取り除いた値のみで、オプションを作成します。 309 * 初期値は、false (拡張子付きファイル名でリスト) です。 310 * 311 * @og.rev 7.2.4.0 (2020/05/11) nameOnly 属性 を追加します。 312 * 313 * @param flag ファイルの拡張子を除いた名前部分のみで、オプションを作成するかどうか [true:名前部分のみ/false:ファイル名] 314 */ 315 public void setNameOnly( final String flag ) { 316 nameOnly = nval( getRequestParameter( flag ),nameOnly ); 317 } 318 319 /** 320 * 【TAG】optgroupを、ディレクトリの値で作成します(1レベルのみ)(初期値:false)。 321 * 322 * @og.tag 323 * optgroupをディレクトリで作成することで、階層のメニューを作成します。 324 * 初期値は、false(通常) です。 325 * 326 * @og.rev 6.3.4.0 (2015/08/01) groupDir 属性の追加 327 * 328 * @param flag ディレクトリで階層メニューを作成するかどうか [true:階層/false:通常] 329 */ 330 public void setGroupDir( final String flag ) { 331 groupDir = nval( getRequestParameter( flag ),groupDir ); 332 } 333 334 /** 335 * 【TAG】ファイルの検索元となるディレクトリを指定します 336 * (初期値:FILE_URL[={@og.value SystemData#FILE_URL}])。 337 * 338 * @og.tag ファイルの検索元となるディレクトリを指定します。 339 * (初期値:システム定数のFILE_URL[={@og.value SystemData#FILE_URL}])。 340 * 341 * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。 342 * @og.rev 6.4.2.1 (2016/02/05) URLの最後に、"/" を追加する処理を廃止。 343 * @og.rev 6.4.2.1 (2016/02/05) HybsSystem.url2dir に引数追加。 344 * 345 * @param url ファイルの検索元となるディレクトリ 346 * @see org.opengion.hayabusa.common.SystemData#FILE_URL 347 */ 348 public void setFrom( final String url ) { 349 final String furl = nval( getRequestParameter( url ),null ); 350 from = HybsSystem.url2dir( from,furl,"." ); // 6.4.2.1 (2016/02/05) 351 } 352 353 /** 354 * 【TAG】検索した結果を表示する表示順をファイル属性名[null/NAME/LASTMODIFIED/FILE_LENGTH]で指定します(初期値:自然順序)。 355 * 356 * @og.tag 357 * ファイルをソートする順(Comparator)を指定します。ソートに指定できる 358 * ファイル属性名は、"NAME","LASTMODIFIED","FILE_LENGTH" の内のどれかひとつです。 359 * 何も指定しない場合は、Fileオブジェクトの自然順序でのソートになります。 360 * (※ 下位互換性のため、LENGTH も残しますが、廃止予定です。) 361 * 362 * @og.rev 3.5.6.2 (2004/07/05) 文字列の連結にStringBuilderを使用します。 363 * @og.rev 4.0.0.0 (2005/01/31) 新規ロジックで改定 364 * @og.rev 5.3.4.0 (2011/04/01) ORDER_BYリストの出力方法 見直し 365 * @og.rev 6.3.4.0 (2015/08/01) Arrays.toString から String.join に置き換え。 366 * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。 367 * 368 * @param ordr ソートキー [null/NAME/LASTMODIFIED/FILE_LENGTH] 369 */ 370 public void setOrderBy( final String ordr ) { 371 orderBy = nval( getRequestParameter( ordr ),orderBy ); 372 373 if( orderBy != null && ! check( orderBy, ORDER_BY_SET ) ) { 374 final String errMsg = "orderBy 属性に、下記の属性名以外の値が設定されました。" + CR 375 + "orderBy=[" + orderBy + "] " + CR 376 + "orderBy List=" + String.join( ", " , ORDER_BY_SET ) ; 377 throw new HybsSystemException( errMsg ); 378 } 379 } 380 381 /** 382 * 【TAG】表示順を逆転するかどうか[true/false]を指定します(初期値:false)。 383 * 384 * @og.tag 385 * orderBy 属性で指定した表示順を、逆順にするかどうかを指定できます。 386 * 初期値は、false (昇順) です。 387 * 388 * @param flag 表示順を逆転するかどうか [true:逆順/false:昇順] 389 */ 390 public void setDesc( final String flag ) { 391 desc = nval( getRequestParameter( flag ),desc ); 392 } 393 394 /** 395 * FileFilterオブジェクトをセットします。 396 * これは、BODY 部に登録した、FileWhereタグによって設定された 397 * ファイルフィルターです。 398 * 399 * @param filter オブジェクト 400 */ 401 protected void setFileFilter( final FileFilter filter ) { 402 this.filter = filter; 403 } 404 405 /** 406 * 名前順でのソート順を指定する Comparator の実体内部クラス 407 * 408 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private static final class に変更。 409 * 410 * @version 4.0 411 * @author Kazuhiko Hasegawa 412 * @since JDK5.0, 413 */ 414 private static final class NameComparator implements Comparator<File>,Serializable { 415 private static final long serialVersionUID = 400020050131L ; // 4.0.0.0 (2005/01/31) 416 417 private final boolean desc ; 418 419 /** 420 * 名前順での比較を行うオブジェクトを作成します。 421 * 422 * @param desc 表示順逆転 [true:昇順/false:降順] 423 */ 424 public NameComparator( final boolean desc ) { this.desc = desc; } 425 426 /** 427 * Comparator インターフェースの compare( File,File ) メソッド。 428 * 429 * @param o1 比較元1のファイルオブジェクト 430 * @param o2 比較元2のファイルオブジェクト 431 * @return 最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数 432 */ 433 public int compare( final File o1, final File o2 ) { 434 final File f1 = desc ? o2 : o1 ; 435 final File f2 = desc ? o1 : o2 ; 436 return f1.getName().compareTo( f2.getName() ) ; 437 } 438 } 439 440 /** 441 * 更新日順でのソート順を指定する Comparator の実体内部クラス 442 * 443 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private static final class に変更。 444 * 445 * @version 4.0 446 * @author Kazuhiko Hasegawa 447 * @since JDK5.0, 448 */ 449 private static final class ModifiedComparator implements Comparator<File>,Serializable { 450 private static final long serialVersionUID = 400020050131L ; // 4.0.0.0 (2005/01/31) 451 452 private final boolean desc ; 453 454 /** 455 * 更新日順での比較を行うオブジェクトを作成します。 456 * 457 * @param desc 表示順逆順 [true:昇順/false:降順] 458 */ 459 public ModifiedComparator( final boolean desc ) { this.desc = desc; } 460 461 /** 462 * Comparator インターフェースの compare( File,File ) メソッド。 463 * 464 * @param o1 比較元1のファイルオブジェクト 465 * @param o2 比較元2のファイルオブジェクト 466 * @return 最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数 467 */ 468 public int compare( final File o1, final File o2 ) { 469 final File f1 = desc ? o2 : o1 ; 470 final File f2 = desc ? o1 : o2 ; 471 return (int)( f1.lastModified() - f2.lastModified() ) ; 472 } 473 } 474 475 /** 476 * ファイルサイズ順でのソート順を指定する Comparator の実体内部クラス 477 * 478 * @og.rev 6.3.9.1 (2015/11/27) 修飾子を、なし → private static final class に変更。 479 * 480 * @version 4.0 481 * @author Kazuhiko Hasegawa 482 * @since JDK5.0, 483 */ 484 private static final class LengthComparator implements Comparator<File>,Serializable { 485 private static final long serialVersionUID = 400020050131L ; // 4.0.0.0 (2005/01/31) 486 487 private final boolean desc ; 488 489 /** 490 * ファイルサイズでの比較を行うオブジェクトを作成します。 491 * 492 * @param desc 表示順逆順 [true:昇順/false:降順] 493 */ 494 public LengthComparator( final boolean desc ) { this.desc = desc; } 495 496 /** 497 * Comparator インターフェースの compare( File,File ) メソッド。 498 * 499 * @param o1 比較元1のファイルオブジェクト 500 * @param o2 比較元2のファイルオブジェクト 501 * @return 最初の引数が 2 番目の引数より小さい場合は負の整数、両方が等しい場合は 0、最初の引数が 2 番目の引数より大きい場合は正の整数 502 */ 503 public int compare( final File o1, final File o2 ) { 504 final File f1 = desc ? o2 : o1 ; 505 final File f2 = desc ? o1 : o2 ; 506 return (int)( f1.length() - f2.length() ) ; 507 } 508 } 509 510 /** 511 * このオブジェクトの文字列表現を返します。 512 * 基本的にデバッグ目的に使用します。 513 * 514 * @return このクラスの文字列表現 515 * @og.rtnNotNull 516 */ 517 @Override 518 public String toString() { 519 return ToString.title( this.getClass().getName() ) 520 .println( "VERSION" ,VERSION ) 521 .println( "orderBy" ,orderBy ) 522 .println( "desc" ,desc ) 523 .println( "from" ,from ) 524 .println( "selValue" ,selValue ) 525 .println( "Other..." ,getAttributes().getAttribute() ) 526 .fixForm().toString() ; 527 } 528}