4 * License : The MIT License
5 * Copyright(c) 2012 olyutorskii
8 package jp.sfjp.jindolf.config;
10 import java.io.BufferedInputStream;
11 import java.io.BufferedOutputStream;
12 import java.io.BufferedReader;
13 import java.io.BufferedWriter;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.InputStreamReader;
18 import java.io.OutputStream;
19 import java.io.OutputStreamWriter;
20 import java.io.Reader;
21 import java.io.Writer;
22 import java.nio.charset.Charset;
23 import java.nio.charset.StandardCharsets;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.logging.Level;
28 import java.util.logging.Logger;
29 import jp.sourceforge.jovsonz.JsComposition;
30 import jp.sourceforge.jovsonz.JsObject;
31 import jp.sourceforge.jovsonz.JsParseException;
32 import jp.sourceforge.jovsonz.JsTypes;
33 import jp.sourceforge.jovsonz.JsVisitException;
34 import jp.sourceforge.jovsonz.Json;
39 public class ConfigStore {
42 public static final Path HIST_FILE = Paths.get("searchHistory.json");
44 public static final Path NETCONFIG_FILE = Paths.get("netconfig.json");
46 public static final Path TALKCONFIG_FILE = Paths.get("talkconfig.json");
47 /** ローカル画像格納ディレクトリ。 */
48 public static final Path LOCALIMG_DIR = Paths.get("img");
50 public static final Path LOCALIMGCONFIG_PATH =
51 Paths.get("avatarCache.json");
53 private static final String LOCKFILE = "lock";
55 private static final Charset CHARSET_JSON = StandardCharsets.UTF_8;
57 private static final Logger LOGGER = Logger.getAnonymousLogger();
60 private boolean useStoreFile;
61 private boolean isImplicitPath;
62 private Path configDir;
68 * @param useStoreFile 設定ディレクトリ内への
70 * @param isImplicitPath 起動コマンドラインから指定された
72 * @param configDirPath 設定ディレクトリ。
73 * 設定ディレクトリを使わない場合は無視される。
75 public ConfigStore(boolean useStoreFile,
76 boolean isImplicitPath,
80 this.useStoreFile = useStoreFile;
82 if(this.useStoreFile){
83 this.isImplicitPath = isImplicitPath;
84 this.configDir = configDirPath;
86 this.isImplicitPath = true;
87 this.configDir = null;
97 * @return 設定ディレクトリを使うならtrue。
99 public boolean useStoreFile(){
100 return this.useStoreFile;
106 * @return 設定ディレクトリ。設定ディレクトリを使わない場合はnull
108 public Path getConfigDir(){
109 return this.configDir;
115 * @return 格納ディレクトリ。格納ディレクトリを使わない場合はnull
117 public Path getLocalImgDir(){
118 if(this.configDir == null) return null;
120 Path result = this.configDir.resolve(LOCALIMG_DIR);
126 * 設定ディレクトリの存在を確認し、なければ作る。
128 * <p>設定ディレクトリを使わない場合は何もしない。
130 public void prepareConfigDir(){
131 if( ! this.useStoreFile ) return;
133 if( ! Files.exists(this.configDir) ){
135 ConfigDirUtils.buildConfigDirectory(this.configDir,
136 this.isImplicitPath );
137 ConfigDirUtils.checkAccessibility(created);
139 ConfigDirUtils.checkAccessibility(this.configDir);
142 Path imgDir = this.configDir.resolve("img");
143 if( ! Files.exists(imgDir) ){
144 ConfigDirUtils.buildImageCacheDir(imgDir);
153 * <p>ロックに失敗したが処理を続行する場合、
154 * 設定ディレクトリは使わないものとして続行する。
156 public void tryLock(){
157 if( ! this.useStoreFile ) return;
159 File lockFile = new File(this.configDir.toFile(), LOCKFILE);
160 InterVMLock lock = new InterVMLock(lockFile);
164 if( ! lock.isFileOwner() ){
165 ConfigDirUtils.confirmLockError(lock);
166 if( ! lock.isFileOwner() ){
167 this.useStoreFile = false;
168 this.isImplicitPath = true;
169 this.configDir = null;
177 * 設定ディレクトリ上のOBJECT型JSONファイルを読み込む。
179 * @param file JSONファイルの相対パス。
180 * @return JSON object。
182 * もしくはJSONファイルが存在しない、
186 public JsObject loadJsObject(Path file){
187 JsComposition<?> root = loadJson(file);
188 if(root == null || root.getJsTypes() != JsTypes.OBJECT) return null;
189 JsObject result = (JsObject) root;
194 * 設定ディレクトリ上のJSONファイルを読み込む。
196 * @param file JSONファイルの相対パス
197 * @return JSON objectまたはarray。
199 * もしくはJSONファイルが存在しない、
202 public JsComposition<?> loadJson(Path file){
203 if( ! this.useStoreFile ) return null;
206 if(file.isAbsolute()){
209 if(this.configDir == null) return null;
210 absFile = this.configDir.resolve(file);
211 if( ! Files.exists(absFile) ) return null;
212 if( ! absFile.isAbsolute() ) return null;
214 String absPath = absFile.toString();
218 istream = Files.newInputStream(absFile);
219 }catch(IOException e){
223 istream = new BufferedInputStream(istream);
225 JsComposition<?> root;
227 root = loadJson(istream);
228 }catch(IOException e){
229 LOGGER.log(Level.SEVERE,
232 + "]の読み込み時に支障がありました。", e);
234 }catch(JsParseException e){
235 LOGGER.log(Level.SEVERE,
238 + "]の内容に不備があります。", e);
243 }catch(IOException e){
244 LOGGER.log(Level.SEVERE,
247 + "]を閉じることができません。", e);
256 * バイトストリーム上のJSONデータを読み込む。
258 * <p>バイトストリームはUTF-8と解釈される。
261 * @return JSON objectまたはarray。
262 * @throws IOException 入力エラー
263 * @throws JsParseException 構文エラー
265 protected JsComposition<?> loadJson(InputStream is)
266 throws IOException, JsParseException {
267 Reader reader = new InputStreamReader(is, CHARSET_JSON);
268 reader = new BufferedReader(reader);
269 JsComposition<?> root = loadJson(reader);
274 * 文字ストリーム上のJSONデータを読み込む。
276 * @param reader 文字ストリーム
277 * @return JSON objectまたはarray。
278 * @throws IOException 入力エラー
279 * @throws JsParseException 構文エラー
281 protected JsComposition<?> loadJson(Reader reader)
282 throws IOException, JsParseException {
283 JsComposition<?> root = Json.parseJson(reader);
288 * 設定ディレクトリ上のJSONファイルに書き込む。
290 * @param file JSONファイルの相対パス
291 * @param root JSON objectまたはarray
292 * @return 正しくセーブが行われればtrue。
293 * 何らかの理由でセーブが完了できなければfalse
295 public boolean saveJson(Path file, JsComposition<?> root){
296 if( ! this.useStoreFile ) return false;
298 // TODO テンポラリファイルを用いたより安全なファイル更新
299 Path absFile = this.configDir.resolve(file);
300 String absPath = absFile.toString();
303 Files.delete(absFile);
304 Files.createFile(absFile);
305 }catch(IOException e){
306 LOGGER.log(Level.SEVERE,
309 + "]の新規生成ができません。", e);
313 OutputStream ostream;
315 ostream = Files.newOutputStream(absFile);
316 }catch(IOException e){
320 ostream = new BufferedOutputStream(ostream);
323 saveJson(ostream, root);
324 }catch(JsVisitException e){
325 LOGGER.log(Level.SEVERE,
328 + "]の出力処理で支障がありました。", e);
330 }catch(IOException e){
331 LOGGER.log(Level.SEVERE,
334 + "]の書き込み時に支障がありました。", e);
339 }catch(IOException e){
340 LOGGER.log(Level.SEVERE,
343 + "]を閉じることができません。", e);
352 * バイトストリームにJSONデータを書き込む。
354 * <p>バイトストリームはUTF-8と解釈される。
356 * @param os バイトストリーム出力
357 * @param root JSON objectまたはarray
358 * @throws IOException 出力エラー
359 * @throws JsVisitException 構造エラー
361 protected void saveJson(OutputStream os, JsComposition<?> root)
362 throws IOException, JsVisitException {
363 Writer writer = new OutputStreamWriter(os, CHARSET_JSON);
364 writer = new BufferedWriter(writer);
365 saveJson(writer, root);
370 * 文字ストリームにJSONデータを書き込む。
372 * @param writer 文字ストリーム出力
373 * @param root JSON objectまたはarray
374 * @throws IOException 出力エラー
375 * @throws JsVisitException 構造エラー
377 protected void saveJson(Writer writer, JsComposition<?> root)
378 throws IOException, JsVisitException {
379 Json.dumpJson(writer, root);
386 * @return 履歴データ。履歴を読まないもしくは読めない場合はnull
388 public JsObject loadHistoryConfig(){
389 JsObject result = loadJsObject(HIST_FILE);
396 * @return ネットワーク設定データ。
397 * 設定を読まないもしくは読めない場合はnull
399 public JsObject loadNetConfig(){
400 JsObject result = loadJsObject(NETCONFIG_FILE);
408 * 設定を読まないもしくは読めない場合はnull
410 public JsObject loadTalkConfig(){
411 JsObject result = loadJsObject(TALKCONFIG_FILE);
418 * @return ローカル画像設定データ。
419 * 設定を読まないもしくは読めない場合はnull
421 public JsObject loadLocalImgConfig(){
422 Path path = LOCALIMG_DIR.resolve(LOCALIMGCONFIG_PATH);
423 JsObject result = loadJsObject(path);
431 * @return 書き込まなかったもしくは書き込めなかった場合はfalse
433 public boolean saveHistoryConfig(JsComposition<?> root){
434 boolean result = saveJson(HIST_FILE, root);
441 * @param root ネットワーク設定
442 * @return 書き込まなかったもしくは書き込めなかった場合はfalse
444 public boolean saveNetConfig(JsComposition<?> root){
445 boolean result = saveJson(NETCONFIG_FILE, root);
453 * @return 書き込まなかったもしくは書き込めなかった場合はfalse
455 public boolean saveTalkConfig(JsComposition<?> root){
456 boolean result = saveJson(TALKCONFIG_FILE, root);