1 package be
.nikiroo
.fanfix
;
4 import java
.io
.IOException
;
5 import java
.net
.MalformedURLException
;
8 import be
.nikiroo
.fanfix
.bundles
.StringId
;
9 import be
.nikiroo
.fanfix
.data
.Chapter
;
10 import be
.nikiroo
.fanfix
.data
.Story
;
11 import be
.nikiroo
.fanfix
.output
.BasicOutput
;
12 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
13 import be
.nikiroo
.fanfix
.reader
.BasicReader
;
14 import be
.nikiroo
.fanfix
.reader
.BasicReader
.ReaderType
;
15 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
16 import be
.nikiroo
.fanfix
.supported
.BasicSupport
.SupportType
;
19 * Main program entry point.
24 private enum MainAction
{
25 IMPORT
, EXPORT
, CONVERT
, READ
, READ_URL
, LIST
, HELP
, SET_READER
29 * Main program entry point.
31 * Known environment variables:
33 * <li>NOUTF: if set to 1 or 'true', the program will prefer non-unicode
34 * {@link String}s when possible</li>
35 * <li>CONFIG_DIR: a path where to look for the <tt>.properties</tt> files
36 * before taking the included ones; they will also be saved/updated into
37 * this path when the program starts</li>
38 * <li>DEBUG: if set to 1 or 'true', the program will override the DEBUG_ERR
39 * configuration value with 'true'</li>
43 * <li>--import [URL]: import into library</li>
44 * <li>--export [id] [output_type] [target]: export story to target</li>
45 * <li>--convert [URL] [output_type] [target] (+info): convert URL into
47 * <li>--read [id] ([chapter number]): read the given story from the library
49 * <li>--read-url [URL] ([cahpter number]): convert on the fly and read the
50 * story, without saving it</li>
51 * <li>--list: list the stories present in the library</li>
52 * <li>--set-reader [reader type]: set the reader type to CLI or LOCAL for
57 * see method description
59 public static void main(String
[] args
) {
60 String urlString
= null;
62 String typeString
= null;
63 String chapString
= null;
65 MainAction action
= MainAction
.HELP
;
66 Boolean plusInfo
= null;
68 boolean noMoreActions
= false;
71 for (int i
= 0; exitCode
== 0 && i
< args
.length
; i
++) {
72 // Action (--) handling:
73 if (!noMoreActions
&& args
[i
].startsWith("--")) {
74 if (args
[i
].equals("--")) {
78 action
= MainAction
.valueOf(args
[i
].substring(2)
79 .toUpperCase().replace("-", "_"));
80 } catch (Exception e
) {
81 Instance
.syserr(new IllegalArgumentException(
82 "Unknown action: " + args
[i
], e
));
92 if (urlString
== null) {
101 } else if (typeString
== null) {
102 typeString
= args
[i
];
103 } else if (target
== null) {
110 if (urlString
== null) {
112 } else if (typeString
== null) {
113 typeString
= args
[i
];
114 } else if (target
== null) {
116 } else if (plusInfo
== null) {
117 if ("+info".equals(args
[i
])) {
127 if (typeString
== null) {
128 typeString
= args
[i
];
136 } else if (chapString
== null) {
137 chapString
= args
[i
];
143 if (urlString
== null) {
145 } else if (chapString
== null) {
146 chapString
= args
[i
];
155 exitCode
= setReaderType(args
[i
]);
160 if (exitCode
!= 255) {
163 exitCode
= imprt(urlString
);
166 exitCode
= export(urlString
, typeString
, target
);
169 exitCode
= convert(urlString
, typeString
, target
,
170 plusInfo
== null ?
false : plusInfo
);
173 exitCode
= list(typeString
);
176 exitCode
= read(luid
, chapString
, true);
179 exitCode
= read(urlString
, chapString
, false);
190 if (exitCode
== 255) {
195 System
.exit(exitCode
);
200 * Return an {@link URL} from this {@link String}, be it a file path or an
201 * actual {@link URL}.
203 * @param sourceString
206 * @return the corresponding {@link URL}
208 * @throws MalformedURLException
209 * if this is neither a file nor a conventional {@link URL}
211 private static URL
getUrl(String sourceString
) throws MalformedURLException
{
212 if (sourceString
== null || sourceString
.isEmpty()) {
213 throw new MalformedURLException("Empty url");
218 source
= new URL(sourceString
);
219 } catch (MalformedURLException e
) {
220 File sourceFile
= new File(sourceString
);
221 source
= sourceFile
.toURI().toURL();
228 * Import the given resource into the {@link Library}.
231 * the resource to import
233 * @return the exit return code (0 = success)
235 private static int imprt(String urlString
) {
237 Story story
= Instance
.getLibrary().imprt(getUrl(urlString
));
238 System
.out
.println(story
.getMeta().getLuid() + ": \""
239 + story
.getMeta().getTitle() + "\" imported.");
240 } catch (IOException e
) {
249 * Export the {@link Story} from the {@link Library} to the given target.
254 * the {@link OutputType} to use
258 * @return the exit return code (0 = success)
260 private static int export(String urlString
, String typeString
, String target
) {
261 OutputType type
= OutputType
.valueOfNullOkUC(typeString
);
263 Instance
.syserr(new Exception(trans(StringId
.OUTPUT_DESC
,
269 Story story
= Instance
.getLibrary().imprt(new URL(urlString
));
270 Instance
.getLibrary().export(story
.getMeta().getLuid(), type
,
272 } catch (IOException e
) {
281 * List the stories of the given type from the {@link Library} (unless NULL
282 * is passed, in which case all stories will be listed).
285 * the {@link SupportType} to list the known stories of, or NULL
286 * to list all stories
288 * @return the exit return code (0 = success)
290 private static int list(String typeString
) {
291 SupportType type
= null;
293 type
= SupportType
.valueOfNullOkUC(typeString
);
294 } catch (Exception e
) {
295 Instance
.syserr(new Exception(
296 trans(StringId
.INPUT_DESC
, typeString
), e
));
300 BasicReader
.getReader().start(type
);
306 * Start the CLI reader for this {@link Story}.
309 * the LUID of the {@link Story} in the {@link Library} <b>or</b>
310 * the {@link Story} {@link URL}
312 * which {@link Chapter} to read (starting at 1), or NULL to get
313 * the {@link Story} description
315 * TRUE if the source is the {@link Story} LUID, FALSE if it is a
318 * @return the exit return code (0 = success)
320 private static int read(String story
, String chapString
, boolean library
) {
322 BasicReader reader
= BasicReader
.getReader();
324 reader
.setStory(story
);
326 reader
.setStory(getUrl(story
));
329 if (chapString
!= null) {
331 reader
.read(Integer
.parseInt(chapString
));
332 } catch (NumberFormatException e
) {
333 Instance
.syserr(new IOException(
334 "Chapter number cannot be parsed: " + chapString
, e
));
340 } catch (IOException e
) {
349 * Convert the {@link Story} into another format.
352 * the source {@link Story} to convert
354 * the {@link OutputType} to convert to
358 * TRUE to also export the cover and info file, even if the given
359 * {@link OutputType} does not usually save them
361 * @return the exit return code (0 = success)
363 private static int convert(String urlString
, String typeString
,
364 String target
, boolean infoCover
) {
367 String sourceName
= urlString
;
369 URL source
= getUrl(urlString
);
370 sourceName
= source
.toString();
371 if (source
.toString().startsWith("file://")) {
372 sourceName
= sourceName
.substring("file://".length());
375 OutputType type
= OutputType
.valueOfAllOkUC(typeString
);
377 Instance
.syserr(new IOException(trans(
378 StringId
.ERR_BAD_OUTPUT_TYPE
, typeString
)));
383 BasicSupport support
= BasicSupport
.getSupport(source
);
384 if (support
!= null) {
385 Story story
= support
.process(source
);
388 target
= new File(target
).getAbsolutePath();
389 BasicOutput
.getOutput(type
, infoCover
).process(
391 } catch (IOException e
) {
392 Instance
.syserr(new IOException(trans(
393 StringId
.ERR_SAVING
, target
), e
));
397 Instance
.syserr(new IOException(trans(
398 StringId
.ERR_NOT_SUPPORTED
, source
)));
402 } catch (IOException e
) {
403 Instance
.syserr(new IOException(trans(StringId
.ERR_LOADING
,
408 } catch (MalformedURLException e
) {
409 Instance
.syserr(new IOException(trans(StringId
.ERR_BAD_URL
,
418 * Simple shortcut method to call {link Instance#getTrans()#getString()}.
421 * the ID to translate
423 * @return the translated result
425 private static String
trans(StringId id
, Object
... params
) {
426 return Instance
.getTrans().getString(id
, params
);
430 * Display the correct syntax of the program to the user to stdout, or an
431 * error message if the syntax used was wrong on stderr.
434 * TRUE to show the syntax help, FALSE to show "syntax error"
436 private static void syntax(boolean showHelp
) {
438 StringBuilder builder
= new StringBuilder();
439 for (SupportType type
: SupportType
.values()) {
440 builder
.append(trans(StringId
.ERR_SYNTAX_TYPE
, type
.toString(),
442 builder
.append('\n');
445 String typesIn
= builder
.toString();
446 builder
.setLength(0);
448 for (OutputType type
: OutputType
.values()) {
449 builder
.append(trans(StringId
.ERR_SYNTAX_TYPE
, type
.toString(),
451 builder
.append('\n');
454 String typesOut
= builder
.toString();
456 System
.out
.println(trans(StringId
.HELP_SYNTAX
, typesIn
, typesOut
));
458 System
.err
.println(trans(StringId
.ERR_SYNTAX
));
463 * Set the default reader type for this session only (it can be changed in
464 * the configuration file, too, but this value will override it).
466 * @param readerTypeString
469 private static int setReaderType(String readerTypeString
) {
471 ReaderType readerType
= ReaderType
.valueOf(readerTypeString
473 BasicReader
.setDefaultReaderType(readerType
);
475 } catch (IllegalArgumentException e
) {
476 Instance
.syserr(new IOException("Unknown reader type: "
477 + readerTypeString
, e
));