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