change config bundle to better work with new nikiroo-utils
[fanfix.git] / src / be / nikiroo / fanfix / supported / Text.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix.supported;
2
3import java.io.File;
7445f856 4import java.io.FileInputStream;
08fe2e33
NR
5import java.io.IOException;
6import java.io.InputStream;
7import java.net.URISyntaxException;
8import java.net.URL;
7445f856 9import java.util.AbstractMap;
08fe2e33
NR
10import java.util.ArrayList;
11import java.util.List;
12import java.util.Map.Entry;
13import java.util.Scanner;
14
7445f856
NR
15import org.jsoup.nodes.Document;
16
08fe2e33
NR
17import be.nikiroo.fanfix.Instance;
18import be.nikiroo.fanfix.bundles.Config;
68686a37 19import be.nikiroo.fanfix.data.MetaData;
16a81ef7 20import be.nikiroo.utils.Image;
81b5e730 21import be.nikiroo.utils.ImageUtils;
67837328 22import be.nikiroo.utils.streams.MarkableFileInputStream;
ed08c171 23import be.nikiroo.utils.Progress;
08fe2e33
NR
24
25/**
26 * Support class for local stories encoded in textual format, with a few rules:
27 * <ul>
28 * <li>The title must be on the first line</li>
29 * <li>The author (preceded by nothing, "by " or "©") must be on the second
30 * line, possibly with the publication date in parenthesis (i.e., "
31 * <tt>By Unknown (3rd October 1998)</tt>")</li>
32 * <li>Chapters must be declared with "<tt>Chapter x</tt>" or "
33 * <tt>Chapter x: NAME OF THE CHAPTER</tt>", where "<tt>x</tt>" is the chapter
34 * number</li>
35 * <li>A description of the story must be given as chapter number 0</li>
36 * <li>A cover may be present, with the same filename but a PNG, JPEG or JPG
48f14dc9 37 * extension</li>
08fe2e33
NR
38 * </ul>
39 *
40 * @author niki
41 */
7445f856
NR
42class Text extends BasicSupport {
43 private File sourceFile;
44 private InputStream in;
45
46 protected File getSourceFile() {
47 return sourceFile;
48 }
49
50 protected InputStream getInput() {
51 if (in != null) {
52 try {
53 in.reset();
54 } catch (IOException e) {
55 Instance.getTraceHandler().error(
56 new IOException("Cannot reset the Text stream", e));
57 }
58
59 return in;
60 }
61
62 return null;
63 }
64
08fe2e33
NR
65 @Override
66 protected boolean isHtml() {
67 return false;
68 }
69
08fe2e33 70 @Override
7445f856
NR
71 protected Document loadDocument(URL source) throws IOException {
72 try {
73 sourceFile = new File(source.toURI());
67837328 74 in = new MarkableFileInputStream(sourceFile);
7445f856
NR
75 } catch (URISyntaxException e) {
76 throw new IOException("Cannot load the text document: " + source);
77 }
78
79 return null;
80 }
81
82 @Override
83 protected MetaData getMeta() throws IOException {
68686a37
NR
84 MetaData meta = new MetaData();
85
7445f856
NR
86 meta.setTitle(getTitle());
87 meta.setAuthor(getAuthor());
88 meta.setDate(getDate());
68686a37 89 meta.setTags(new ArrayList<String>());
727108fe 90 meta.setSource(getType().getSourceName());
7445f856 91 meta.setUrl(getSourceFile().toURI().toURL().toString());
2206ef66 92 meta.setPublisher("");
7445f856 93 meta.setUuid(getSourceFile().toString());
68686a37 94 meta.setLuid("");
7445f856
NR
95 meta.setLang(getLang()); // default is EN
96 meta.setSubject(getSourceFile().getParentFile().getName());
68686a37
NR
97 meta.setType(getType().toString());
98 meta.setImageDocument(false);
7445f856 99 meta.setCover(getCover(getSourceFile()));
68686a37
NR
100
101 return meta;
08fe2e33
NR
102 }
103
7445f856 104 private String getLang() {
08fe2e33 105 @SuppressWarnings("resource")
7445f856 106 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
107 scan.useDelimiter("\\n");
108 scan.next(); // Title
109 scan.next(); // Author (Date)
110 String chapter0 = scan.next(); // empty or Chapter 0
111 while (chapter0.isEmpty()) {
112 chapter0 = scan.next();
113 }
114
22848428
NR
115 String lang = detectChapter(chapter0, 0);
116 if (lang == null) {
117 // No description??
118 lang = detectChapter(chapter0, 1);
119 }
120
08fe2e33 121 if (lang == null) {
276f95c6 122 lang = "en";
08fe2e33 123 } else {
276f95c6 124 lang = lang.toLowerCase();
08fe2e33
NR
125 }
126
127 return lang;
128 }
129
7445f856 130 private String getTitle() {
08fe2e33 131 @SuppressWarnings("resource")
7445f856 132 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
133 scan.useDelimiter("\\n");
134 return scan.next();
135 }
136
7445f856 137 private String getAuthor() {
08fe2e33 138 @SuppressWarnings("resource")
7445f856 139 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
140 scan.useDelimiter("\\n");
141 scan.next();
142 String authorDate = scan.next();
143
144 String author = authorDate;
145 int pos = authorDate.indexOf('(');
146 if (pos >= 0) {
147 author = authorDate.substring(0, pos);
148 }
149
0ffa4754 150 return BasicSupportHelper.fixAuthor(author);
08fe2e33
NR
151 }
152
7445f856 153 private String getDate() {
08fe2e33 154 @SuppressWarnings("resource")
7445f856 155 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
156 scan.useDelimiter("\\n");
157 scan.next();
158 String authorDate = scan.next();
159
160 String date = "";
161 int pos = authorDate.indexOf('(');
162 if (pos >= 0) {
163 date = authorDate.substring(pos + 1).trim();
164 pos = date.lastIndexOf(')');
165 if (pos >= 0) {
166 date = date.substring(0, pos).trim();
167 }
168 }
169
170 return date;
171 }
172
173 @Override
7445f856
NR
174 protected String getDesc() throws IOException {
175 return getChapterContent(null, 0, null);
08fe2e33
NR
176 }
177
7445f856
NR
178 private Image getCover(File sourceFile) {
179 String path = sourceFile.getName();
08fe2e33
NR
180
181 for (String ext : new String[] { ".txt", ".text", ".story" }) {
182 if (path.endsWith(ext)) {
183 path = path.substring(0, path.length() - ext.length());
184 }
185 }
186
81b5e730
NR
187 Image cover = BasicSupportImages.getImage(this,
188 sourceFile.getParentFile(), path);
189 if (cover != null) {
190 try {
191 File tmp = Instance.getTempFiles().createTempFile(
192 "test_cover_image");
193 ImageUtils.getInstance().saveAsImage(cover, tmp, "png");
194 tmp.delete();
195 } catch (IOException e) {
196 cover = null;
197 }
198 }
199
200 return cover;
08fe2e33
NR
201 }
202
203 @Override
7445f856
NR
204 protected List<Entry<String, URL>> getChapters(Progress pg)
205 throws IOException {
08fe2e33
NR
206 List<Entry<String, URL>> chaps = new ArrayList<Entry<String, URL>>();
207 @SuppressWarnings("resource")
7445f856 208 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33 209 scan.useDelimiter("\\n");
08fe2e33
NR
210 boolean prevLineEmpty = false;
211 while (scan.hasNext()) {
212 String line = scan.next();
22848428
NR
213 if (prevLineEmpty && detectChapter(line, chaps.size() + 1) != null) {
214 String chapName = Integer.toString(chaps.size() + 1);
215 int pos = line.indexOf(':');
216 if (pos >= 0 && pos + 1 < line.length()) {
217 chapName = line.substring(pos + 1).trim();
218 }
08fe2e33 219
7445f856
NR
220 chaps.add(new AbstractMap.SimpleEntry<String, URL>(//
221 chapName, //
222 getSourceFile().toURI().toURL()));
08fe2e33
NR
223 }
224
225 prevLineEmpty = line.trim().isEmpty();
226 }
227
228 return chaps;
229 }
230
231 @Override
7445f856
NR
232 protected String getChapterContent(URL source, int number, Progress pg)
233 throws IOException {
08fe2e33
NR
234 StringBuilder builder = new StringBuilder();
235 @SuppressWarnings("resource")
7445f856 236 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
237 scan.useDelimiter("\\n");
238 boolean inChap = false;
08fe2e33
NR
239 while (scan.hasNext()) {
240 String line = scan.next();
68686a37
NR
241 if (detectChapter(line, number) != null) {
242 inChap = true;
22848428 243 } else if (inChap && detectChapter(line, number + 1) != null) {
68686a37
NR
244 break;
245 } else if (inChap) {
246 builder.append(line);
247 builder.append("\n");
08fe2e33 248 }
08fe2e33
NR
249 }
250
251 return builder.toString();
252 }
253
7445f856
NR
254 @Override
255 protected void close() {
256 InputStream in = getInput();
257 if (in != null) {
258 try {
259 in.close();
260 } catch (IOException e) {
261 Instance.getTraceHandler().error(
262 new IOException(
263 "Cannot close the text source file input", e));
264 }
265 }
266
267 super.close();
268 }
269
08fe2e33
NR
270 @Override
271 protected boolean supports(URL url) {
86d49dbc
NR
272 return supports(url, false);
273 }
274
275 /**
276 * Check if we supports this {@link URL}, that is, if the info file can be
277 * found OR not found.
278 *
279 * @param url
280 * the {@link URL} to check
281 * @param info
282 * TRUE to require the info file, FALSE to forbid the info file
283 *
284 * @return TRUE if it is supported
285 */
286 protected boolean supports(URL url, boolean info) {
287 boolean infoPresent = false;
08fe2e33
NR
288 if ("file".equals(url.getProtocol())) {
289 File file;
290 try {
291 file = new File(url.toURI());
86d49dbc 292 file = assureNoTxt(file);
08fe2e33
NR
293 file = new File(file.getPath() + ".info");
294 } catch (URISyntaxException e) {
62c63b07 295 Instance.getTraceHandler().error(e);
08fe2e33
NR
296 file = null;
297 }
298
86d49dbc 299 infoPresent = (file != null && file.exists());
08fe2e33
NR
300 }
301
86d49dbc
NR
302 return infoPresent == info;
303 }
304
305 /**
306 * Remove the ".txt" extension if it is present.
307 *
308 * @param file
309 * the file to process
310 *
311 * @return the same file or a copy of it without the ".txt" extension if it
312 * was present
313 */
314 protected File assureNoTxt(File file) {
315 if (file.getName().endsWith(".txt")) {
316 file = new File(file.getPath().substring(0,
317 file.getPath().length() - 4));
318 }
319
320 return file;
08fe2e33
NR
321 }
322
08fe2e33
NR
323 /**
324 * Check if the given line looks like the given starting chapter in a
325 * supported language, and return the language if it does (or NULL if not).
326 *
327 * @param line
328 * the line to check
329 *
330 * @return the language or NULL
331 */
7445f856 332 static private String detectChapter(String line, int number) {
08fe2e33 333 line = line.toUpperCase();
b7cd9db8 334 for (String lang : Instance.getConfig().getList(Config.CHAPTER)) {
08fe2e33
NR
335 String chapter = Instance.getConfig().getStringX(Config.CHAPTER,
336 lang);
337 if (chapter != null && !chapter.isEmpty()) {
338 chapter = chapter.toUpperCase() + " ";
339 if (line.startsWith(chapter)) {
22848428
NR
340 // We want "[CHAPTER] [number]: [name]", with ": [name]"
341 // optional
342 String test = line.substring(chapter.length()).trim();
343 if (test.startsWith(Integer.toString(number))) {
344 test = test
345 .substring(Integer.toString(number).length())
346 .trim();
347 if (test.isEmpty() || test.startsWith(":")) {
348 return lang;
08fe2e33 349 }
08fe2e33
NR
350 }
351 }
352 }
353 }
354
355 return null;
356 }
357}