OSDN Git Service

split JSON I/O class.
authorOlyutorskii <olyutorskii@users.osdn.me>
Wed, 13 May 2020 11:17:12 +0000 (20:17 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Wed, 13 May 2020 11:17:12 +0000 (20:17 +0900)
src/main/java/jp/sfjp/jindolf/Controller.java
src/main/java/jp/sfjp/jindolf/config/AppSetting.java
src/main/java/jp/sfjp/jindolf/config/ConfigStore.java
src/main/java/jp/sfjp/jindolf/config/JsonIo.java [new file with mode: 0644]

index 1dc94d0..1e7de97 100644 (file)
@@ -42,6 +42,7 @@ import javax.swing.filechooser.FileNameExtensionFilter;
 import javax.swing.tree.TreePath;
 import jp.sfjp.jindolf.config.AppSetting;
 import jp.sfjp.jindolf.config.ConfigStore;
+import jp.sfjp.jindolf.config.JsonIo;
 import jp.sfjp.jindolf.config.OptionInfo;
 import jp.sfjp.jindolf.data.Anchor;
 import jp.sfjp.jindolf.data.DialogPref;
@@ -189,9 +190,9 @@ public class Controller
             LogUtils.switchHandler(newHandler);
         });
 
-        ConfigStore config = this.appSetting.getConfigStore();
+        JsonIo jsonIo = this.appSetting.getJsonIo();
 
-        JsObject history = config.loadHistoryConfig();
+        JsObject history = jsonIo.loadHistoryConfig();
         findPanel.putJson(history);
 
         FontInfo fontInfo = this.appSetting.getFontInfo();
@@ -1469,12 +1470,12 @@ public class Controller
      * アプリ正常終了処理。
      */
     private void shutdown(){
-        ConfigStore configStore = this.appSetting.getConfigStore();
+        JsonIo jsonIo = this.appSetting.getJsonIo();
 
         FindPanel findPanel = this.windowManager.getFindPanel();
         JsObject findConf = findPanel.getJson();
         if( ! findPanel.hasConfChanged(findConf) ){
-            configStore.saveHistoryConfig(findConf);
+            jsonIo.saveHistoryConfig(findConf);
         }
 
         this.appSetting.saveConfig();
index 951d50e..5e8f94c 100644 (file)
@@ -59,6 +59,7 @@ public class AppSetting{
 
     private final OptionInfo optInfo;
     private final ConfigStore configStore;
+    private final JsonIo jsonIo;
     private final Rectangle frameRect;
 
     private FontInfo fontInfo;
@@ -83,6 +84,7 @@ public class AppSetting{
 
         this.optInfo = info;
         this.configStore = parseConfigStore(this.optInfo);
+        this.jsonIo = new JsonIo(this.configStore);
         this.frameRect = parseGeometrySetting(this.optInfo);
 
         return;
@@ -97,25 +99,27 @@ public class AppSetting{
     private static ConfigStore parseConfigStore(OptionInfo option){
         CmdOption opt = option.getExclusiveOption(CmdOption.OPT_CONFDIR,
                                                   CmdOption.OPT_NOCONF );
-
-        boolean useConfig;
-        Path configPath;
-
-        if(opt == CmdOption.OPT_NOCONF){
-            useConfig = false;
-            configPath = null;
-        }else if(opt == CmdOption.OPT_CONFDIR){
-            useConfig = true;
-            String optArg = option.getStringArg(opt);
-            configPath = Paths.get(optArg);
-            configPath = configPath.toAbsolutePath();
+        ConfigStore result;
+        if(opt == null){
+            result = new ConfigStore(true, null);
         }else{
-            useConfig = true;
-            configPath = ConfigDirUtils.getDefaultConfDirPath();
+            switch(opt){
+            case OPT_NOCONF:
+                result = new ConfigStore();
+                break;
+            case OPT_CONFDIR:
+                String optArg = option.getStringArg(opt);
+                Path configPath = Paths.get(optArg);
+                configPath = configPath.toAbsolutePath();
+                result = new ConfigStore(true, configPath);
+                break;
+            default:
+                result = null;
+                assert false;
+                break;
+            }
         }
 
-        ConfigStore result = new ConfigStore(useConfig, configPath);
-
         return result;
     }
 
@@ -199,6 +203,15 @@ public class AppSetting{
     }
 
     /**
+     * JSON入出力設定を返す。
+     *
+     * @return 入出力設定
+     */
+    public JsonIo getJsonIo(){
+        return this.jsonIo;
+    }
+
+    /**
      * 初期のフレーム幅を返す。
      * @return 初期のフレーム幅
      */
@@ -296,7 +309,7 @@ public class AppSetting{
      * ネットワーク設定をロードする。
      */
     private void loadNetConfig(){
-        JsObject root = this.configStore.loadNetConfig();
+        JsObject root = this.jsonIo.loadNetConfig();
         if(root == null) return;
         this.loadedNetConfig = root;
 
@@ -315,7 +328,7 @@ public class AppSetting{
      * 会話表示設定をロードする。
      */
     private void loadTalkConfig(){
-        JsObject root = this.configStore.loadTalkConfig();
+        JsObject root = this.jsonIo.loadTalkConfig();
         if(root == null) return;
         this.loadedTalkConfig = root;
 
@@ -407,7 +420,7 @@ public class AppSetting{
      * ローカル画像設定をロードする。
      */
     private void loadLocalImageConfig(){
-        JsObject root = this.configStore.loadLocalImgConfig();
+        JsObject root = this.jsonIo.loadLocalImgConfig();
         if(root == null) return;
 
         JsValue faceConfig = root.getValue("avatarFace");
@@ -478,7 +491,7 @@ public class AppSetting{
             if(this.loadedNetConfig.equals(root)) return;
         }
 
-        this.configStore.saveNetConfig(root);
+        this.jsonIo.saveNetConfig(root);
 
         return;
     }
@@ -512,7 +525,7 @@ public class AppSetting{
             if(this.loadedTalkConfig.equals(root)) return;
         }
 
-        this.configStore.saveTalkConfig(root);
+        this.jsonIo.saveTalkConfig(root);
 
         return;
     }
index 578b72a..0983216 100644 (file)
@@ -7,31 +7,10 @@
 
 package jp.sfjp.jindolf.config;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jp.sourceforge.jovsonz.JsComposition;
-import jp.sourceforge.jovsonz.JsObject;
-import jp.sourceforge.jovsonz.JsParseException;
-import jp.sourceforge.jovsonz.JsTypes;
-import jp.sourceforge.jovsonz.JsVisitException;
-import jp.sourceforge.jovsonz.Json;
 
 /**
  * Jindolf設定ディレクトリ以下に配置される各種ファイル資源の管理を行う。
@@ -44,30 +23,14 @@ import jp.sourceforge.jovsonz.Json;
  * <li>JSON入出力
  * <li>ロックファイルの獲得/解放。
  * </ul>
- *
- *
  */
 public class ConfigStore {
 
-    /** 検索履歴ファイル。 */
-    public static final Path HIST_FILE = Paths.get("searchHistory.json");
-    /** ネットワーク設定ファイル。 */
-    public static final Path NETCONFIG_FILE = Paths.get("netconfig.json");
-    /** 台詞表示設定ファイル。 */
-    public static final Path TALKCONFIG_FILE = Paths.get("talkconfig.json");
-
     /** ローカル画像格納ディレクトリ。 */
     public static final Path LOCALIMG_DIR = Paths.get("img");
-    /** ローカル画像設定ファイル。 */
-    public static final Path LOCALIMGCONFIG_PATH =
-            Paths.get("avatarCache.json");
 
     private static final String LOCKFILE = "lock";
 
-    private static final Charset CHARSET_JSON = StandardCharsets.UTF_8;
-
-    private static final Logger LOGGER = Logger.getAnonymousLogger();
-
 
     private boolean useStoreFile;
     private Path configDir;
@@ -76,10 +39,34 @@ public class ConfigStore {
     /**
      * コンストラクタ。
      *
+     * <p>このインスタンスでは、
+     * 設定ディレクトリへの入出力を行わない。
+     */
+    public ConfigStore(){
+        this(false, null);
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     *
+     * @param configDirPath 設定ディレクトリ。
+     */
+    public ConfigStore(Path configDirPath){
+        this(true, configDirPath);
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     *
      * @param useStoreFile 設定ディレクトリ内への
-     *     データセーブ機能を使うならtrue
-     * @param configDirPath 設定ディレクトリ
+     *     入出力機能を使うならtrue
+     * @param configDirPath 設定ディレクトリ。
      *     設定ディレクトリを使わない場合は無視される。
+     *     この時点でのディレクトリ存在の有無は関知しない。
+     *     既存ディレクトリの各種属性チェックは後にチェックするものとする。
+     *     nullの場合デフォルトの設定ディレクトリが用いられる。
      */
     public ConfigStore(boolean useStoreFile,
                        Path configDirPath ){
@@ -88,11 +75,18 @@ public class ConfigStore {
         this.useStoreFile = useStoreFile;
 
         if(this.useStoreFile){
-            this.configDir = configDirPath;
+            if(configDirPath != null){
+                this.configDir = configDirPath;
+            }else{
+                this.configDir = ConfigDirUtils.getDefaultConfDirPath();
+            }
         }else{
             this.configDir = null;
         }
 
+        assert     (     this.useStoreFile  && this.configDir != null)
+                || ( ( ! this.useStoreFile) && this.configDir == null);
+
         return;
     }
 
@@ -178,258 +172,4 @@ public class ConfigStore {
         return;
     }
 
-    /**
-     * 設定ディレクトリ上のOBJECT型JSONファイルを読み込む。
-     *
-     * @param file JSONファイルの相対パス。
-     * @return JSON object。
-     *     設定ディレクトリを使わない設定、
-     *     もしくはJSONファイルが存在しない、
-     *     もしくはOBJECT型でなかった、
-     *     もしくは入力エラーがあればnull
-     */
-    public JsObject loadJsObject(Path file){
-        JsComposition<?> root = loadJson(file);
-        if(root == null || root.getJsTypes() != JsTypes.OBJECT) return null;
-        JsObject result = (JsObject) root;
-        return result;
-    }
-
-    /**
-     * 設定ディレクトリ上のJSONファイルを読み込む。
-     *
-     * @param file JSONファイルの相対パス
-     * @return JSON objectまたはarray。
-     *     設定ディレクトリを使わない設定、
-     *     もしくはJSONファイルが存在しない、
-     *     もしくは入力エラーがあればnull
-     */
-    public JsComposition<?> loadJson(Path file){
-        if( ! this.useStoreFile ) return null;
-
-        Path absFile;
-        if(file.isAbsolute()){
-            absFile = file;
-        }else{
-            if(this.configDir == null) return null;
-            absFile = this.configDir.resolve(file);
-            if( ! Files.exists(absFile) ) return null;
-            if( ! absFile.isAbsolute() ) return null;
-        }
-        String absPath = absFile.toString();
-
-        JsComposition<?> root;
-        try(InputStream is = Files.newInputStream(absFile)){
-            InputStream bis = new BufferedInputStream(is);
-            root = loadJson(bis);
-        }catch(IOException e){
-            LOGGER.log(Level.SEVERE,
-                    "JSONファイル["
-                    + absPath
-                    + "]の読み込み時に支障がありました。", e);
-            return null;
-        }catch(JsParseException e){
-            LOGGER.log(Level.SEVERE,
-                    "JSONファイル["
-                    + absPath
-                    + "]の内容に不備があります。", e);
-            return null;
-        }
-
-        return root;
-    }
-
-    /**
-     * バイトストリーム上のJSONデータを読み込む。
-     *
-     * <p>バイトストリームはUTF-8と解釈される。
-     *
-     * @param is バイトストリーム
-     * @return JSON objectまたはarray。
-     * @throws IOException 入力エラー
-     * @throws JsParseException 構文エラー
-     */
-    protected JsComposition<?> loadJson(InputStream is)
-            throws IOException, JsParseException {
-        Reader reader = new InputStreamReader(is, CHARSET_JSON);
-        reader = new BufferedReader(reader);
-        JsComposition<?> root = loadJson(reader);
-        return root;
-    }
-
-    /**
-     * 文字ストリーム上のJSONデータを読み込む。
-     *
-     * @param reader 文字ストリーム
-     * @return JSON objectまたはarray。
-     * @throws IOException 入力エラー
-     * @throws JsParseException 構文エラー
-     */
-    protected JsComposition<?> loadJson(Reader reader)
-            throws IOException, JsParseException {
-        JsComposition<?> root = Json.parseJson(reader);
-        return root;
-    }
-
-    /**
-     * 設定ディレクトリ上のJSONファイルに書き込む。
-     *
-     * @param file JSONファイルの相対パス
-     * @param root JSON objectまたはarray
-     * @return 正しくセーブが行われればtrue。
-     *     何らかの理由でセーブが完了できなければfalse
-     */
-    public boolean saveJson(Path file, JsComposition<?> root){
-        if( ! this.useStoreFile ) return false;
-
-        // TODO テンポラリファイルを用いたより安全なファイル更新
-        Path absFile = this.configDir.resolve(file);
-        String absPath = absFile.toString();
-
-        try{
-            Files.deleteIfExists(absFile);
-        }catch(IOException e){
-            // NOTHING
-            assert true;
-        }
-
-        try{
-            Files.createFile(absFile);
-        }catch(IOException e){
-            LOGGER.log(Level.SEVERE,
-                    "JSONファイル["
-                    + absPath
-                    + "]の新規生成ができません。", e);
-            return false;
-        }
-
-        try(OutputStream os = Files.newOutputStream(absFile)){
-            OutputStream bos = new BufferedOutputStream(os);
-            saveJson(bos, root);
-        }catch(IOException e){
-            LOGGER.log(Level.SEVERE,
-                    "JSONファイル["
-                    + absPath
-                    + "]の書き込み時に支障がありました。", e);
-            return false;
-        }catch(JsVisitException e){
-            LOGGER.log(Level.SEVERE,
-                    "JSONファイル["
-                    + absPath
-                    + "]の出力処理で支障がありました。", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * バイトストリームにJSONデータを書き込む。
-     *
-     * <p>バイトストリームはUTF-8と解釈される。
-     *
-     * @param os バイトストリーム出力
-     * @param root JSON objectまたはarray
-     * @throws IOException 出力エラー
-     * @throws JsVisitException 構造エラー
-     */
-    protected void saveJson(OutputStream os, JsComposition<?> root)
-            throws IOException, JsVisitException {
-        Writer writer = new OutputStreamWriter(os, CHARSET_JSON);
-        writer = new BufferedWriter(writer);
-        saveJson(writer, root);
-        return;
-    }
-
-    /**
-     * 文字ストリームにJSONデータを書き込む。
-     *
-     * @param writer 文字ストリーム出力
-     * @param root JSON objectまたはarray
-     * @throws IOException 出力エラー
-     * @throws JsVisitException 構造エラー
-     */
-    protected void saveJson(Writer writer, JsComposition<?> root)
-            throws IOException, JsVisitException {
-        Json.dumpJson(writer, root);
-        return;
-    }
-
-    /**
-     * 検索履歴ファイルを読み込む。
-     *
-     * @return 履歴データ。履歴を読まないもしくは読めない場合はnull
-     */
-    public JsObject loadHistoryConfig(){
-        JsObject result = loadJsObject(HIST_FILE);
-        return result;
-    }
-
-    /**
-     * ネットワーク設定ファイルを読み込む。
-     *
-     * @return ネットワーク設定データ。
-     *     設定を読まないもしくは読めない場合はnull
-     */
-    public JsObject loadNetConfig(){
-        JsObject result = loadJsObject(NETCONFIG_FILE);
-        return result;
-    }
-
-    /**
-     * 台詞表示設定ファイルを読み込む。
-     *
-     * @return 台詞表示設定データ。
-     *     設定を読まないもしくは読めない場合はnull
-     */
-    public JsObject loadTalkConfig(){
-        JsObject result = loadJsObject(TALKCONFIG_FILE);
-        return result;
-    }
-
-    /**
-     * ローカル画像設定ファイルを読み込む。
-     *
-     * @return ローカル画像設定データ。
-     *     設定を読まないもしくは読めない場合はnull
-     */
-    public JsObject loadLocalImgConfig(){
-        Path path = LOCALIMG_DIR.resolve(LOCALIMGCONFIG_PATH);
-        JsObject result = loadJsObject(path);
-        return result;
-    }
-
-    /**
-     * 検索履歴ファイルに書き込む。
-     *
-     * @param root 履歴データ
-     * @return 書き込まなかったもしくは書き込めなかった場合はfalse
-     */
-    public boolean saveHistoryConfig(JsComposition<?> root){
-        boolean result = saveJson(HIST_FILE, root);
-        return result;
-    }
-
-    /**
-     * ネットワーク設定ファイルに書き込む。
-     *
-     * @param root ネットワーク設定
-     * @return 書き込まなかったもしくは書き込めなかった場合はfalse
-     */
-    public boolean saveNetConfig(JsComposition<?> root){
-        boolean result = saveJson(NETCONFIG_FILE, root);
-        return result;
-    }
-
-    /**
-     * 台詞表示設定ファイルに書き込む。
-     *
-     * @param root 台詞表示設定
-     * @return 書き込まなかったもしくは書き込めなかった場合はfalse
-     */
-    public boolean saveTalkConfig(JsComposition<?> root){
-        boolean result = saveJson(TALKCONFIG_FILE, root);
-        return result;
-    }
-
 }
diff --git a/src/main/java/jp/sfjp/jindolf/config/JsonIo.java b/src/main/java/jp/sfjp/jindolf/config/JsonIo.java
new file mode 100644 (file)
index 0000000..2fba756
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * JSON I/O
+ *
+ * License : The MIT License
+ * Copyright(c) 2020 olyutorskii
+ */
+
+package jp.sfjp.jindolf.config;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jp.sourceforge.jovsonz.JsComposition;
+import jp.sourceforge.jovsonz.JsObject;
+import jp.sourceforge.jovsonz.JsParseException;
+import jp.sourceforge.jovsonz.JsTypes;
+import jp.sourceforge.jovsonz.JsVisitException;
+import jp.sourceforge.jovsonz.Json;
+
+/**
+ * JSONファイルの入出力。
+ */
+public class JsonIo {
+
+    /** 検索履歴ファイル。 */
+    public static final Path HIST_FILE = Paths.get("searchHistory.json");
+    /** ネットワーク設定ファイル。 */
+    public static final Path NETCONFIG_FILE = Paths.get("netconfig.json");
+    /** 台詞表示設定ファイル。 */
+    public static final Path TALKCONFIG_FILE = Paths.get("talkconfig.json");
+
+    /** ローカル画像設定ファイル。 */
+    public static final Path LOCALIMGCONFIG_PATH =
+            Paths.get("avatarCache.json");
+
+
+    private static final Charset CHARSET_JSON = StandardCharsets.UTF_8;
+    private static final Logger LOGGER = Logger.getAnonymousLogger();
+
+
+    private final ConfigStore configStore;
+
+
+    /**
+     * Constructor.
+     *
+     * @param configStore 設定ディレクトリ
+     */
+    public JsonIo(ConfigStore configStore){
+        super();
+        Objects.nonNull(configStore);
+        this.configStore = configStore;
+        return;
+    }
+
+
+    /**
+     * 設定ディレクトリ上のOBJECT型JSONファイルを読み込む。
+     *
+     * @param file JSONファイルの相対パス。
+     * @return JSON object。
+     *     設定ディレクトリを使わない設定、
+     *     もしくはJSONファイルが存在しない、
+     *     もしくはOBJECT型でなかった、
+     *     もしくは入力エラーがあればnull
+     */
+    public JsObject loadJsObject(Path file){
+        JsComposition<?> root = loadJson(file);
+        if(root == null || root.getJsTypes() != JsTypes.OBJECT) return null;
+        JsObject result = (JsObject) root;
+        return result;
+    }
+
+    /**
+     * 設定ディレクトリ上のJSONファイルを読み込む。
+     *
+     * @param file JSONファイルの相対パス
+     * @return JSON objectまたはarray。
+     *     設定ディレクトリを使わない設定、
+     *     もしくはJSONファイルが存在しない、
+     *     もしくは入力エラーがあればnull
+     */
+    public JsComposition<?> loadJson(Path file){
+        Path absFile;
+        if(file.isAbsolute()){
+            absFile = file;
+        }else{
+            Path configDir = this.configStore.getConfigDir();
+            if(configDir == null) return null;
+            absFile = configDir.resolve(file);
+            if( ! Files.exists(absFile) ) return null;
+            if( ! absFile.isAbsolute() ) return null;
+        }
+        String absPath = absFile.toString();
+
+        JsComposition<?> root;
+        try(InputStream is = Files.newInputStream(absFile)){
+            InputStream bis = new BufferedInputStream(is);
+            root = loadJson(bis);
+        }catch(IOException e){
+            LOGGER.log(Level.SEVERE,
+                    "JSONファイル["
+                    + absPath
+                    + "]の読み込み時に支障がありました。", e);
+            return null;
+        }catch(JsParseException e){
+            LOGGER.log(Level.SEVERE,
+                    "JSONファイル["
+                    + absPath
+                    + "]の内容に不備があります。", e);
+            return null;
+        }
+
+        return root;
+    }
+
+    /**
+     * バイトストリーム上のJSONデータを読み込む。
+     *
+     * <p>バイトストリームはUTF-8と解釈される。
+     *
+     * @param is バイトストリーム
+     * @return JSON objectまたはarray。
+     * @throws IOException 入力エラー
+     * @throws JsParseException 構文エラー
+     */
+    protected JsComposition<?> loadJson(InputStream is)
+            throws IOException, JsParseException {
+        Reader reader = new InputStreamReader(is, CHARSET_JSON);
+        reader = new BufferedReader(reader);
+        JsComposition<?> root = loadJson(reader);
+        return root;
+    }
+
+    /**
+     * 文字ストリーム上のJSONデータを読み込む。
+     *
+     * @param reader 文字ストリーム
+     * @return JSON objectまたはarray。
+     * @throws IOException 入力エラー
+     * @throws JsParseException 構文エラー
+     */
+    protected JsComposition<?> loadJson(Reader reader)
+            throws IOException, JsParseException {
+        JsComposition<?> root = Json.parseJson(reader);
+        return root;
+    }
+
+    /**
+     * 設定ディレクトリ上のJSONファイルに書き込む。
+     *
+     * @param file JSONファイルの相対パス
+     * @param root JSON objectまたはarray
+     * @return 正しくセーブが行われればtrue。
+     *     何らかの理由でセーブが完了できなければfalse
+     */
+    public boolean saveJson(Path file, JsComposition<?> root){
+        // TODO テンポラリファイルを用いたより安全なファイル更新
+        Path configDir = this.configStore.getConfigDir();
+        Path absFile = configDir.resolve(file);
+        String absPath = absFile.toString();
+
+        try{
+            Files.deleteIfExists(absFile);
+        }catch(IOException e){
+            // NOTHING
+            assert true;
+        }
+
+        try{
+            Files.createFile(absFile);
+        }catch(IOException e){
+            LOGGER.log(Level.SEVERE,
+                    "JSONファイル["
+                    + absPath
+                    + "]の新規生成ができません。", e);
+            return false;
+        }
+
+        try(OutputStream os = Files.newOutputStream(absFile)){
+            OutputStream bos = new BufferedOutputStream(os);
+            saveJson(bos, root);
+        }catch(IOException e){
+            LOGGER.log(Level.SEVERE,
+                    "JSONファイル["
+                    + absPath
+                    + "]の書き込み時に支障がありました。", e);
+            return false;
+        }catch(JsVisitException e){
+            LOGGER.log(Level.SEVERE,
+                    "JSONファイル["
+                    + absPath
+                    + "]の出力処理で支障がありました。", e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * バイトストリームにJSONデータを書き込む。
+     *
+     * <p>バイトストリームはUTF-8と解釈される。
+     *
+     * @param os バイトストリーム出力
+     * @param root JSON objectまたはarray
+     * @throws IOException 出力エラー
+     * @throws JsVisitException 構造エラー
+     */
+    protected void saveJson(OutputStream os, JsComposition<?> root)
+            throws IOException, JsVisitException {
+        Writer writer = new OutputStreamWriter(os, CHARSET_JSON);
+        writer = new BufferedWriter(writer);
+        saveJson(writer, root);
+        return;
+    }
+
+    /**
+     * 文字ストリームにJSONデータを書き込む。
+     *
+     * @param writer 文字ストリーム出力
+     * @param root JSON objectまたはarray
+     * @throws IOException 出力エラー
+     * @throws JsVisitException 構造エラー
+     */
+    protected void saveJson(Writer writer, JsComposition<?> root)
+            throws IOException, JsVisitException {
+        Json.dumpJson(writer, root);
+        return;
+    }
+
+    /**
+     * 検索履歴ファイルを読み込む。
+     *
+     * @return 履歴データ。履歴を読まないもしくは読めない場合はnull
+     */
+    public JsObject loadHistoryConfig(){
+        JsObject result = loadJsObject(HIST_FILE);
+        return result;
+    }
+
+    /**
+     * ネットワーク設定ファイルを読み込む。
+     *
+     * @return ネットワーク設定データ。
+     *     設定を読まないもしくは読めない場合はnull
+     */
+    public JsObject loadNetConfig(){
+        JsObject result = loadJsObject(NETCONFIG_FILE);
+        return result;
+    }
+
+    /**
+     * 台詞表示設定ファイルを読み込む。
+     *
+     * @return 台詞表示設定データ。
+     *     設定を読まないもしくは読めない場合はnull
+     */
+    public JsObject loadTalkConfig(){
+        JsObject result = loadJsObject(TALKCONFIG_FILE);
+        return result;
+    }
+
+    /**
+     * ローカル画像設定ファイルを読み込む。
+     *
+     * @return ローカル画像設定データ。
+     *     設定を読まないもしくは読めない場合はnull
+     */
+    public JsObject loadLocalImgConfig(){
+        Path path = ConfigStore.LOCALIMG_DIR.resolve(LOCALIMGCONFIG_PATH);
+        JsObject result = loadJsObject(path);
+        return result;
+    }
+
+    /**
+     * 検索履歴ファイルに書き込む。
+     *
+     * @param root 履歴データ
+     * @return 書き込まなかったもしくは書き込めなかった場合はfalse
+     */
+    public boolean saveHistoryConfig(JsComposition<?> root){
+        boolean result = saveJson(HIST_FILE, root);
+        return result;
+    }
+
+    /**
+     * ネットワーク設定ファイルに書き込む。
+     *
+     * @param root ネットワーク設定
+     * @return 書き込まなかったもしくは書き込めなかった場合はfalse
+     */
+    public boolean saveNetConfig(JsComposition<?> root){
+        boolean result = saveJson(NETCONFIG_FILE, root);
+        return result;
+    }
+
+    /**
+     * 台詞表示設定ファイルに書き込む。
+     *
+     * @param root 台詞表示設定
+     * @return 書き込まなかったもしくは書き込めなかった場合はfalse
+     */
+    public boolean saveTalkConfig(JsComposition<?> root){
+        boolean result = saveJson(TALKCONFIG_FILE, root);
+        return result;
+    }
+
+}