Add more warnings source to 1.6) and fix warnings
[nikiroo-utils.git] / src / be / nikiroo / fanfix / output / BasicOutput.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix.output;
2
3import java.io.File;
4import java.io.IOException;
bee7dffe
NR
5import java.util.ArrayList;
6import java.util.List;
08fe2e33
NR
7
8import be.nikiroo.fanfix.Instance;
9import be.nikiroo.fanfix.bundles.StringId;
10import be.nikiroo.fanfix.data.Chapter;
11import be.nikiroo.fanfix.data.Paragraph;
08fe2e33 12import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
bee7dffe 13import be.nikiroo.fanfix.data.Story;
3b2b638f 14import be.nikiroo.utils.Progress;
08fe2e33
NR
15
16/**
17 * This class is the base class used by the other output classes. It can be used
18 * outside of this package, and have static method that you can use to get
19 * access to the correct support class.
20 *
21 * @author niki
22 */
23public abstract class BasicOutput {
24 /**
25 * The supported output types for which we can get a {@link BasicOutput}
26 * object.
27 *
28 * @author niki
29 */
30 public enum OutputType {
31 /** EPUB files created with this program */
32 EPUB,
33 /** Pure text file with some rules */
34 TEXT,
35 /** TEXT but with associated .info file */
36 INFO_TEXT,
37 /** DEBUG output to console */
38 SYSOUT,
39 /** ZIP with (PNG) images */
40 CBZ,
41 /** LaTeX file with "book" template */
2206ef66
NR
42 LATEX,
43 /** HTML files in a dedicated directory */
44 HTML,
45
46 ;
47
211f7ddb 48 @Override
08fe2e33
NR
49 public String toString() {
50 return super.toString().toLowerCase();
51 }
52
53 /**
54 * A description of this output type.
55 *
4d205683
NR
56 * @param longDesc
57 * TRUE for the long description, FALSE for the short one
58 *
08fe2e33
NR
59 * @return the description
60 */
4d205683
NR
61 public String getDesc(boolean longDesc) {
62 StringId id = longDesc ? StringId.OUTPUT_DESC
63 : StringId.OUTPUT_DESC_SHORT;
64
65 String desc = Instance.getTrans().getStringX(id, this.name());
08fe2e33
NR
66
67 if (desc == null) {
4d205683 68 desc = Instance.getTrans().getString(id, this);
08fe2e33
NR
69 }
70
b2612f9d
NR
71 if (desc == null) {
72 desc = this.toString();
73 }
74
08fe2e33
NR
75 return desc;
76 }
77
2206ef66
NR
78 /**
79 * The default extension to add to the output files.
80 *
10d558d2
NR
81 * @param readerTarget
82 * the target to point to to read the {@link Story} (for
83 * instance, the main entry point if this {@link Story} is in
84 * a directory bundle)
85 *
2206ef66
NR
86 * @return the extension
87 */
10d558d2 88 public String getDefaultExtension(boolean readerTarget) {
2206ef66
NR
89 BasicOutput output = BasicOutput.getOutput(this, false);
90 if (output != null) {
10d558d2 91 return output.getDefaultExtension(readerTarget);
2206ef66
NR
92 }
93
94 return null;
95 }
96
08fe2e33 97 /**
0efd25e3
NR
98 * Call {@link OutputType#valueOf(String)} after conversion to upper
99 * case.
08fe2e33
NR
100 *
101 * @param typeName
102 * the possible type name
103 *
104 * @return NULL or the type
105 */
106 public static OutputType valueOfUC(String typeName) {
107 return OutputType.valueOf(typeName == null ? null : typeName
108 .toUpperCase());
109 }
110
111 /**
0efd25e3
NR
112 * Call {@link OutputType#valueOf(String)} after conversion to upper
113 * case but return NULL for NULL and empty instead of raising an
114 * exception.
08fe2e33
NR
115 *
116 * @param typeName
117 * the possible type name
118 *
119 * @return NULL or the type
120 */
121 public static OutputType valueOfNullOkUC(String typeName) {
a6395bef 122 if (typeName == null || typeName.isEmpty()) {
08fe2e33
NR
123 return null;
124 }
125
126 return OutputType.valueOfUC(typeName);
127 }
128
129 /**
0efd25e3
NR
130 * Call {@link OutputType#valueOf(String)} after conversion to upper
131 * case but return NULL in case of error instead of raising an
132 * exception.
08fe2e33
NR
133 *
134 * @param typeName
135 * the possible type name
136 *
137 * @return NULL or the type
138 */
139 public static OutputType valueOfAllOkUC(String typeName) {
140 try {
141 return OutputType.valueOfUC(typeName);
142 } catch (Exception e) {
143 return null;
144 }
145 }
146 }
147
148 /** The creator name (this program, by me!) */
149 static final String EPUB_CREATOR = "Fanfix (by Niki)";
150
151 /** The current best name for an image */
152 private String imageName;
153 private File targetDir;
154 private String targetName;
155 private OutputType type;
156 private boolean writeCover;
157 private boolean writeInfo;
bee7dffe
NR
158 private Progress storyPg;
159 private Progress chapPg;
08fe2e33
NR
160
161 /**
162 * Process the {@link Story} into the given target.
163 *
164 * @param story
165 * the {@link Story} to export
166 * @param target
167 * the target where to save to (will not necessary be taken as is
168 * by the processor, for instance an extension can be added)
bee7dffe
NR
169 * @param pg
170 * the optional progress reporter
08fe2e33
NR
171 *
172 * @return the actual main target saved, which can be slightly different
173 * that the input one
174 *
175 * @throws IOException
176 * in case of I/O error
177 */
bee7dffe
NR
178 public File process(Story story, String target, Progress pg)
179 throws IOException {
180 storyPg = pg;
181
d5a7153c
NR
182 File targetDir = null;
183 String targetName = null;
184 if (target != null) {
185 target = new File(target).getAbsolutePath();
186 targetDir = new File(target).getParentFile();
187 targetName = new File(target).getName();
188
189 String ext = getDefaultExtension(false);
190 if (ext != null && !ext.isEmpty()) {
191 if (targetName.toLowerCase().endsWith(ext)) {
192 targetName = targetName.substring(0, targetName.length()
193 - ext.length());
194 }
08fe2e33
NR
195 }
196 }
197
198 return process(story, targetDir, targetName);
199 }
200
201 /**
202 * Process the {@link Story} into the given target.
203 * <p>
204 * This method is expected to be overridden in most cases.
205 *
206 * @param story
207 * the {@link Story} to export
208 * @param targetDir
209 * the target dir where to save to
210 * @param targetName
211 * the target filename (will not necessary be taken as is by the
212 * processor, for instance an extension can be added)
213 *
bee7dffe 214 *
08fe2e33
NR
215 * @return the actual main target saved, which can be slightly different
216 * that the input one
217 *
218 * @throws IOException
219 * in case of I/O error
220 */
221 protected File process(Story story, File targetDir, String targetName)
222 throws IOException {
223 this.targetDir = targetDir;
224 this.targetName = targetName;
225
226 writeStory(story);
227
228 return null;
229 }
230
231 /**
232 * The output type.
233 *
234 * @return the type
235 */
236 public OutputType getType() {
237 return type;
238 }
239
240 /**
241 * The output type.
242 *
243 * @param type
244 * the new type
0efd25e3
NR
245 * @param writeInfo
246 * TRUE to enable the creation of a .info file
247 * @param writeCover
248 * TRUE to enable the creation of a cover if possible
08fe2e33
NR
249 *
250 * @return this
251 */
252 protected BasicOutput setType(OutputType type, boolean writeCover,
253 boolean writeInfo) {
254 this.type = type;
255 this.writeCover = writeCover;
256 this.writeInfo = writeInfo;
257
258 return this;
259 }
260
261 /**
262 * The default extension to add to the output files.
08fe2e33 263 *
10d558d2
NR
264 * @param readerTarget
265 * the target to point to to read the {@link Story} (for
266 * instance, the main entry point if this {@link Story} is in a
267 * directory bundle)
268 *
08fe2e33
NR
269 * @return the extension
270 */
211f7ddb
NR
271 public String getDefaultExtension(
272 @SuppressWarnings("unused") boolean readerTarget) {
08fe2e33
NR
273 return "";
274 }
275
211f7ddb 276 @SuppressWarnings("unused")
08fe2e33
NR
277 protected void writeStoryHeader(Story story) throws IOException {
278 }
279
211f7ddb 280 @SuppressWarnings("unused")
08fe2e33
NR
281 protected void writeChapterHeader(Chapter chap) throws IOException {
282 }
283
211f7ddb 284 @SuppressWarnings("unused")
08fe2e33
NR
285 protected void writeParagraphHeader(Paragraph para) throws IOException {
286 }
287
211f7ddb 288 @SuppressWarnings("unused")
08fe2e33
NR
289 protected void writeStoryFooter(Story story) throws IOException {
290 }
291
211f7ddb 292 @SuppressWarnings("unused")
08fe2e33
NR
293 protected void writeChapterFooter(Chapter chap) throws IOException {
294 }
295
211f7ddb 296 @SuppressWarnings("unused")
08fe2e33
NR
297 protected void writeParagraphFooter(Paragraph para) throws IOException {
298 }
299
300 protected void writeStory(Story story) throws IOException {
bee7dffe
NR
301 if (storyPg == null) {
302 storyPg = new Progress(0, story.getChapters().size() + 2);
303 } else {
304 storyPg.setMinMax(0, story.getChapters().size() + 2);
305 }
306
08fe2e33
NR
307 String chapterNameNum = String.format("%03d", 0);
308 String paragraphNumber = String.format("%04d", 0);
309 imageName = paragraphNumber + "_" + chapterNameNum + ".png";
310
fe999aa4 311 if (story.getMeta() != null) {
68686a37 312 story.getMeta().setType("" + getType());
fe999aa4 313 }
68686a37 314
08fe2e33
NR
315 if (writeCover) {
316 InfoCover.writeCover(targetDir, targetName, story.getMeta());
317 }
318 if (writeInfo) {
319 InfoCover.writeInfo(targetDir, targetName, story.getMeta());
320 }
321
bee7dffe
NR
322 storyPg.setProgress(1);
323
324 List<Progress> chapPgs = new ArrayList<Progress>(story.getChapters()
325 .size());
08fe2e33 326 for (Chapter chap : story) {
bee7dffe
NR
327 chapPg = new Progress(0, chap.getParagraphs().size());
328 storyPg.addProgress(chapPg, 1);
329 chapPgs.add(chapPg);
330 chapPg = null;
331 }
332
333 writeStoryHeader(story);
334 for (int i = 0; i < story.getChapters().size(); i++) {
335 chapPg = chapPgs.get(i);
336 writeChapter(story.getChapters().get(i));
337 chapPg.setProgress(chapPg.getMax());
338 chapPg = null;
08fe2e33
NR
339 }
340 writeStoryFooter(story);
bee7dffe
NR
341
342 storyPg.setProgress(storyPg.getMax());
343 storyPg = null;
08fe2e33
NR
344 }
345
346 protected void writeChapter(Chapter chap) throws IOException {
347 String chapterNameNum;
348 if (chap.getName() == null || chap.getName().isEmpty()) {
349 chapterNameNum = String.format("%03d", chap.getNumber());
350 } else {
351 chapterNameNum = String.format("%03d", chap.getNumber()) + "_"
352 + chap.getName().replace(" ", "_");
353 }
354
355 int num = 0;
356 String paragraphNumber = String.format("%04d", num++);
357 imageName = chapterNameNum + "_" + paragraphNumber + ".png";
358
359 writeChapterHeader(chap);
bee7dffe 360 int i = 1;
08fe2e33
NR
361 for (Paragraph para : chap) {
362 paragraphNumber = String.format("%04d", num++);
363 imageName = chapterNameNum + "_" + paragraphNumber + ".png";
364 writeParagraph(para);
bee7dffe
NR
365 if (chapPg != null) {
366 chapPg.setProgress(i++);
367 }
08fe2e33
NR
368 }
369 writeChapterFooter(chap);
370 }
371
372 protected void writeParagraph(Paragraph para) throws IOException {
373 writeParagraphHeader(para);
374 writeTextLine(para.getType(), para.getContent());
375 writeParagraphFooter(para);
376 }
377
211f7ddb 378 @SuppressWarnings("unused")
08fe2e33
NR
379 protected void writeTextLine(ParagraphType type, String line)
380 throws IOException {
381 }
382
383 /**
384 * Return the current best guess for an image name, based upon the current
385 * {@link Chapter} and {@link Paragraph}.
386 *
387 * @param prefix
388 * add the original target name as a prefix
389 *
390 * @return the guessed name
391 */
392 protected String getCurrentImageBestName(boolean prefix) {
393 if (prefix) {
394 return targetName + "_" + imageName;
395 }
396
397 return imageName;
398 }
399
400 /**
401 * Return the given word or sentence as <b>bold</b>.
402 *
403 * @param word
404 * the input
405 *
406 * @return the bold output
407 */
408 protected String enbold(String word) {
409 return word;
410 }
411
412 /**
413 * Return the given word or sentence as <i>italic</i>.
414 *
415 * @param word
416 * the input
417 *
418 * @return the italic output
419 */
420 protected String italize(String word) {
421 return word;
422 }
423
424 /**
425 * Decorate the given text with <b>bold</b> and <i>italic</i> words,
426 * according to {@link BasicOutput#enbold(String)} and
427 * {@link BasicOutput#italize(String)}.
428 *
429 * @param text
430 * the input
431 *
432 * @return the decorated output
433 */
434 protected String decorateText(String text) {
435 StringBuilder builder = new StringBuilder();
436
437 int bold = -1;
438 int italic = -1;
439 char prev = '\0';
440 for (char car : text.toCharArray()) {
441 switch (car) {
442 case '*':
443 if (bold >= 0 && prev != ' ') {
444 String data = builder.substring(bold);
445 builder.setLength(bold);
446 builder.append(enbold(data));
447 bold = -1;
448 } else if (bold < 0
449 && (prev == ' ' || prev == '\0' || prev == '\n')) {
450 bold = builder.length();
451 } else {
452 builder.append(car);
453 }
454
455 break;
456 case '_':
457 if (italic >= 0 && prev != ' ') {
458 String data = builder.substring(italic);
459 builder.setLength(italic);
460 builder.append(enbold(data));
461 italic = -1;
462 } else if (italic < 0
463 && (prev == ' ' || prev == '\0' || prev == '\n')) {
464 italic = builder.length();
465 } else {
466 builder.append(car);
467 }
468
469 break;
470 default:
471 builder.append(car);
472 break;
473 }
474
475 prev = car;
476 }
477
478 if (bold >= 0) {
479 builder.insert(bold, '*');
480 }
481
482 if (italic >= 0) {
483 builder.insert(italic, '_');
484 }
485
486 return builder.toString();
487 }
488
489 /**
490 * Return a {@link BasicOutput} object compatible with the given
491 * {@link OutputType}.
492 *
493 * @param type
494 * the type
495 * @param infoCover
496 * force the <tt>.info</tt> file and the cover to be saved next
497 * to the main target file
498 *
499 * @return the {@link BasicOutput}
500 */
501 public static BasicOutput getOutput(OutputType type, boolean infoCover) {
502 if (type != null) {
503 switch (type) {
504 case EPUB:
505 return new Epub().setType(type, infoCover, infoCover);
506 case TEXT:
507 return new Text().setType(type, true, infoCover);
508 case INFO_TEXT:
509 return new InfoText().setType(type, true, true);
510 case SYSOUT:
511 return new Sysout().setType(type, false, false);
512 case CBZ:
513 return new Cbz().setType(type, infoCover, infoCover);
514 case LATEX:
515 return new LaTeX().setType(type, infoCover, infoCover);
2206ef66 516 case HTML:
a6395bef 517 return new Html().setType(type, infoCover, infoCover);
08fe2e33
NR
518 }
519 }
520
521 return null;
522 }
523}