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                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 比較元1のファイルオブジェクト
324                 * @param o2 File 比較元2のファイルオブジェクト
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 比較元1のファイルオブジェクト
358                 * @param o2 File 比較元2のファイルオブジェクト
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 比較元1のファイルオブジェクト
392                 * @param o2 File 比較元2のファイルオブジェクト
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}