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