    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.StringUtil;
    023
    024import static org.opengion.fukurou.util.StringUtil.nval ;
    025
    026import java.io.File;
    027import java.io.FileFilter;
    028import java.io.Serializable;
    029import java.io.ObjectOutputStream;
    030import java.io.ObjectInputStream;
    031import java.io.IOException;
    032import java.util.Arrays;
    033import java.util.Comparator;
    034
    035/**
    036 * ファイルのプルダウンリストの作成するタグです。
    037 *
    038 * SelectタグのBODY部に指定します。
    039 * 並び替えについては、このタグで指定しますが、ファイルの選別は、
    040 * BODY 部に記述する fileWhere タグで指定します。
    041 *
    042 * @og.formSample
    043 * ●形式：&lt;og:fileOption from="…" value="[…]" ･･･ &gt;･･･&lt;/og:fileOption&gt;
    044 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
    045 *
    046 * ●Tag定義：
    047 *   &lt;og:fileOption
    048 *       from               【TAG】ファイルの検索元となるディレクトリを指定します (初期値:FILE_URL[=filetemp/])
    049 *       value              【TAG】Optionの初期値で選ばれる値を指定します
    050 *       orderBy            【TAG】検索した結果を表示する表示順をファイル属性名で指定します(初期値:自然順序)
    051 *       desc               【TAG】表示順を逆転するかどうか[true/false]を指定します(初期値:false)
    052 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    053 *   &gt;   ... Body ...
    054 *   &lt;/og:fileOption&gt;
    055 *
    056 * ●使用例
    057 *      ・&lt;og:fileOption val1="ABCD" val2="{&#064;value}" &gt;
    058 *            &lt;og:fileWhere startsWith="ABCD" ･･･ /&gt;
    059 *        &lt;/og:fileOption&gt;
    060 *
    061 * @og.rev 2.1.1.0 (2002/11/11) 新規作成
    062 * @og.rev 4.0.0.0 (2005/01/31) 内部ロジック改定
    063 * @og.group その他入力
    064 *
    065 * @version  4.0
    066 * @author   Kazuhiko Hasegawa
    067 * @since    JDK5.0,
    068 */
    069public class FileOptionTag extends CommonTagSupport {
    070        //* このプログラムのVERSION文字列を設定します。   {@value} */
    071        private static final String VERSION = "5.3.4.0 (2011/04/01)" ;
    072
    073        private static final long serialVersionUID = 534020110401L ;
    074
    075        private String          orderBy         = null;         // ｿｰﾄ項目
    076        private boolean         desc            = false;        // 降順ﾌﾗｸﾞ
    077        private String      from                = HybsSystem.sys( "FILE_URL" ); // 検索起点ﾌｧｲﾙ
    078        private String          selValue        = null;         // 選択済み初期値にする場合
    079        private transient FileFilter    filter          = null;                                                 // FileWhere で指定したフィルター
    080
    081        private static final String[] ORDER_BY = new String[] {
    082                                                                        "NAME","LASTMODIFIED","FILE_LENGTH","LENGTH" }; // 5.3.4.0 (2011/04/01) FILE_LENGTH 追加
    083
    084        /**
    085         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
    086         *
    087         * @return      後続処理の指示( EVAL_BODY_BUFFERED )
    088         */
    089        @Override
    090        public int doStartTag() {
    091                return EVAL_BODY_BUFFERED ;     // Body を評価する。( extends BodyTagSupport 時)
    092        }
    093
    094        /**
    095         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
    096         *
    097         * @return      後続処理の指示(SKIP_BODY)
    098         */
    099        @Override
    100        public int doAfterBody() {
    101                return SKIP_BODY ;
    102        }
    103
    104        /**
    105         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    106         *
    107         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
    108         *
    109         * @return      後続処理の指示
    110         */
    111        @Override
    112        public int doEndTag() {
    113                debugPrint();           // 4.0.0 (2005/02/28)
    114                OptionAncestorIF select = (OptionAncestorIF)findAncestorWithClass( this, OptionAncestorIF.class );
    115                if( select == null ) {
    116                        String errMsg = "<b>" + getTagName() + "タグは、SelectTag または、DatalistTag のBODY に記述する必要があります。</b>";
    117                        throw new HybsSystemException( errMsg );
    118                }
    119                Comparator<File> comp = makeComparator( orderBy,desc );
    120                makeLabel( select,comp );
    121
    122                return EVAL_PAGE ;
    123        }
    124
    125        /**
    126         * タグリブオブジェクトをリリースします。
    127         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    128         *
    129         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
    130         *
    131         */
    132        @Override
    133        protected void release2() {
    134                super.release2();
    135                orderBy         = null;         // ｿｰﾄ項目
    136                desc            = false;        // 降順ﾌﾗｸﾞ
    137                from            = HybsSystem.sys( "FILE_URL" );
    138                filter          = null;
    139                selValue        = null;
    140        }
    141
    142        /**
    143         * オプションを作成します。
    144         *
    145         * ファイル名を "value" に、
    146         * BODY属性 に登録するOptionを作成します。
    147         *
    148         * @og.rev 5.3.4.0 (2011/04/01) FILE_LENGTH 追加
    149         *
    150         * @param       orderBy ソートする属性[NAME/LASTMODIFIED/FILE_LENGTH/LENGTH]
    151         * @param       desc    並び順[true:昇順/false:降順]
    152         *
    153         * @return      ファイル比較用のComparatorオブジェクト
    154         */
    155        private Comparator<File> makeComparator( final String orderBy,final boolean desc ) {
    156                if( orderBy == null ) { return null; }
    157
    158                Comparator<File> comp = null ;
    159
    160                if( "NAME".equalsIgnoreCase( orderBy ) ) {
    161                        comp = new NameComparator( desc );
    162                }
    163                else if( "LASTMODIFIED".equalsIgnoreCase( orderBy ) ) {
    164                        comp = new ModifiedComparator( desc );
    165                }
    166                // "LENGTH" を残すのは、互換性のため
    167                else if( "FILE_LENGTH".equalsIgnoreCase( orderBy ) || "LENGTH".equalsIgnoreCase( orderBy ) ) {
    168                        comp = new LengthComparator( desc );
    169                }
    170
    171                return comp ;
    172        }
    173
    174        /**
    175         * オプションを作成します。
    176         *
    177         * ﾌｧｲﾙ名を "value" に、
    178         * BODY属性 に登録するOptionを作成します。
    179         *
    180         * @og.rev 3.8.0.9 (2005/10/17) 複数選択可能時に全選択を設定する。
    181         *
    182         * @param       select  SelectTagオブジェクト
    183         * @param       comp    並び順を指定するためのComparatorオブジェクト
    184         */
    185        private void makeLabel( final OptionAncestorIF select,final Comparator<File> comp ) {
    186                File path = new File( from );
    187
    188                File[] list = path.listFiles( filter );
    189
    190                boolean multipleAll = select.isMultipleAll();           // 3.8.0.9 (2005/10/17)
    191                if( list != null )  {
    192                        Arrays.sort( list, comp );
    193                        for( int i = 0; i < list.length; i++ ) {
    194                                if( list[i].isDirectory() ) { continue; }       // ディレクトリは除外
    195                                Attributes attri = new Attributes();
    196                                String value = list[i].getName();
    197                                attri.set( "value", value );
    198                                if( ( selValue != null && selValue.equalsIgnoreCase( value ) ) || multipleAll ) {
    199                                        attri.set( "selected", "selected" );
    200                                }
    201                                attri.set( "body", value );
    202                                select.addOption( XHTMLTag.option( attri ) );
    203                        }
    204                }
    205        }
    206
    207        /**
    208         * 【TAG】Optionの初期値で選ばれる値を指定します。
    209         *
    210         * @og.tag
    211         * キーになるのは、ファイル属性の NAME です。(ディレクトリなしのファイル名)
    212         * ここで value属性に指定した場合、このファイル名と(大文字小文字を無視して)
    213         * 一致する場合に、プルダウンの初期値に表示されます。(selected 属性が設定される。)
    214         *
    215         * @param   val  初期値で選ばれる値
    216         */
    217        public void setValue( final String val ) {
    218                selValue = nval( getRequestParameter( val ),selValue );
    219        }
    220
    221        /**
    222         * 【TAG】ファイルの検索元となるディレクトリを指定します
    223         *              (初期値:FILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。
    224         *
    225         * @og.tag ファイルの検索元となるディレクトリを指定します。
    226         * (初期値:システム定数のFILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。
    227         *
    228         * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。
    229         *
    230         * @param       url ファイルの検索元となるディレクトリ
    231         * @see         org.opengion.hayabusa.common.SystemData#FILE_URL
    232         */
    233        public void setFrom( final String url ) {
    234                String furl = nval( getRequestParameter( url ),null );
    235                if( furl != null ) {
    236                        char ch = furl.charAt( furl.length()-1 );
    237                        if( ch != '/' && ch != '\\' ) { furl = furl + "/"; }
    238                }
    239                furl = StringUtil.urlAppend( from,furl );
    240                furl = StringUtil.urlAppend( furl,"." );
    241                from = HybsSystem.url2dir( furl );
    242        }
    243
    244        /**
    245         * 【TAG】検索した結果を表示する表示順をファイル属性名で指定します(初期値:自然順序)。
    246         *
    247         * @og.tag
    248         * ファイルをソートする順(Comparator)を指定します。ソートに指定できる
    249         * ファイル属性名は、"NAME","LASTMODIFIED","FILE_LENGTH" の内のどれかひとつです。
    250         * 何も指定しない場合は、Fileオブジェクトの自然順序でのソートになります。
    251         * (※ 下位互換性のため、LENGTH も残しますが、廃止予定です。)
    252         *
    253         * @og.rev 3.5.6.2 (2004/07/05) 文字列の連結にStringBuilderを使用します。
    254         * @og.rev 4.0.0.0 (2005/01/31) 新規ロジックで改定
    255         * @og.rev 5.3.4.0 (2011/04/01) ORDER_BYリストの出力方法 見直し
    256         *
    257         * @param       ordr  ソートキー("NAME","LASTMODIFIED","FILE_LENGTH")
    258         */
    259        public void setOrderBy( final String ordr ) {
    260                orderBy = nval( getRequestParameter( ordr ),orderBy );
    261
    262                if( orderBy != null && ! check( orderBy, ORDER_BY ) ) {
    263                        StringBuilder errMsg = new StringBuilder();
    264                        errMsg.append( "orderBy 属性に、下記の属性名以外の値が設定されました。" );
    265                        errMsg.append( HybsSystem.CR );
    266                        errMsg.append( " orderBy=[" ).append( orderBy ).append( "]" );
    267                        errMsg.append( HybsSystem.CR );
    268                        errMsg.append( " orderBy List=[" );
    269                        errMsg.append( StringUtil.array2csv( ORDER_BY ) );
    270                        errMsg.append( "]" );
    271                        throw new HybsSystemException( errMsg.toString() );
    272                }
    273        }
    274
    275        /**
    276         * 【TAG】表示順を逆転するかどうか[true/false]を指定します(初期値:false)。
    277         *
    278         * @og.tag
    279         * orderBy 属性で指定した表示順を、逆順にするかどうかを指定できます。
    280         * 初期値は、false (昇順) です。
    281         *
    282         * @param       flag 表示順を逆転するかどうか [true:逆順/false:昇順]
    283         */
    284        public void setDesc( final String flag ) {
    285                desc = nval( getRequestParameter( flag ),desc );
    286        }
    287
    288        /**
    289         * FileFilterオブジェクトをセットします。
    290         * これは、BODY 部に登録した、FileWhereタグによって設定された
    291         * ファイルフィルターです。
    292         *
    293         * @param       filter  オブジェクト
    294         */
    295        protected void setFileFilter( final FileFilter filter ) {
    296                this.filter = filter;
    297        }
    298
    299        /**
    300         * 名前順でのソート順を指定する Comparator の実体内部クラス
    301         *
    302         * @og.group その他入力
    303         *
    304         * @version  4.0
    305         * @author   Kazuhiko Hasegawa
    306         * @since    JDK5.0,
    307         */
    308        static class NameComparator implements Comparator<File>,Serializable {
    309                private static final long serialVersionUID = 400020050131L ;    // 4.0.0.0 (2005/01/31)
    310
    311                private final boolean desc ;
    312
    313                /**
    314                 * 名前順での比較を行うオブジェクトを作成します。
    315                 *
    316                 * @param desc [true:昇順/false:降順]
    317                 */
    318                public NameComparator( final boolean desc ) { this.desc = desc; }
    319
    320                /**
    321                 * Comparator インターフェースの compare( File,File ) メソッド
    322                 *
    323                 * @param o1 File 比較元１のファイルオブジェクト
    324                 * @param o2 File 比較元２のファイルオブジェクト
    325                 */
    326                public int compare( final File o1, final File o2 ) {
    327                        File f1 = desc ? o2 : o1 ;
    328                        File f2 = desc ? o1 : o2 ;
    329                        return f1.getName().compareTo( f2.getName() ) ;
    330                }
    331        }
    332
    333        /**
    334         * 更新日順でのソート順を指定する Comparator の実体内部クラス
    335         *
    336         * @og.group その他入力
    337         *
    338         * @version  4.0
    339         * @author   Kazuhiko Hasegawa
    340         * @since    JDK5.0,
    341         */
    342        static class ModifiedComparator implements Comparator<File>,Serializable {
    343                private static final long serialVersionUID = 400020050131L ;    // 4.0.0.0 (2005/01/31)
    344
    345                private final boolean desc ;
    346
    347                /**
    348                 * 更新日順での比較を行うオブジェクトを作成します。
    349                 *
    350                 * @param desc [true:昇順/false:降順]
    351                 */
    352                public ModifiedComparator( final boolean desc ) { this.desc = desc; }
    353
    354                /**
    355                 * Comparator インターフェースの compare( File,File ) メソッド
    356                 *
    357                 * @param o1 File 比較元１のファイルオブジェクト
    358                 * @param o2 File 比較元２のファイルオブジェクト
    359                 */
    360                public int compare( final File o1, final File o2 ) {
    361                        File f1 = desc ? o2 : o1 ;
    362                        File f2 = desc ? o1 : o2 ;
    363                        return (int)( f1.lastModified() - f2.lastModified() ) ;
    364                }
    365        }
    366
    367        /**
    368         * ファイルサイズ順でのソート順を指定する Comparator の実体内部クラス
    369         *
    370         * @og.group その他入力
    371         *
    372         * @version  4.0
    373         * @author   Kazuhiko Hasegawa
    374         * @since    JDK5.0,
    375         */
    376        static class LengthComparator implements Comparator<File>,Serializable {
    377                private static final long serialVersionUID = 400020050131L ;    // 4.0.0.0 (2005/01/31)
    378
    379                private final boolean desc ;
    380
    381                /**
    382                 * ファイルサイズでの比較を行うオブジェクトを作成します。
    383                 *
    384                 * @param desc [true:昇順/false:降順]
    385                 */
    386                public LengthComparator( final boolean desc ) { this.desc = desc; }
    387
    388                /**
    389                 * Comparator インターフェースの compare( File,File ) メソッド
    390                 *
    391                 * @param o1 File 比較元１のファイルオブジェクト
    392                 * @param o2 File 比較元２のファイルオブジェクト
    393                 */
    394                public int compare( final File o1, final File o2 ) {
    395                        File f1 = desc ? o2 : o1 ;
    396                        File f2 = desc ? o1 : o2 ;
    397                        return (int)( f1.length() - f2.length() ) ;
    398                }
    399        }
    400
    401        /**
    402         * シリアライズ用のカスタムシリアライズ書き込みメソッド
    403         *
    404         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
    405         * @serialData 一部のオブジェクトは、シリアライズされません。
    406         *
    407         * @param       strm    ObjectOutputStreamオブジェクト
    408         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
    409         */
    410        private void writeObject( final ObjectOutputStream strm ) throws IOException {
    411                strm.defaultWriteObject();
    412        }
    413
    414        /**
    415         * シリアライズ用のカスタムシリアライズ読み込みメソッド
    416         *
    417         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
    418         *
    419         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
    420         * @serialData 一部のオブジェクトは、シリアライズされません。
    421         *
    422         * @param       strm    ObjectInputStreamオブジェクト
    423         * @see #release2()
    424         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
    425         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
    426         */
    427        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
    428                strm.defaultReadObject();
    429        }
    430
    431        /**
    432         * このオブジェクトの文字列表現を返します。
    433         * 基本的にデバッグ目的に使用します。
    434         *
    435         * @return このクラスの文字列表現
    436         */
    437        @Override
    438        public String toString() {
    439                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
    440                                .println( "VERSION"             ,VERSION        )
    441                                .println( "orderBy"             ,orderBy        )
    442                                .println( "desc"                ,desc           )
    443                                .println( "from"                ,from           )
    444                                .println( "selValue"    ,selValue       )
    445                                .println( "Other..."    ,getAttributes().getAttribute() )
    446                                .fixForm().toString() ;
    447        }
    448}