Merge branch 'subtree'
[fanfix.git] / src / be / nikiroo / fanfix / Instance.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix;
2
3import java.io.File;
4import java.io.IOException;
b42117f1 5import java.util.Date;
08fe2e33
NR
6
7import be.nikiroo.fanfix.bundles.Config;
8import be.nikiroo.fanfix.bundles.ConfigBundle;
99ccbdf6 9import be.nikiroo.fanfix.bundles.StringId;
08fe2e33 10import be.nikiroo.fanfix.bundles.StringIdBundle;
5bc9573b 11import be.nikiroo.fanfix.bundles.StringIdGuiBundle;
b4dc6ab5
NR
12import be.nikiroo.fanfix.bundles.UiConfig;
13import be.nikiroo.fanfix.bundles.UiConfigBundle;
e42573a0 14import be.nikiroo.fanfix.library.BasicLibrary;
5895a958 15import be.nikiroo.fanfix.library.CacheLibrary;
e42573a0 16import be.nikiroo.fanfix.library.LocalLibrary;
e023483b 17import be.nikiroo.fanfix.library.RemoteLibrary;
f433d153 18import be.nikiroo.fanfix.library.WebLibrary;
581d42c0 19import be.nikiroo.utils.Cache;
b42117f1 20import be.nikiroo.utils.IOUtils;
12443642 21import be.nikiroo.utils.Image;
dddfac1d 22import be.nikiroo.utils.Proxy;
2aac79c7 23import be.nikiroo.utils.TempFiles;
581d42c0 24import be.nikiroo.utils.TraceHandler;
08fe2e33
NR
25import be.nikiroo.utils.resources.Bundles;
26
27/**
28 * Global state for the program (services and singletons).
29 *
30 * @author niki
31 */
32public class Instance {
d66deb8d
NR
33 static private Instance instance;
34 static private Object instancelock = new Object();
35
36 private ConfigBundle config;
37 private UiConfigBundle uiconfig;
38 private StringIdBundle trans;
39 private DataLoader cache;
40 private StringIdGuiBundle transGui;
41 private BasicLibrary lib;
42 private File coverDir;
43 private File readerTmp;
44 private File remoteDir;
45 private String configDir;
46 private TraceHandler tracer;
47 private TempFiles tempFiles;
ee9b7083
NR
48
49 /**
50 * Initialise the instance -- if already initialised, nothing will happen.
51 * <p>
4ce96da3
NR
52 * Before calling this method, you may call
53 * {@link Bundles#setDirectory(String)} if wanted.
54 * <p>
55 * Note that this method will honour some environment variables, the 3 most
56 * important ones probably being:
57 * <ul>
58 * <li><tt>DEBUG</tt>: will enable DEBUG output if set to 1 (or Y or TRUE or
59 * ON, case insensitive)</li>
60 * <li><tt>CONFIG_DIR</tt>: will use this directory as configuration
61 * directory (supports $HOME notation, defaults to $HOME/.fanfix</li>
62 * <li><tt>BOOKS_DIR</tt>: will use this directory as library directory
63 * (supports $HOME notation, defaults to $HOME/Books</li>
64 * </ul>
ee9b7083
NR
65 */
66 static public void init() {
6a211e41
NR
67 init(false);
68 }
69
70 /**
f433d153
NR
71 * Initialise the instance -- if already initialised, nothing will happen
72 * unless you pass TRUE to <tt>force</tt>.
6a211e41 73 * <p>
f433d153
NR
74 * Before calling this method, you may call
75 * {@link Bundles#setDirectory(String)} if wanted.
6a211e41 76 * <p>
f433d153
NR
77 * Note: forcing the initialisation can be dangerous, so make sure to only
78 * make it under controlled circumstances -- for instance, at the start of
79 * the program, you could call {@link Instance#init()}, change some settings
80 * because you want to force those settings (it will also forbid users to
81 * change them!) and then call {@link Instance#init(boolean)} with
82 * <tt>force</tt> set to TRUE.
83 *
84 * @param force
85 * force the initialisation even if already initialised
6a211e41
NR
86 */
87 static public void init(boolean force) {
d66deb8d
NR
88 synchronized (instancelock) {
89 if (instance == null || force) {
90 instance = new Instance();
91 }
ee9b7083
NR
92 }
93
d66deb8d
NR
94 }
95
96 /**
97 * Force-initialise the {@link Instance} to a known value.
98 * <p>
99 * Usually for DEBUG/Test purposes.
100 *
f433d153
NR
101 * @param instance
102 * the actual Instance to use
d66deb8d
NR
103 */
104 static public void init(Instance instance) {
105 Instance.instance = instance;
106 }
107
108 /**
109 * The (mostly unique) instance of this {@link Instance}.
110 *
111 * @return the (mostly unique) instance
112 */
113 public static Instance getInstance() {
114 return instance;
115 }
ee9b7083 116
d66deb8d
NR
117 /**
118 * Actually initialise the instance.
119 * <p>
f433d153
NR
120 * Before calling this method, you may call
121 * {@link Bundles#setDirectory(String)} if wanted.
d66deb8d
NR
122 */
123 protected Instance() {
62c63b07 124 // Before we can configure it:
a3c35586
NR
125 Boolean debug = checkEnv("DEBUG");
126 boolean trace = debug != null && debug;
127 tracer = new TraceHandler(true, trace, trace);
62c63b07 128
9f705f1a
NR
129 // config dir:
130 configDir = getConfigDir();
b42117f1
NR
131 if (!new File(configDir).exists()) {
132 new File(configDir).mkdirs();
fe999aa4
NR
133 }
134
9f705f1a 135 // Most of the rest is dependent upon this:
38febea9 136 createConfigs(configDir, false);
a3c35586 137
4ff0b1a9 138 // Proxy support
d66deb8d 139 Proxy.use(config.getString(Config.NETWORK_PROXY));
2206ef66 140
9f705f1a 141 // update tracer:
a3c35586 142 if (debug == null) {
d66deb8d
NR
143 debug = config.getBoolean(Config.DEBUG_ERR, false);
144 trace = config.getBoolean(Config.DEBUG_TRACE, false);
d0114000
NR
145 }
146
62c63b07 147 tracer = new TraceHandler(true, debug, trace);
581d42c0 148
9f705f1a
NR
149 // default Library
150 remoteDir = new File(configDir, "remote");
151 lib = createDefaultLibrary(remoteDir);
778d8d85 152
12443642 153 // create cache and TMP
0275353d 154 File tmp = getFile(Config.CACHE_DIR, configDir, "tmp");
7cd006eb
NR
155 Image.setTemporaryFilesRoot(new File(tmp.getParent(), "tmp.images"));
156
13fdb89a 157 String ua = config.getString(Config.NETWORK_USER_AGENT, "");
08fe2e33 158 try {
b7cd9db8
NR
159 int hours = config.getInteger(Config.CACHE_MAX_TIME_CHANGING, 0);
160 int hoursLarge = config.getInteger(Config.CACHE_MAX_TIME_STABLE, 0);
f1fb834c 161 cache = new DataLoader(tmp, ua, hours, hoursLarge);
08fe2e33 162 } catch (IOException e) {
f433d153
NR
163 tracer.error(new IOException(
164 "Cannot create cache (will continue without cache)", e));
ae78e517 165 cache = new DataLoader(ua);
08fe2e33 166 }
ae78e517
NR
167
168 cache.setTraceHandler(tracer);
9f705f1a
NR
169
170 // readerTmp / coverDir
f433d153
NR
171 readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER, configDir,
172 "tmp-reader");
0275353d 173 coverDir = getFile(Config.DEFAULT_COVERS_DIR, configDir, "covers");
7cd006eb 174 coverDir.mkdirs();
2aac79c7
NR
175
176 try {
177 tempFiles = new TempFiles("fanfix");
178 } catch (IOException e) {
f433d153
NR
179 tracer.error(
180 new IOException("Cannot create temporary directory", e));
2aac79c7 181 }
08fe2e33
NR
182 }
183
581d42c0
NR
184 /**
185 * The traces handler for this {@link Cache}.
62c63b07
NR
186 * <p>
187 * It is never NULL.
581d42c0 188 *
62c63b07 189 * @return the traces handler (never NULL)
581d42c0 190 */
d66deb8d 191 public TraceHandler getTraceHandler() {
581d42c0
NR
192 return tracer;
193 }
194
195 /**
196 * The traces handler for this {@link Cache}.
197 *
f433d153
NR
198 * @param tracer
199 * the new traces handler or NULL
581d42c0 200 */
d66deb8d 201 public void setTraceHandler(TraceHandler tracer) {
62c63b07
NR
202 if (tracer == null) {
203 tracer = new TraceHandler(false, false, false);
204 }
205
d66deb8d 206 this.tracer = tracer;
ae78e517 207 cache.setTraceHandler(tracer);
581d42c0
NR
208 }
209
08fe2e33
NR
210 /**
211 * Get the (unique) configuration service for the program.
212 *
213 * @return the configuration service
214 */
d66deb8d 215 public ConfigBundle getConfig() {
08fe2e33
NR
216 return config;
217 }
218
b4dc6ab5
NR
219 /**
220 * Get the (unique) UI configuration service for the program.
221 *
222 * @return the configuration service
223 */
d66deb8d 224 public UiConfigBundle getUiConfig() {
b4dc6ab5
NR
225 return uiconfig;
226 }
227
ae78e517
NR
228 /**
229 * Reset the configuration.
230 *
f433d153
NR
231 * @param resetTrans
232 * also reset the translation files
ae78e517 233 */
d66deb8d 234 public void resetConfig(boolean resetTrans) {
ae78e517
NR
235 String dir = Bundles.getDirectory();
236 Bundles.setDirectory(null);
237 try {
238 try {
239 ConfigBundle config = new ConfigBundle();
240 config.updateFile(configDir);
241 } catch (IOException e) {
242 tracer.error(e);
243 }
244 try {
245 UiConfigBundle uiconfig = new UiConfigBundle();
246 uiconfig.updateFile(configDir);
247 } catch (IOException e) {
248 tracer.error(e);
249 }
250
251 if (resetTrans) {
252 try {
253 StringIdBundle trans = new StringIdBundle(null);
254 trans.updateFile(configDir);
255 } catch (IOException e) {
256 tracer.error(e);
257 }
258 }
259 } finally {
260 Bundles.setDirectory(dir);
261 }
262 }
263
08fe2e33 264 /**
f1fb834c 265 * Get the (unique) {@link DataLoader} for the program.
08fe2e33 266 *
f1fb834c 267 * @return the {@link DataLoader}
08fe2e33 268 */
d66deb8d 269 public DataLoader getCache() {
08fe2e33
NR
270 return cache;
271 }
272
273 /**
274 * Get the (unique) {link StringIdBundle} for the program.
5bc9573b
NR
275 * <p>
276 * This is used for the translations of the core parts of Fanfix.
39c3c689 277 *
08fe2e33
NR
278 * @return the {link StringIdBundle}
279 */
d66deb8d 280 public StringIdBundle getTrans() {
08fe2e33
NR
281 return trans;
282 }
283
5bc9573b
NR
284 /**
285 * Get the (unique) {link StringIdGuiBundle} for the program.
286 * <p>
287 * This is used for the translations of the GUI parts of Fanfix.
288 *
289 * @return the {link StringIdGuiBundle}
290 */
d66deb8d 291 public StringIdGuiBundle getTransGui() {
5bc9573b
NR
292 return transGui;
293 }
294
08fe2e33 295 /**
d7367179 296 * Get the (unique) {@link BasicLibrary} for the program.
08fe2e33 297 *
d7367179 298 * @return the {@link BasicLibrary}
08fe2e33 299 */
d66deb8d 300 public BasicLibrary getLibrary() {
778d8d85
NR
301 if (lib == null) {
302 throw new NullPointerException("We don't have a library to return");
303 }
304
08fe2e33
NR
305 return lib;
306 }
307
d7367179
NR
308 /**
309 * Change the default {@link BasicLibrary} for this program.
310 * <p>
311 * Be careful.
312 *
313 * @param lib
314 * the new {@link BasicLibrary}
315 */
316 public void setLibrary(BasicLibrary lib) {
317 this.lib = lib;
318 }
319
08fe2e33
NR
320 /**
321 * Return the directory where to look for default cover pages.
322 *
323 * @return the default covers directory
324 */
d66deb8d 325 public File getCoverDir() {
08fe2e33
NR
326 return coverDir;
327 }
328
3727aae2
NR
329 /**
330 * Return the directory where to store temporary files for the local reader.
331 *
332 * @return the directory
333 */
d66deb8d 334 public File getReaderDir() {
3727aae2
NR
335 return readerTmp;
336 }
337
b0e88ebd
NR
338 /**
339 * Return the directory where to store temporary files for the remote
68e2c6d2 340 * {@link LocalLibrary}.
b0e88ebd 341 *
f433d153
NR
342 * @param host
343 * the remote for this host
b0e88ebd
NR
344 *
345 * @return the directory
346 */
d66deb8d 347 public File getRemoteDir(String host) {
9f705f1a
NR
348 return getRemoteDir(remoteDir, host);
349 }
350
351 /**
352 * Return the directory where to store temporary files for the remote
353 * {@link LocalLibrary}.
354 *
f433d153
NR
355 * @param remoteDir
356 * the base remote directory
357 * @param host
358 * the remote for this host
9f705f1a
NR
359 *
360 * @return the directory
361 */
d66deb8d 362 private File getRemoteDir(File remoteDir, String host) {
b0e88ebd
NR
363 remoteDir.mkdirs();
364
365 if (host != null) {
930a399d
NR
366 host = host.replace("fanfix://", "");
367 host = host.replace("http://", "");
368 host = host.replace("https://", "");
369 host = host.replaceAll("[^a-zA-Z0-9=+.-]", "_");
370
b0e88ebd
NR
371 return new File(remoteDir, host);
372 }
373
374 return remoteDir;
375 }
376
b42117f1
NR
377 /**
378 * Check if we need to check that a new version of Fanfix is available.
379 *
380 * @return TRUE if we need to
381 */
d66deb8d 382 public boolean isVersionCheckNeeded() {
b42117f1 383 try {
f433d153
NR
384 long wait = config.getInteger(Config.NETWORK_UPDATE_INTERVAL, 0)
385 * 24 * 60 * 60 * 1000;
b42117f1 386 if (wait >= 0) {
f433d153
NR
387 String lastUpString = IOUtils
388 .readSmallFile(new File(configDir, "LAST_UPDATE"));
389 long delay = new Date().getTime()
390 - Long.parseLong(lastUpString);
b42117f1
NR
391 if (delay > wait) {
392 return true;
393 }
394 } else {
395 return false;
396 }
397 } catch (Exception e) {
398 // No file or bad file:
399 return true;
400 }
401
402 return false;
403 }
404
405 /**
406 * Notify that we checked for a new version of Fanfix.
407 */
d66deb8d 408 public void setVersionChecked() {
b42117f1 409 try {
f433d153
NR
410 IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE",
411 Long.toString(new Date().getTime()));
b42117f1 412 } catch (IOException e) {
581d42c0 413 tracer.error(e);
08fe2e33
NR
414 }
415 }
416
2aac79c7
NR
417 /**
418 * The facility to use temporary files in this program.
419 * <p>
420 * <b>MUST</b> be closed at end of program.
421 *
422 * @return the facility
423 */
d66deb8d 424 public TempFiles getTempFiles() {
2aac79c7
NR
425 return tempFiles;
426 }
427
9f705f1a 428 /**
f433d153
NR
429 * The configuration directory (will check, in order of preference, the
430 * system properties, the environment and then defaults to
f466a217 431 * {@link Instance#getHome()}/.fanfix).
9f705f1a
NR
432 *
433 * @return the config directory
434 */
d66deb8d 435 private String getConfigDir() {
f466a217 436 String configDir = System.getProperty("CONFIG_DIR");
9f705f1a
NR
437
438 if (configDir == null) {
439 configDir = System.getenv("CONFIG_DIR");
440 }
441
442 if (configDir == null) {
443 configDir = new File(getHome(), ".fanfix").getPath();
444 }
445
446 return configDir;
447 }
448
449 /**
450 * Create the config variables ({@link Instance#config},
5bc9573b
NR
451 * {@link Instance#uiconfig}, {@link Instance#trans} and
452 * {@link Instance#transGui}).
9f705f1a 453 *
f433d153
NR
454 * @param configDir
455 * the directory where to find the configuration files
456 * @param refresh
457 * TRUE to reset the configuration files from the default
458 * included ones
9f705f1a 459 */
d66deb8d 460 private void createConfigs(String configDir, boolean refresh) {
9f705f1a
NR
461 if (!refresh) {
462 Bundles.setDirectory(configDir);
463 }
464
465 try {
466 config = new ConfigBundle();
467 config.updateFile(configDir);
468 } catch (IOException e) {
469 tracer.error(e);
470 }
471
472 try {
473 uiconfig = new UiConfigBundle();
474 uiconfig.updateFile(configDir);
475 } catch (IOException e) {
476 tracer.error(e);
477 }
478
479 // No updateFile for this one! (we do not want the user to have custom
480 // translations that won't accept updates from newer versions)
481 trans = new StringIdBundle(getLang());
5bc9573b 482 transGui = new StringIdGuiBundle(getLang());
9f705f1a
NR
483
484 // Fix an old bug (we used to store custom translation files by
485 // default):
486 if (trans.getString(StringId.INPUT_DESC_CBZ) == null) {
487 trans.deleteFile(configDir);
488 }
489
a3c35586
NR
490 Boolean noutf = checkEnv("NOUTF");
491 if (noutf != null && noutf) {
9f705f1a 492 trans.setUnicode(false);
5bc9573b 493 transGui.setUnicode(false);
9f705f1a
NR
494 }
495
496 Bundles.setDirectory(configDir);
497 }
498
499 /**
500 * Create the default library as specified by the config.
501 *
f433d153
NR
502 * @param remoteDir
503 * the base remote directory if needed
9f705f1a
NR
504 *
505 * @return the default {@link BasicLibrary}
506 */
d66deb8d 507 private BasicLibrary createDefaultLibrary(File remoteDir) {
9f705f1a
NR
508 BasicLibrary lib = null;
509
f433d153
NR
510 boolean useRemote = config.getBoolean(Config.REMOTE_LIBRARY_ENABLED,
511 false);
459b4c28
NR
512 if (useRemote) {
513 String host = null;
514 int port = -1;
515 try {
f433d153
NR
516 host = config.getString(Config.REMOTE_LIBRARY_HOST,
517 "fanfix://localhost");
459b4c28
NR
518 port = config.getInteger(Config.REMOTE_LIBRARY_PORT, -1);
519 String key = config.getString(Config.REMOTE_LIBRARY_KEY);
520
f433d153
NR
521 if (!host.startsWith("http://") && !host.startsWith("https://")
522 && !host.startsWith("fanfix://")) {
523 host = "fanfix://" + host;
524 }
525
459b4c28 526 tracer.trace("Selecting remote library " + host + ":" + port);
f433d153
NR
527
528 if (host.startsWith("fanfix://")) {
529 lib = new RemoteLibrary(key, host, port);
530 } else {
531 lib = new WebLibrary(key, host, port);
532 }
533
534 lib = new CacheLibrary(getRemoteDir(remoteDir, host), lib,
535 uiconfig);
459b4c28 536 } catch (Exception e) {
f433d153
NR
537 tracer.error(
538 new IOException("Cannot create remote library for: "
539 + host + ":" + port, e));
459b4c28
NR
540 }
541 } else {
7cd006eb 542 String libDir = System.getenv("BOOKS_DIR");
9f705f1a 543 if (libDir == null || libDir.isEmpty()) {
f433d153
NR
544 libDir = getFile(Config.LIBRARY_DIR, configDir, "$HOME/Books")
545 .getPath();
9f705f1a
NR
546 }
547 try {
0275353d 548 lib = new LocalLibrary(new File(libDir), config);
9f705f1a 549 } catch (Exception e) {
f433d153
NR
550 tracer.error(new IOException(
551 "Cannot create library for directory: " + libDir, e));
9f705f1a 552 }
9f705f1a
NR
553 }
554
555 return lib;
556 }
557
08fe2e33
NR
558 /**
559 * Return a path, but support the special $HOME variable.
560 *
f433d153
NR
561 * @param id
562 * the key for the path, which may contain "$HOME"
563 * @param configDir
564 * the directory to use as base if not absolute
565 * @param def
566 * the default value if none (will be configDir-rooted if needed)
d66deb8d 567 * @return the path, with expanded "$HOME" if needed
08fe2e33 568 */
0275353d
NR
569 protected File getFile(Config id, String configDir, String def) {
570 String path = config.getString(id, def);
571 return getFile(path, configDir);
b4dc6ab5
NR
572 }
573
574 /**
575 * Return a path, but support the special $HOME variable.
576 *
f433d153
NR
577 * @param id
578 * the key for the path, which may contain "$HOME"
579 * @param configDir
580 * the directory to use as base if not absolute
581 * @param def
582 * the default value if none (will be configDir-rooted if needed)
d66deb8d 583 * @return the path, with expanded "$HOME" if needed
b4dc6ab5 584 */
0275353d
NR
585 protected File getFile(UiConfig id, String configDir, String def) {
586 String path = uiconfig.getString(id, def);
587 return getFile(path, configDir);
b4dc6ab5
NR
588 }
589
590 /**
591 * Return a path, but support the special $HOME variable.
592 *
f433d153
NR
593 * @param path
594 * the path, which may contain "$HOME"
595 * @param configDir
596 * the directory to use as base if not absolute
d66deb8d 597 * @return the path, with expanded "$HOME" if needed
b4dc6ab5 598 */
0275353d 599 protected File getFile(String path, String configDir) {
08fe2e33 600 File file = null;
08fe2e33
NR
601 if (path != null && !path.isEmpty()) {
602 path = path.replace('/', File.separatorChar);
603 if (path.contains("$HOME")) {
ae78e517 604 path = path.replace("$HOME", getHome());
0275353d
NR
605 } else if (!path.startsWith("/")) {
606 path = new File(configDir, path).getPath();
08fe2e33
NR
607 }
608
609 file = new File(path);
610 }
611
612 return file;
613 }
614
ae78e517 615 /**
7cd006eb
NR
616 * Return the home directory from the environment (FANFIX_DIR) or the system
617 * properties.
618 * <p>
619 * The environment variable is tested first. Then, the custom property
f433d153
NR
620 * "fanfix.home" is tried, followed by the usual "user.home" then
621 * "java.io.tmp" if nothing else is found.
ae78e517
NR
622 *
623 * @return the home
624 */
d66deb8d 625 protected String getHome() {
7cd006eb 626 String home = System.getenv("FANFIX_DIR");
b4f9071c
NR
627 if (home != null && new File(home).isFile()) {
628 home = null;
629 }
630
7cd006eb
NR
631 if (home == null || home.trim().isEmpty()) {
632 home = System.getProperty("fanfix.home");
633 if (home != null && new File(home).isFile()) {
634 home = null;
635 }
636 }
637
b4f9071c
NR
638 if (home == null || home.trim().isEmpty()) {
639 home = System.getProperty("user.home");
640 if (!new File(home).isDirectory()) {
641 home = null;
642 }
643 }
644
ae78e517
NR
645 if (home == null || home.trim().isEmpty()) {
646 home = System.getProperty("java.io.tmpdir");
b4f9071c
NR
647 if (!new File(home).isDirectory()) {
648 home = null;
649 }
ae78e517
NR
650 }
651
652 if (home == null) {
653 home = "";
654 }
655
656 return home;
657 }
658
08fe2e33
NR
659 /**
660 * The language to use for the application (NULL = default system language).
661 *
662 * @return the language
663 */
d66deb8d 664 protected String getLang() {
08fe2e33
NR
665 String lang = config.getString(Config.LANG);
666
f83510cf 667 if (lang == null || lang.isEmpty()) {
f433d153
NR
668 if (System.getenv("LANG") != null
669 && !System.getenv("LANG").isEmpty()) {
fee80815
NR
670 lang = System.getenv("LANG");
671 }
08fe2e33
NR
672 }
673
674 if (lang != null && lang.isEmpty()) {
675 lang = null;
676 }
677
678 return lang;
679 }
d0114000
NR
680
681 /**
682 * Check that the given environment variable is "enabled".
683 *
f433d153
NR
684 * @param key
685 * the variable to check
d0114000
NR
686 *
687 * @return TRUE if it is
688 */
d66deb8d 689 protected Boolean checkEnv(String key) {
d0114000
NR
690 String value = System.getenv(key);
691 if (value != null) {
692 value = value.trim().toLowerCase();
f433d153
NR
693 if ("yes".equals(value) || "true".equals(value)
694 || "on".equals(value) || "1".equals(value)
d0114000
NR
695 || "y".equals(value)) {
696 return true;
697 }
a3c35586
NR
698
699 return false;
d0114000
NR
700 }
701
a3c35586 702 return null;
d0114000 703 }
08fe2e33 704}