    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.fukurou.util;
    017
    018import java.io.File;
    019import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
    020import java.util.concurrent.ConcurrentHashMap;                          // 6.4.3.1 (2016/02/12) refactoring
    021import java.util.Locale ;
    022import java.util.Set;
    023
    024import org.opengion.fukurou.system.ThrowUtil;                           // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
    025
    026/**
    027 * FileMap は、ファイルを読み取って、キー情報から、ファイルへのリンクを作成するための
    028 * 情報を返します。
    029 * ファイルそのものは、指定のディレクトリをすべて読み取り、拡張子以外の部分を、キーとして
    030 * 登録します。(キーは大文字に統一されます。)
    031 * 実際のファイルの拡張子は、リンク作成時の処理で付与されます。
    032 * 例えば、HELPファイルを、XXXX.html や、XXXX.htm 、XXXX.pdf など、色々な形態で作成した
    033 * 場合でも、キーとしては、XXXX で存在チェックをかけることができるようになります。
    034 *
    035 * ファイルは、一旦すべて読み取ってメモリ上で管理されます。
    036 * ディレクトリの再読取が必要な場合は、オブジェクトを再作成する必要があります。
    037 *
    038 * @version  4.0
    039 * @author   Kazuhiko Hasegawa
    040 * @since    JDK5.0,
    041 */
    042public final class FileMap implements Cleanable {
    043        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
    044        private final ConcurrentMap<String,String> fMap = new ConcurrentHashMap<>();                            // 6.4.3.1 (2016/02/12) 変数名も変えておきます。
    045
    046        /**
    047         * デフォルトコンストラクター
    048         *
    049         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    050         */
    051        public FileMap() { super(); }           // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    052
    053//      /**
    054//       * 読み取るディレクトリを指定して、ファイルマップを構築します。
    055//       *
    056//       * このディレクトリは、OSに対する物理アドレスになります。
    057//       *
    058//       * @og.rev 5.5.4.2 (2012/07/13) makeFileMap() を直接コンストラクターとして使用
    059//       * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
    060//       * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
    061//       * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
    062//       * @og.rev 7.3.2.0 (2021/03/19) 廃止
    063//       *
    064//       * @param  dir ディレクトリ
    065//       */
    066//      public void init( final String dir ) {
    067//              init( dir , null , null );
    068//      }
    069
    070        /**
    071         * 読み取るディレクトリを指定して、ファイルマップを構築します。
    072         *
    073         * このディレクトリは、OSに対する物理アドレスになります。
    074         *
    075         * @og.rev 5.5.4.2 (2012/07/13) makeFileMap() を直接コンストラクターとして使用
    076         * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
    077         * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
    078         * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
    079         *
    080         * @param  dir ディレクトリ
    081         * @param  path ファイル名に付与するパス文字列
    082         */
    083        public void init( final String dir , final String path ) {
    084                init( dir , path , null );
    085        }
    086
    087//      /**
    088//       * すでに読み取った Set オブジェクトを指定して、ファイルマップを構築します。
    089//       *
    090//       * これは、ServletContext を利用した、META-INF/resources からの読み取り対応になります。
    091//       * 一覧を取得するのは、ServletContext 関連の実装が必要になるため、fukurou では
    092//       * java の一般的なオブジェクトである Set を処理するだけとします。
    093//       *
    094//       * ファイル名は、dir を削除した残りで構築します。フォルダ階層を含みます。
    095//       * Mapのキーは、フォルダ階層を含まない、ファイル名のみとします。
    096//       * つまり、フォルダ階層を持ってリソースを用意しておいても、キーとしては、
    097//       * ファイル名のみを使用します。
    098//       *
    099//       * @og.rev 5.5.4.2 (2012/07/13) 新規作成
    100//       * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
    101//       * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
    102//       * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
    103//       * @og.rev 7.3.2.0 (2021/03/19) 廃止
    104//       *
    105//       * @param  dir ディレクトリ
    106//       * @param  resourcePaths リソースパス
    107//       */
    108//      public void init( final String dir , final Set<?> resourcePaths ) {
    109//              init( dir , null , resourcePaths );
    110//      }
    111
    112        /**
    113         * すでに読み取った Set オブジェクトを指定して、ファイルマップを構築します。
    114         *
    115         * これは、ServletContext を利用した、META-INF/resources からの読み取り対応になります。
    116         * 一覧を取得するのは、ServletContext 関連の実装が必要になるため、fukurou では
    117         * java の一般的なオブジェクトである Set を処理するだけとします。
    118         *
    119         * ファイル名は、dir を削除した残りで構築します。フォルダ階層を含みます。
    120         * Mapのキーは、フォルダ階層を含まない、ファイル名のみとします。
    121         * つまり、フォルダ階層を持ってリソースを用意しておいても、キーとしては、
    122         * ファイル名のみを使用します。
    123         *
    124         * @og.rev 5.5.4.2 (2012/07/13) 新規作成
    125         * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
    126         * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
    127         * @og.rev 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。
    128         * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
    129         * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動するとともに、メソッド名を、ogThrowMsgPrint → ogThrowMsgPrint に変更。
    130         * @og.rev 6.4.3.2 (2016/02/19) 指定のフォルダが存在しない場合、作成します。
    131         * @og.rev 7.3.2.0 (2021/03/19) dir と path の nullﾁｪｯｸ(エラーでも止めない)
    132         *
    133         * @param  dir ディレクトリ
    134         * @param  path ファイル名に付与するパス文字列
    135         * @param  resourcePaths リソースパス
    136         */
    137//      public void init( final String dir , final String path , final Set<?> resourcePaths ) {
    138        public void init( final String dir , final String path , final Set<String> resourcePaths ) {
    139                // 7.3.2.0 (2021/03/19) dir と path の nullﾁｪｯｸ(エラーでも止めない)
    140                if( dir == null || path == null ) {
    141                        final String errMsg1 = "指定のディレクトリかパスが、nullです。dir=[" + dir + "] , path=[" + path + "]" ;
    142                        System.err.println( ThrowUtil.ogThrowMsg( errMsg1 ) );
    143                }
    144
    145                if( resourcePaths == null ) {
    146                        final File directory = new File( dir );
    147                        if( ! directory.exists() ) {
    148                                final String errMsg = "指定のディレクトリは、存在しません。dir=[" + directory + "] , path=[" + path + "]" ;
    149                                // 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
    150//                              System.out.println( errMsg );
    151                                System.err.println( ThrowUtil.ogThrowMsg( errMsg ) );           // 7.3.2.0 (2021/03/19) ﾌｫﾙﾀﾞなしは、積極的なエラーにする。
    152        //                      // 7.3.2.0 (2021/03/19) ﾌｫﾙﾀﾞを作成する必要はない。あくまでｲﾒｰｼﾞを用意しておいて利用するだけでよい。
    153        //                      // 6.4.3.2 (2016/02/19) 指定のフォルダが存在しない場合、作成します。
    154        //                      if( directory.mkdirs() ) {
    155        //                              final String errMsg2 = "指定のディレクトリを自動作成しました。[" + directory + "]";
    156        //                              System.out.println( errMsg2 );
    157        //                      }
    158        //                      else {
    159        //                              final String errMsg3 = "指定のディレクトリの自動作成に失敗しました。[" + directory + "]";
    160        //                              System.err.println( ThrowUtil.ogThrowMsg( errMsg3 ) );
    161        //                      }
    162                                return ;
    163                        }
    164
    165                        if( ! directory.isDirectory() ) {
    166                                final String errMsg = "指定のキーは、ディレクトリではありません。[" + directory + "]";
    167                                // 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
    168                                System.err.println( ThrowUtil.ogThrowMsg( errMsg ) );
    169                                return ;
    170                        }
    171                        // 6.3.8.4 (2015/10/09) ﾌｧｲﾙのみ取り込む
    172                        // 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。
    173                        final File[] files = directory.listFiles();
    174                        if( files != null ) {
    175                                for( final File file : files ) {
    176                                        if( file != null && file.isFile() ) {
    177                                                dataSet( file.getName() , path );
    178                                        }
    179                                }
    180                        }
    181                }
    182                else {
    183                        final int len = dir.length() ;
    184//                      for( final Object rpath : resourcePaths ) {
    185//                              final String fname = String.valueOf( rpath ).substring( len );  // ファイル名
    186//                              dataSet( fname , path );
    187//                      }
    188                        for( final String rpath : resourcePaths ) {                     // 7.3.2.0 (2021/03/19)
    189                                final String fname = rpath.substring( len );    // ファイル名
    190                                dataSet( fname , path );
    191                        }
    192                }
    193        }
    194
    195        /**
    196         * ファイルマップを構築する内部処理。
    197         *
    198         * これは、ServletContext を利用した、META-INF/resources からの読み取り対応と、
    199         * 通常のフォルダスキャンの読み取りの共通処理をまとめた目疎度です。
    200         *
    201         * @og.rev 6.3.8.4 (2015/10/09) 新規作成
    202         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
    203         *
    204         * @param  fname ファイル名
    205         * @param  path  ファイル名に付与するパス文字列
    206         */
    207        private void dataSet( final String fname , final String path ) {
    208                final String upkey = fname.toUpperCase( Locale.JAPAN ) ;
    209                String tmpName = fname;
    210
    211                // path が、nullやゼロ文字列以外の場合は、最後の文字を判定して、ファイル名に連結します。
    212                if( path != null && !path.isEmpty() ) {
    213                        final char ch = path.charAt( path.length()-1 ) ;
    214                        if( ch == '/' || ch == '\\' ) {
    215                                tmpName = path + fname;
    216                        }
    217                        else {
    218                                tmpName = path + '/' + fname;
    219                        }
    220                }
    221
    222                // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
    223                final int idx = upkey.lastIndexOf( '.' );
    224                if( idx >= 0 ) {
    225                        fMap.put( upkey.substring( 0,idx ), tmpName );
    226                }
    227                else {
    228                        fMap.put( upkey, tmpName );
    229                }
    230        }
    231
    232        /**
    233         * 指定のキーのファイルが存在しているかどうかを返します。
    234         * 存在している場合は、true , 存在していない場合は、false になります。
    235         *
    236         * @og.rev 6.3.8.5 (2015/10/16) Exception を throw しないようにします。
    237         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
    238         *
    239         * @param   key 指定のキー
    240         *
    241         * @return      存在しているかどうか(true:存在する/false:存在しない)
    242         */
    243        public boolean exists( final String key ) {
    244                // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
    245                return key != null && fMap.containsKey( key.toUpperCase( Locale.JAPAN ) );
    246        }
    247
    248        /**
    249         * キーに対応したファイル名を返します。
    250         * 指定のキーに対するファイル名が存在しない場合は、null を返します。
    251         *
    252         * 引数を可変長引数にして、前から順番にMapを調べます。最初に、null でない
    253         * 値を返します。最後まで一致しなければ、null を返します。
    254         * 引数のキーが、nullや、ゼロ配列の場合も、nullになります。
    255         *
    256         * @og.rev 6.3.8.4 (2015/10/09) FileMap のコンストラクタ変更に伴う対応。
    257         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
    258         *
    259         * @param   keys 指定のキー配列(可変長引数)
    260         *
    261         * @return      ファイル名(ディレクトリパスは含まず)、存在しない場合は、null
    262         */
    263        public String getFilename( final String... keys ) {
    264                if( keys != null ) {
    265                        for( final String key : keys ) {
    266                                // 6.3.8.4 (2015/10/09) 最初に見つけた値を返す。
    267                                if( key != null ) {
    268                                        // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
    269                                        final String rtn = fMap.get( key.toUpperCase( Locale.JAPAN ) );
    270                                        if( rtn != null ) { return rtn; }
    271                                }
    272                        }
    273                }
    274                return null;
    275        }
    276
    277        /**
    278         * 初期化が完了しているかどうかを、返します。
    279         * 完了している場合は、true を返します。未完了、または、clear() 実行後は、falseです。
    280         *
    281         * インスタンスは、init処理が完了するまでは、false が返る為、簡易的な同期処理は
    282         * 行われています。
    283         * (内部のMapへの書き込みは、init処理でのみ行われます。)
    284         *
    285         * @og.rev 6.3.9.0 (2015/11/06) 新規作成。
    286         * @og.rev 6.4.3.2 (2016/02/19) initFlagを廃止し、直接 Mapが空かどうかで判定します。
    287         *
    288         * @return      初期化が完了していればtrue
    289         */
    290        public boolean isInit() { return !fMap.isEmpty(); }
    291
    292        /**
    293         * 初期化(クリア)します。
    294         *
    295         * @og.rev 6.3.9.0 (2015/11/06) 新規作成。
    296         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
    297         */
    298        @Override
    299        public void clear() {
    300                // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
    301                fMap.clear();
    302        }
    303}