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