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