Some fixes: output types, libraries, remote
[fanfix.git] / src / be / nikiroo / fanfix / Instance.java
1 package be.nikiroo.fanfix;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.Date;
6
7 import be.nikiroo.fanfix.bundles.Config;
8 import be.nikiroo.fanfix.bundles.ConfigBundle;
9 import be.nikiroo.fanfix.bundles.StringId;
10 import be.nikiroo.fanfix.bundles.StringIdBundle;
11 import be.nikiroo.fanfix.bundles.UiConfig;
12 import be.nikiroo.fanfix.bundles.UiConfigBundle;
13 import be.nikiroo.fanfix.library.BasicLibrary;
14 import be.nikiroo.fanfix.library.LocalLibrary;
15 import be.nikiroo.utils.IOUtils;
16 import be.nikiroo.utils.resources.Bundles;
17
18 /**
19 * Global state for the program (services and singletons).
20 *
21 * @author niki
22 */
23 public class Instance {
24 /**
25 * A handler when a recoverable exception was caught by the program.
26 *
27 * @author niki
28 */
29 public interface SyserrHandler {
30 /**
31 * An exception happened, log it.
32 *
33 * @param e
34 * the exception
35 * @param showDetails
36 * show more details (usually equivalent to the value of
37 * DEBUG)
38 */
39 public void notify(Exception e, boolean showDetails);
40 }
41
42 /**
43 * A handler when a trace message is sent.
44 *
45 * @author niki
46 */
47 public interface TraceHandler {
48 /**
49 * A trace happened, show it.
50 * <p>
51 * Will only be called if TRACE is true.
52 *
53 * @param message
54 * the trace message
55 */
56 public void trace(String message);
57 }
58
59 private static ConfigBundle config;
60 private static UiConfigBundle uiconfig;
61 private static StringIdBundle trans;
62 private static DataLoader cache;
63 private static LocalLibrary lib;
64 private static boolean debug;
65 private static boolean trace;
66 private static File coverDir;
67 private static File readerTmp;
68 private static File remoteDir;
69 private static String configDir;
70
71 private static SyserrHandler syserrHandler;
72
73 private static TraceHandler traceHandler;
74
75 static {
76 // Most of the rest is dependent upon this:
77 config = new ConfigBundle();
78
79 configDir = System.getProperty("CONFIG_DIR");
80 if (configDir == null) {
81 configDir = System.getenv("CONFIG_DIR");
82 }
83
84 if (configDir == null) {
85 configDir = new File(System.getProperty("user.home"), ".fanfix")
86 .getPath();
87 }
88
89 if (!new File(configDir).exists()) {
90 new File(configDir).mkdirs();
91 } else {
92 Bundles.setDirectory(configDir);
93 }
94
95 try {
96 config = new ConfigBundle();
97 config.updateFile(configDir);
98 } catch (IOException e) {
99 syserr(e);
100 }
101 try {
102 uiconfig = new UiConfigBundle();
103 uiconfig.updateFile(configDir);
104 } catch (IOException e) {
105 syserr(e);
106 }
107
108 // No updateFile for this one! (we do not want the user to have custom
109 // translations that won't accept updates from newer versions)
110 trans = new StringIdBundle(getLang());
111
112 // Fix an old bug (we used to store custom translation files by
113 // default):
114 if (trans.getString(StringId.INPUT_DESC_CBZ) == null) {
115 // TODO: create the deleteFile method
116 // trans.deleteFile(configDir);
117 }
118
119 Bundles.setDirectory(configDir);
120
121 uiconfig = new UiConfigBundle();
122 trans = new StringIdBundle(getLang());
123 try {
124 lib = new LocalLibrary(getFile(Config.LIBRARY_DIR));
125 } catch (Exception e) {
126 syserr(new IOException("Cannot create library for directory: "
127 + getFile(Config.LIBRARY_DIR), e));
128 }
129
130 debug = Instance.getConfig().getBoolean(Config.DEBUG_ERR, false);
131 trace = Instance.getConfig().getBoolean(Config.DEBUG_TRACE, false);
132 coverDir = getFile(Config.DEFAULT_COVERS_DIR);
133 File tmp = getFile(Config.CACHE_DIR);
134 readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER);
135 remoteDir = new File(configDir, "remote");
136
137 if (checkEnv("NOUTF")) {
138 trans.setUnicode(false);
139 }
140
141 if (checkEnv("DEBUG")) {
142 debug = true;
143 }
144
145 // Could have used: System.getProperty("java.io.tmpdir")
146 if (tmp == null) {
147 tmp = new File(configDir, "tmp");
148 }
149 if (readerTmp == null) {
150 readerTmp = new File(configDir, "tmp-reader");
151 }
152 //
153
154 if (coverDir != null && !coverDir.exists()) {
155 syserr(new IOException(
156 "The 'default covers' directory does not exists: "
157 + coverDir));
158 coverDir = null;
159 }
160
161 try {
162 String ua = config.getString(Config.USER_AGENT);
163 int hours = config.getInteger(Config.CACHE_MAX_TIME_CHANGING, -1);
164 int hoursLarge = config
165 .getInteger(Config.CACHE_MAX_TIME_STABLE, -1);
166
167 cache = new DataLoader(tmp, ua, hours, hoursLarge);
168 } catch (IOException e) {
169 syserr(new IOException(
170 "Cannot create cache (will continue without cache)", e));
171 }
172 }
173
174 /**
175 * Get the (unique) configuration service for the program.
176 *
177 * @return the configuration service
178 */
179 public static ConfigBundle getConfig() {
180 return config;
181 }
182
183 /**
184 * Get the (unique) UI configuration service for the program.
185 *
186 * @return the configuration service
187 */
188 public static UiConfigBundle getUiConfig() {
189 return uiconfig;
190 }
191
192 /**
193 * Get the (unique) {@link DataLoader} for the program.
194 *
195 * @return the {@link DataLoader}
196 */
197 public static DataLoader getCache() {
198 return cache;
199 }
200
201 /**
202 * Get the (unique) {link StringIdBundle} for the program.
203 *
204 * @return the {link StringIdBundle}
205 */
206 public static StringIdBundle getTrans() {
207 return trans;
208 }
209
210 /**
211 * Get the (unique) {@link LocalLibrary} for the program.
212 *
213 * @return the {@link LocalLibrary}
214 */
215 public static BasicLibrary getLibrary() {
216 return lib;
217 }
218
219 /**
220 * Return the directory where to look for default cover pages.
221 *
222 * @return the default covers directory
223 */
224 public static File getCoverDir() {
225 return coverDir;
226 }
227
228 /**
229 * Return the directory where to store temporary files for the local reader.
230 *
231 * @return the directory
232 */
233 public static File getReaderDir() {
234 return readerTmp;
235 }
236
237 /**
238 * Return the directory where to store temporary files for the remote
239 * {@link LocalLibrary}.
240 *
241 * @param host
242 * the remote for this host
243 *
244 * @return the directory
245 */
246 public static File getRemoteDir(String host) {
247 remoteDir.mkdirs();
248
249 if (host != null) {
250 return new File(remoteDir, host);
251 }
252
253 return remoteDir;
254 }
255
256 /**
257 * Check if we need to check that a new version of Fanfix is available.
258 *
259 * @return TRUE if we need to
260 */
261 public static boolean isVersionCheckNeeded() {
262 try {
263 long wait = config.getInteger(Config.UPDATE_INTERVAL, 1) * 24 * 60
264 * 60 * 1000;
265 if (wait >= 0) {
266 String lastUpString = IOUtils.readSmallFile(new File(configDir,
267 "LAST_UPDATE"));
268 long delay = new Date().getTime()
269 - Long.parseLong(lastUpString);
270 if (delay > wait) {
271 return true;
272 }
273 } else {
274 return false;
275 }
276 } catch (Exception e) {
277 // No file or bad file:
278 return true;
279 }
280
281 return false;
282 }
283
284 /**
285 * Notify that we checked for a new version of Fanfix.
286 */
287 public static void setVersionChecked() {
288 try {
289 IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE",
290 Long.toString(new Date().getTime()));
291 } catch (IOException e) {
292 syserr(e);
293 }
294 }
295
296 /**
297 * Replace the global syserr handler.
298 *
299 * @param syserrHandler
300 * the new syserr handler
301 */
302 public static void setSyserrHandler(SyserrHandler syserrHandler) {
303 Instance.syserrHandler = syserrHandler;
304 }
305
306 /**
307 * Replace the global trace handler.
308 *
309 * @param traceHandler
310 * the new trace handler
311 */
312 public static void setTraceHandler(TraceHandler traceHandler) {
313 Instance.traceHandler = traceHandler;
314 }
315
316 /**
317 * Report an error to the user
318 *
319 * @param e
320 * the {@link Exception} to report
321 */
322 public static void syserr(Exception e) {
323 if (syserrHandler != null) {
324 syserrHandler.notify(e, debug);
325 } else {
326 if (debug) {
327 e.printStackTrace();
328 } else {
329 System.err.println(e.getMessage());
330 }
331 }
332 }
333
334 /**
335 * Notify of a debug message.
336 *
337 * @param message
338 * the message
339 */
340 public static void trace(String message) {
341 if (trace) {
342 if (traceHandler != null) {
343 traceHandler.trace(message);
344 } else {
345 System.out.println(message);
346 }
347 }
348 }
349
350 /**
351 * Return a path, but support the special $HOME variable.
352 *
353 * @return the path
354 */
355 private static File getFile(Config id) {
356 return getFile(config.getString(id));
357 }
358
359 /**
360 * Return a path, but support the special $HOME variable.
361 *
362 * @return the path
363 */
364 private static File getFile(UiConfig id) {
365 return getFile(uiconfig.getString(id));
366 }
367
368 /**
369 * Return a path, but support the special $HOME variable.
370 *
371 * @return the path
372 */
373 private static File getFile(String path) {
374 File file = null;
375 if (path != null && !path.isEmpty()) {
376 path = path.replace('/', File.separatorChar);
377 if (path.contains("$HOME")) {
378 path = path.replace("$HOME",
379 "" + System.getProperty("user.home"));
380 }
381
382 file = new File(path);
383 }
384
385 return file;
386 }
387
388 /**
389 * The language to use for the application (NULL = default system language).
390 *
391 * @return the language
392 */
393 private static String getLang() {
394 String lang = config.getString(Config.LANG);
395
396 if (System.getenv("LANG") != null && !System.getenv("LANG").isEmpty()) {
397 lang = System.getenv("LANG");
398 }
399
400 if (lang != null && lang.isEmpty()) {
401 lang = null;
402 }
403
404 return lang;
405 }
406
407 /**
408 * Check that the given environment variable is "enabled".
409 *
410 * @param key
411 * the variable to check
412 *
413 * @return TRUE if it is
414 */
415 private static boolean checkEnv(String key) {
416 String value = System.getenv(key);
417 if (value != null) {
418 value = value.trim().toLowerCase();
419 if ("yes".equals(value) || "true".equals(value)
420 || "on".equals(value) || "1".equals(value)
421 || "y".equals(value)) {
422 return true;
423 }
424 }
425
426 return false;
427 }
428 }