OSDN Git Service

mangled名をutf-8をベースにするように変更。旧mangledは置換する。
authorseraphy <seraphy@users.osdn.me>
Tue, 5 Feb 2019 18:24:54 +0000 (03:24 +0900)
committerseraphy <seraphy@users.osdn.me>
Wed, 6 Feb 2019 23:29:10 +0000 (08:29 +0900)
src/main/java/charactermanaj/model/io/PartsImageDirectoryWatchAgentThread.java
src/main/java/charactermanaj/util/FileUserData.java
src/main/java/charactermanaj/util/UserDataFactory.java

index 9c450bb..2103e83 100644 (file)
@@ -22,16 +22,16 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
         * ロガー
         */
        private static final Logger logger = Logger.getLogger(PartsImageDirectoryWatchAgent.class.getName());
-       
+
        private final CharacterData characterData;
-       
+
        private final File baseDir;
-       
+
        /**
         * リスナーとサスペンド要求マップのロックオブジェクト
         */
        private final Object lockListeners = new Object();
-       
+
        /**
         * 監視を通知されるリスナー.
         */
@@ -43,46 +43,46 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
         */
        private IdentityHashMap<PartsImageDirectoryWatchListener, Integer> suspendStateMap
                        = new IdentityHashMap<PartsImageDirectoryWatchListener, Integer>();
-       
-       
+
+
        /**
         * ロックオブジェクト
         */
        private final Object lock = new Object();
-       
-       
+
+
        /**
         * 停止フラグ
         */
        private volatile boolean stopFlag;
-       
+
        /**
         * 監視インターバル
         */
        private int dirWatchInterval;
-       
+
        /**
         * スレッド、生成されていなければnull
         */
        private Thread thread;
-       
+
        /**
         * 監視結果1、まだ監視されていなければnull
         */
        private volatile Long signature;
-       
+
        /**
         * 監視結果2、検出されたアイテムの個数
         */
        private volatile int itemCount;
-       
+
        /**
         * 監視結果3、検出されたアイテムの最終更新日。ただし未来の日付は除外する。
         */
        private volatile long maxLastModified;
 
-       
-       
+
+
        public PartsImageDirectoryWatchAgentThread(CharacterData characterData) {
                if (characterData == null) {
                        throw new IllegalArgumentException();
@@ -98,11 +98,11 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                this.baseDir = baseDir;
                this.dirWatchInterval = AppConfig.getInstance().getDirWatchInterval();
        }
-       
+
        public CharacterData getCharcterData() {
                return characterData;
        }
-       
+
        /**
         * 監視を開始する.<br>
         * 一定時間待機後、フォルダを監視し、前回監視結果と比較して変更があれば通知を行う.<br>
@@ -150,10 +150,10 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                        thread.start();
                }
        }
-       
+
        /**
         * 監視を停止する.<br>
-        * 
+        *
         * @return 停止した場合はtrue、すでに停止していたか開始されていない場合はfalse
         */
        private boolean stop() {
@@ -173,14 +173,14 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                                }
                                stopped = true;
                        }
-                       
+
                        // スレッドは停止されていると見なす.
                        thread = null;
                }
                return stopped;
        }
-       
-       
+
+
        /**
         * スレッドの停止フラグがたてられるまで、一定時間待機と監視とを永久に繰り返す.<br>
         * ただし、スレッド自身はデーモンとして動作させているので他の非デーモンスレッドが存在しなくなれば停止する.<br>
@@ -190,7 +190,7 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
 
                // 初回スキャンは無視するためリセット状態とする.
                this.signature = null;
-               
+
                // スキャンループ
                while (!stopFlag) {
                        try {
@@ -211,11 +211,11 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                }
                logger.log(Level.FINE, "watch-dir thead stopped. " + this);
        }
-       
+
        /**
         * 監視を行う.<br>
         * 停止フラグが設定されるか、割り込みされた場合は処理を中断してInterruptedException例外を返して終了する.<br>
-        * 
+        *
         * @param notifier
         *            通知するためのオブジェクト
         * @throws InterruptedException
@@ -229,7 +229,7 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                int itemCount = 0;
                long maxLastModified = 0;
                long now = System.currentTimeMillis() + dirWatchInterval;
-               
+
                CRC32 crc = new CRC32();
                for (PartsCategory partsCategory : characterData.getPartsCategories()) {
                        for (Layer layer : partsCategory.getLayers()) {
@@ -261,6 +261,8 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                                        Collections.sort(files);
                                }
                                for (String file : files) {
+                                       // 同一動作環境上でのファイル名の比較に使うためなので
+                                       // 文字コードは現在のデフォルトで良い。
                                        crc.update(file.getBytes());
                                }
                        }
@@ -281,12 +283,12 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                this.signature = Long.valueOf(signature);
                this.maxLastModified = maxLastModified;
                this.itemCount = itemCount;
-               
+
        }
-       
+
        /**
         * イベントリスナを登録する
-        * 
+        *
         * @param l
         *            リスナ
         */
@@ -298,10 +300,10 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                        changeSuspendState();
                }
        }
-       
+
        /**
         * イベントリスナを登録解除する
-        * 
+        *
         * @param l
         *            リスナ
         */
@@ -314,7 +316,7 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                        changeSuspendState();
                }
        }
-       
+
        public void suspend(PartsImageDirectoryWatchListener l) {
                if (l != null) {
                        synchronized (lockListeners) {
@@ -330,7 +332,7 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                        changeSuspendState();
                }
        }
-       
+
        public void resume(PartsImageDirectoryWatchListener l) {
                if (l != null) {
                        synchronized (lockListeners) {
@@ -347,7 +349,7 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                        changeSuspendState();
                }
        }
-       
+
        protected void changeSuspendState() {
                boolean active;
                synchronized (lockListeners) {
@@ -360,7 +362,7 @@ public class PartsImageDirectoryWatchAgentThread implements Runnable {
                        stop();
                }
        }
-       
+
        /**
         * イベントを通知する.
         */
index f2e88de..d1296c2 100644 (file)
@@ -34,6 +34,10 @@ public class FileUserData implements UserData {
                this.file = file;
        }
 
+       public File getFile() {
+               return file;
+       }
+
        @Override
        public boolean exists() {
                return file.exists() && file.isFile();
index 6f143bf..7c1d704 100644 (file)
@@ -2,6 +2,8 @@ package charactermanaj.util;
 
 import java.io.File;
 import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.Arrays;
 import java.util.UUID;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -124,8 +126,31 @@ public class UserDataFactory {
         * @return 保存先
         */
        public UserData getMangledNamedUserData(URI docBase, String name) {
-               String mangledName = getMangledName(docBase);
-               return getUserData(mangledName + "-" + name);
+               // Mangled名はファイル名を既定でUTF-8変換しハッシュ化したものを使用する
+               Charset fileNameEncoding = getMangledEncoding();
+               String mangledName = getMangledName(docBase, fileNameEncoding);
+               FileUserData userData = (FileUserData) getUserData(mangledName + "-" + name);
+
+               if (!userData.exists()) {
+                       // 指定されたMangled名が、まだ実在しない場合
+                       // システムデフォルトの文字コードでMangledされた旧名があれば
+                       // 旧名のファイル名をUTF-8によるMangledされたものにリネームする。
+                       // (ver0.999まではmangledの文字コードを明示していなかったため、
+                       // java5以前はシステム固有、java6以降ではfile.encodingのシステムプロパティで変動していた)
+                       for (Charset defaultEncoding : getSystemEncodings()) {
+                               if (!fileNameEncoding.equals(defaultEncoding)) {
+                                       String oldMangledName = getMangledName(docBase, defaultEncoding);
+                                       FileUserData oldUserData = (FileUserData) getUserData(oldMangledName + "-" + name);
+                                       if (oldUserData.exists()) {
+                                               logger.log(Level.INFO, "rename mangled: (" + defaultEncoding + ")" + oldUserData.getFile()
+                                                               + " -> (" + fileNameEncoding + ")" + userData.getFile());
+                                               oldUserData.getFile().renameTo(userData.getFile());
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               return userData;
        }
 
        /**
@@ -135,16 +160,18 @@ public class UserDataFactory {
         *
         * @param docBase
         *            URI、null可
+        * @param encoding
+        *            docBase名をハッシュ化するための文字コード
         * @return 名前ベースのUUID
         */
-       private String getMangledName(URI docBase) {
+       private String getMangledName(URI docBase, Charset encoding) {
                String docBaseStr;
                if (docBase == null) {
                        docBaseStr = "";
                } else {
                        docBaseStr = docBase.toString();
                }
-               String mangledName = UUID.nameUUIDFromBytes(docBaseStr.getBytes()).toString();
+               String mangledName = UUID.nameUUIDFromBytes(docBaseStr.getBytes(encoding)).toString();
 
                if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST, "mangledName " + docBase + "=" + mangledName);
@@ -152,5 +179,88 @@ public class UserDataFactory {
 
                return mangledName;
        }
+
+       /**
+        * Mangled名生成に使う文字コード
+        */
+       private static Charset mangledEncoding;
+
+       /**
+        * システムのデフォルトの文字コードの候補。
+        */
+       private static Charset[] systemEncodings;
+
+       /**
+        * Mandled名生成に使う文字コードを指定するシステムプロパティ名
+        */
+       public static final String KEY_MANGLED_ENCODING = "charactermanaj.mangled.encoding";
+
+       /**
+        *
+        * @return
+        */
+       private static Charset getMangledEncoding() {
+               if (mangledEncoding == null) {
+                       String encName = System.getProperty(KEY_MANGLED_ENCODING, "UTF-8");
+                       Charset fileNameEncoding = null;
+                       if (encName != null && encName.length() > 0) {
+                               try {
+                                       fileNameEncoding = Charset.forName(encName);
+                               } catch (Exception ex) {
+                                       logger.log(Level.SEVERE, "unsupported charset: " + encName, ex);
+                               }
+                       }
+                       if (fileNameEncoding == null) {
+                               // だめならシステム標準にする
+                               fileNameEncoding = Charset.defaultCharset();
+                       }
+                       mangledEncoding = fileNameEncoding;
+               }
+               return mangledEncoding;
+       }
+
+       /**
+        * システムのデフォルトの文字コードの候補リストを取得する。
+        * コンテンツの文字コード(file.encoding)とファイル名の文字コード(sun.jnu.encoding)の両方を取得する。
+        * ただし、どちらも同じコードである場合は1つにまとめられる。
+        * これらのシステムプロパティのデフォルト値が、どのように決定されるかは以下参照。
+        * https://stackoverflow.com/questions/1006276/what-is-the-default-encoding-of-the-jvm
+        * http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/native/java/lang/System.c#l169
+        * http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/solaris/native/java/lang/java_props_md.c#l427
+        * @return システムのデフォルトの文字コードの候補リスト
+        */
+       private static Charset[] getSystemEncodings() {
+               if (systemEncodings == null) {
+                       String[] keys = { "file.encoding", "sun.jnu.encoding" };
+                       Charset[] encodings = new Charset[keys.length];
+                       int cnt = 0;
+                       for (String key : keys) {
+                               String encodingName = System.getProperty(key);
+                               if (encodingName != null && encodingName.length() > 0) {
+                                       Charset encoding;
+                                       try {
+                                               encoding = Charset.forName(encodingName);
+                                       } catch (Exception ex) {
+                                               // 基本的にはありえない
+                                               logger.log(Level.SEVERE, "invalid charset:" + encodingName, ex);
+                                               continue;
+                                       }
+
+                                       boolean found = false;
+                                       for (int idx = 0; idx < cnt; idx++) {
+                                               if (encodings[idx].equals(encoding)) {
+                                                       found = true;
+                                                       break;
+                                               }
+                                       }
+                                       if (!found) {
+                                               encodings[cnt++] = encoding;
+                                       }
+                               }
+                       }
+                       systemEncodings = Arrays.copyOf(encodings, cnt);
+               }
+               return systemEncodings;
+       }
 }