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         *
063         * @param  dir ディレクトリ
064         */
065        public void init( final String dir ) {
066                init( dir , null , null );
067        }
068
069        /**
070         * 読み取るディレクトリを指定して、ファイルマップを構築します。
071         *
072         * このディレクトリは、OSに対する物理アドレスになります。
073         *
074         * @og.rev 5.5.4.2 (2012/07/13) makeFileMap() を直接コンストラクターとして使用
075         * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
076         * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
077         * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
078         *
079         * @param  dir ディレクトリ
080         * @param  path ファイル名に付与するパス文字列
081         */
082        public void init( final String dir , final String path ) {
083                init( dir , path , null );
084        }
085
086        /**
087         * すでに読み取った Set オブジェクトを指定して、ファイルマップを構築します。
088         *
089         * これは、ServletContext を利用した、META-INF/resources からの読み取り対応になります。
090         * 一覧を取得するのは、ServletContext 関連の実装が必要になるため、fukurou では
091         * java の一般的なオブジェクトである Set を処理するだけとします。
092         *
093         * ファイル名は、dir を削除した残りで構築します。フォルダ階層を含みます。
094         * Mapのキーは、フォルダ階層を含まない、ファイル名のみとします。
095         * つまり、フォルダ階層を持ってリソースを用意しておいても、キーとしては、
096         * ファイル名のみを使用します。
097         *
098         * @og.rev 5.5.4.2 (2012/07/13) 新規作成
099         * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
100         * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
101         * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
102         *
103         * @param  dir ディレクトリ
104         * @param  resourcePaths リソースパス
105         */
106        public void init( final String dir , final Set<?> resourcePaths ) {
107                init( dir , null , resourcePaths );
108        }
109
110        /**
111         * すでに読み取った Set オブジェクトを指定して、ファイルマップを構築します。
112         *
113         * これは、ServletContext を利用した、META-INF/resources からの読み取り対応になります。
114         * 一覧を取得するのは、ServletContext 関連の実装が必要になるため、fukurou では
115         * java の一般的なオブジェクトである Set を処理するだけとします。
116         *
117         * ファイル名は、dir を削除した残りで構築します。フォルダ階層を含みます。
118         * Mapのキーは、フォルダ階層を含まない、ファイル名のみとします。
119         * つまり、フォルダ階層を持ってリソースを用意しておいても、キーとしては、
120         * ファイル名のみを使用します。
121         *
122         * @og.rev 5.5.4.2 (2012/07/13) 新規作成
123         * @og.rev 6.3.8.4 (2015/10/09) 別のコンストラクターを呼ぶようにします。
124         * @og.rev 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
125         * @og.rev 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。
126         * @og.rev 6.3.9.0 (2015/11/06) コンストラクターを止めて、初期化メソッドに変更する。
127         * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動するとともに、メソッド名を、ogThrowMsgPrint → ogThrowMsgPrint に変更。
128         * @og.rev 6.4.3.2 (2016/02/19) 指定のフォルダが存在しない場合、作成します。
129         *
130         * @param  dir ディレクトリ
131         * @param  path ファイル名に付与するパス文字列
132         * @param  resourcePaths リソースパス
133         */
134        public void init( final String dir , final String path , final Set<?> resourcePaths ) {
135                if( resourcePaths == null ) {
136                        final File directory = new File( dir );
137                        if( ! directory.exists() ) {
138                                final String errMsg = "指定のディレクトリは、存在しません。[" + directory + "]";
139                                // 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
140                                System.out.println( errMsg );
141                                // 6.4.3.2 (2016/02/19) 指定のフォルダが存在しない場合、作成します。
142                                if( directory.mkdirs() ) {
143                                        final String errMsg2 = "指定のディレクトリを自動作成しました。[" + directory + "]";
144                                        System.out.println( errMsg2 );
145                                }
146                                else {
147                                        final String errMsg3 = "指定のディレクトリの自動作成に失敗しました。[" + directory + "]";
148                                        System.err.println( ThrowUtil.ogThrowMsg( errMsg3 ) );
149                                }
150                                return ;
151                        }
152
153                        if( ! directory.isDirectory() ) {
154                                final String errMsg = "指定のキーは、ディレクトリではありません。[" + directory + "]";
155                                // 6.3.8.5 (2015/10/16) コンストラクターで、Exception を throw しないようにします。
156                                System.err.println( ThrowUtil.ogThrowMsg( errMsg ) );
157                                return ;
158                        }
159                        // 6.3.8.4 (2015/10/09) ファイルのみ取り込む
160                        // 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。
161                        final File[] files = directory.listFiles();
162                        if( files != null ) {
163                                for( final File file : files ) {
164                                        if( file != null && file.isFile() ) {
165                                                dataSet( file.getName() , path );
166                                        }
167                                }
168                        }
169                }
170                else {
171                        final int len = dir.length() ;
172
173                        for( final Object rpath : resourcePaths ) {
174                                final String fname = String.valueOf( rpath ).substring( len );  // ファイル名
175                                dataSet( fname , path );
176                        }
177                }
178        }
179
180        /**
181         * ファイルマップを構築する内部処理。
182         *
183         * これは、ServletContext を利用した、META-INF/resources からの読み取り対応と、
184         * 通常のフォルダスキャンの読み取りの共通処理をまとめた目疎度です。
185         *
186         * @og.rev 6.3.8.4 (2015/10/09) 新規作成
187         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
188         *
189         * @param  fname ファイル名
190         * @param  path  ファイル名に付与するパス文字列
191         */
192        private void dataSet( final String fname , final String path ) {
193                final String upkey = fname.toUpperCase( Locale.JAPAN ) ;
194                String tmpName = fname;
195
196                // path が、nullやゼロ文字列以外の場合は、最後の文字を判定して、ファイル名に連結します。
197                if( path != null && !path.isEmpty() ) {
198                        final char ch = path.charAt( path.length()-1 ) ;
199                        if( ch == '/' || ch == '\\' ) {
200                                tmpName = path + fname;
201                        }
202                        else {
203                                tmpName = path + '/' + fname;
204                        }
205                }
206
207                // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
208                final int idx = upkey.lastIndexOf( '.' );
209                if( idx >= 0 ) {
210                        fMap.put( upkey.substring( 0,idx ), tmpName );
211                }
212                else {
213                        fMap.put( upkey, tmpName );
214                }
215        }
216
217        /**
218         * 指定のキーのファイルが存在しているかどうかを返します。
219         * 存在している場合は、true , 存在していない場合は、false になります。
220         *
221         * @og.rev 6.3.8.5 (2015/10/16) Exception を throw しないようにします。
222         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
223         *
224         * @param   key 指定のキー
225         *
226         * @return      存在しているかどうか(true:存在する/false:存在しない)
227         */
228        public boolean exists( final String key ) {
229                // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
230                return key != null && fMap.containsKey( key.toUpperCase( Locale.JAPAN ) );
231        }
232
233        /**
234         * キーに対応したファイル名を返します。
235         * 指定のキーに対するファイル名が存在しない場合は、null を返します。
236         *
237         * 引数を可変長引数にして、前から順番にMapを調べます。最初に、null でない
238         * 値を返します。最後まで一致しなければ、null を返します。
239         * 引数のキーが、nullや、ゼロ配列の場合も、nullになります。
240         *
241         * @og.rev 6.3.8.4 (2015/10/09) FileMap のコンストラクタ変更に伴う対応。
242         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
243         *
244         * @param   keys 指定のキー配列(可変長引数)
245         *
246         * @return      ファイル名(ディレクトリパスは含まず)、存在しない場合は、null
247         */
248        public String getFilename( final String... keys ) {
249                if( keys != null ) {
250                        for( final String key : keys ) {
251                                // 6.3.8.4 (2015/10/09) 最初に見つけた値を返す。
252                                if( key != null ) {
253                                        // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
254                                        final String rtn = fMap.get( key.toUpperCase( Locale.JAPAN ) );
255                                        if( rtn != null ) { return rtn; }
256                                }
257                        }
258                }
259                return null;
260        }
261
262        /**
263         * 初期化が完了しているかどうかを、返します。
264         * 完了している場合は、true を返します。未完了、または、clear() 実行後は、falseです。
265         *
266         * インスタンスは、init処理が完了するまでは、false が返る為、簡易的な同期処理は
267         * 行われています。
268         * (内部のMapへの書き込みは、init処理でのみ行われます。)
269         *
270         * @og.rev 6.3.9.0 (2015/11/06) 新規作成。
271         * @og.rev 6.4.3.2 (2016/02/19) initFlagを廃止し、直接 Mapが空かどうかで判定します。。
272         *
273         * @return      初期化が完了していればtrue
274         */
275        public boolean isInit() { return !fMap.isEmpty(); }
276
277        /**
278         * 初期化(クリア)します。
279         *
280         * @og.rev 6.3.9.0 (2015/11/06) 新規作成。
281         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
282         */
283        @Override
284        public void clear() {
285                // 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。
286                fMap.clear();
287        }
288}