    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 * ●形式：&lt;og:fileOption from="…" value="[…]" ･･･ &gt;･･･&lt;/og:fileOption&gt;
    043 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    044 *
    045 * ●Tag定義：
    046 *   &lt;og:fileOption
    047 *       from               【TAG】ファイルの検索元となるディレクトリを指定します (初期値:FILE_URL[=filetemp/])
    048 *       value              【TAG】Optionの初期値で選ばれる値を指定します
    049 *       useDir             【TAG】optionリストの作成を、ディレクトリの値で行います。
    050 *       groupDir           【TAG】optgroupを、ディレクトリの値で作成します(１レベルのみ)。
    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 *   &gt;   ... Body ...
    060 *   &lt;/og:fileOption&gt;
    061 *
    062 * ●使用例
    063 *      ・&lt;og:fileOption val1="ABCD" val2="{&#064;value}" &gt;
    064 *            &lt;og:fileWhere startsWith="ABCD" ･･･ /&gt;
    065 *        &lt;/og:fileOption&gt;
    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:１層]
    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を、ディレクトリの値で作成します(１レベルのみ)(初期値: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 比較元１のファイルオブジェクト
    400                 * @param o2 比較元２のファイルオブジェクト
    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 比較元１のファイルオブジェクト
    435                 * @param o2 比較元２のファイルオブジェクト
    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 比較元１のファイルオブジェクト
    470                 * @param o2 比較元２のファイルオブジェクト
    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}