cached lib can now getStory()
[nikiroo-utils.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;
7445f856 22import be.nikiroo.utils.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
70 @Override
71 public String getSourceName() {
72 return "text";
73 }
74
75 @Override
7445f856
NR
76 protected Document loadDocument(URL source) throws IOException {
77 try {
78 sourceFile = new File(source.toURI());
79 in = new MarkableFileInputStream(new FileInputStream(sourceFile));
80 } catch (URISyntaxException e) {
81 throw new IOException("Cannot load the text document: " + source);
82 }
83
84 return null;
85 }
86
87 @Override
88 protected MetaData getMeta() throws IOException {
68686a37
NR
89 MetaData meta = new MetaData();
90
7445f856
NR
91 meta.setTitle(getTitle());
92 meta.setAuthor(getAuthor());
93 meta.setDate(getDate());
68686a37
NR
94 meta.setTags(new ArrayList<String>());
95 meta.setSource(getSourceName());
7445f856 96 meta.setUrl(getSourceFile().toURI().toURL().toString());
2206ef66 97 meta.setPublisher("");
7445f856 98 meta.setUuid(getSourceFile().toString());
68686a37 99 meta.setLuid("");
7445f856
NR
100 meta.setLang(getLang()); // default is EN
101 meta.setSubject(getSourceFile().getParentFile().getName());
68686a37
NR
102 meta.setType(getType().toString());
103 meta.setImageDocument(false);
7445f856 104 meta.setCover(getCover(getSourceFile()));
68686a37
NR
105
106 return meta;
08fe2e33
NR
107 }
108
7445f856 109 private String getLang() {
08fe2e33 110 @SuppressWarnings("resource")
7445f856 111 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
112 scan.useDelimiter("\\n");
113 scan.next(); // Title
114 scan.next(); // Author (Date)
115 String chapter0 = scan.next(); // empty or Chapter 0
116 while (chapter0.isEmpty()) {
117 chapter0 = scan.next();
118 }
119
22848428
NR
120 String lang = detectChapter(chapter0, 0);
121 if (lang == null) {
122 // No description??
123 lang = detectChapter(chapter0, 1);
124 }
125
08fe2e33 126 if (lang == null) {
276f95c6 127 lang = "en";
08fe2e33 128 } else {
276f95c6 129 lang = lang.toLowerCase();
08fe2e33
NR
130 }
131
132 return lang;
133 }
134
7445f856 135 private String getTitle() {
08fe2e33 136 @SuppressWarnings("resource")
7445f856 137 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
138 scan.useDelimiter("\\n");
139 return scan.next();
140 }
141
7445f856 142 private String getAuthor() {
08fe2e33 143 @SuppressWarnings("resource")
7445f856 144 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
145 scan.useDelimiter("\\n");
146 scan.next();
147 String authorDate = scan.next();
148
149 String author = authorDate;
150 int pos = authorDate.indexOf('(');
151 if (pos >= 0) {
152 author = authorDate.substring(0, pos);
153 }
154
0ffa4754 155 return BasicSupportHelper.fixAuthor(author);
08fe2e33
NR
156 }
157
7445f856 158 private String getDate() {
08fe2e33 159 @SuppressWarnings("resource")
7445f856 160 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
161 scan.useDelimiter("\\n");
162 scan.next();
163 String authorDate = scan.next();
164
165 String date = "";
166 int pos = authorDate.indexOf('(');
167 if (pos >= 0) {
168 date = authorDate.substring(pos + 1).trim();
169 pos = date.lastIndexOf(')');
170 if (pos >= 0) {
171 date = date.substring(0, pos).trim();
172 }
173 }
174
175 return date;
176 }
177
178 @Override
7445f856
NR
179 protected String getDesc() throws IOException {
180 return getChapterContent(null, 0, null);
08fe2e33
NR
181 }
182
7445f856
NR
183 private Image getCover(File sourceFile) {
184 String path = sourceFile.getName();
08fe2e33
NR
185
186 for (String ext : new String[] { ".txt", ".text", ".story" }) {
187 if (path.endsWith(ext)) {
188 path = path.substring(0, path.length() - ext.length());
189 }
190 }
191
81b5e730
NR
192 Image cover = BasicSupportImages.getImage(this,
193 sourceFile.getParentFile(), path);
194 if (cover != null) {
195 try {
196 File tmp = Instance.getTempFiles().createTempFile(
197 "test_cover_image");
198 ImageUtils.getInstance().saveAsImage(cover, tmp, "png");
199 tmp.delete();
200 } catch (IOException e) {
201 cover = null;
202 }
203 }
204
205 return cover;
08fe2e33
NR
206 }
207
208 @Override
7445f856
NR
209 protected List<Entry<String, URL>> getChapters(Progress pg)
210 throws IOException {
08fe2e33
NR
211 List<Entry<String, URL>> chaps = new ArrayList<Entry<String, URL>>();
212 @SuppressWarnings("resource")
7445f856 213 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33 214 scan.useDelimiter("\\n");
08fe2e33
NR
215 boolean prevLineEmpty = false;
216 while (scan.hasNext()) {
217 String line = scan.next();
22848428
NR
218 if (prevLineEmpty && detectChapter(line, chaps.size() + 1) != null) {
219 String chapName = Integer.toString(chaps.size() + 1);
220 int pos = line.indexOf(':');
221 if (pos >= 0 && pos + 1 < line.length()) {
222 chapName = line.substring(pos + 1).trim();
223 }
08fe2e33 224
7445f856
NR
225 chaps.add(new AbstractMap.SimpleEntry<String, URL>(//
226 chapName, //
227 getSourceFile().toURI().toURL()));
08fe2e33
NR
228 }
229
230 prevLineEmpty = line.trim().isEmpty();
231 }
232
233 return chaps;
234 }
235
236 @Override
7445f856
NR
237 protected String getChapterContent(URL source, int number, Progress pg)
238 throws IOException {
08fe2e33
NR
239 StringBuilder builder = new StringBuilder();
240 @SuppressWarnings("resource")
7445f856 241 Scanner scan = new Scanner(getInput(), "UTF-8");
08fe2e33
NR
242 scan.useDelimiter("\\n");
243 boolean inChap = false;
08fe2e33
NR
244 while (scan.hasNext()) {
245 String line = scan.next();
68686a37
NR
246 if (detectChapter(line, number) != null) {
247 inChap = true;
22848428 248 } else if (inChap && detectChapter(line, number + 1) != null) {
68686a37
NR
249 break;
250 } else if (inChap) {
251 builder.append(line);
252 builder.append("\n");
08fe2e33 253 }
08fe2e33
NR
254 }
255
256 return builder.toString();
257 }
258
7445f856
NR
259 @Override
260 protected void close() {
261 InputStream in = getInput();
262 if (in != null) {
263 try {
264 in.close();
265 } catch (IOException e) {
266 Instance.getTraceHandler().error(
267 new IOException(
268 "Cannot close the text source file input", e));
269 }
270 }
271
272 super.close();
273 }
274
08fe2e33
NR
275 @Override
276 protected boolean supports(URL url) {
86d49dbc
NR
277 return supports(url, false);
278 }
279
280 /**
281 * Check if we supports this {@link URL}, that is, if the info file can be
282 * found OR not found.
283 *
284 * @param url
285 * the {@link URL} to check
286 * @param info
287 * TRUE to require the info file, FALSE to forbid the info file
288 *
289 * @return TRUE if it is supported
290 */
291 protected boolean supports(URL url, boolean info) {
292 boolean infoPresent = false;
08fe2e33
NR
293 if ("file".equals(url.getProtocol())) {
294 File file;
295 try {
296 file = new File(url.toURI());
86d49dbc 297 file = assureNoTxt(file);
08fe2e33
NR
298 file = new File(file.getPath() + ".info");
299 } catch (URISyntaxException e) {
62c63b07 300 Instance.getTraceHandler().error(e);
08fe2e33
NR
301 file = null;
302 }
303
86d49dbc 304 infoPresent = (file != null && file.exists());
08fe2e33
NR
305 }
306
86d49dbc
NR
307 return infoPresent == info;
308 }
309
310 /**
311 * Remove the ".txt" extension if it is present.
312 *
313 * @param file
314 * the file to process
315 *
316 * @return the same file or a copy of it without the ".txt" extension if it
317 * was present
318 */
319 protected File assureNoTxt(File file) {
320 if (file.getName().endsWith(".txt")) {
321 file = new File(file.getPath().substring(0,
322 file.getPath().length() - 4));
323 }
324
325 return file;
08fe2e33
NR
326 }
327
08fe2e33
NR
328 /**
329 * Check if the given line looks like the given starting chapter in a
330 * supported language, and return the language if it does (or NULL if not).
331 *
332 * @param line
333 * the line to check
334 *
335 * @return the language or NULL
336 */
7445f856 337 static private String detectChapter(String line, int number) {
08fe2e33
NR
338 line = line.toUpperCase();
339 for (String lang : Instance.getConfig().getString(Config.CHAPTER)
340 .split(",")) {
341 String chapter = Instance.getConfig().getStringX(Config.CHAPTER,
342 lang);
343 if (chapter != null && !chapter.isEmpty()) {
344 chapter = chapter.toUpperCase() + " ";
345 if (line.startsWith(chapter)) {
22848428
NR
346 // We want "[CHAPTER] [number]: [name]", with ": [name]"
347 // optional
348 String test = line.substring(chapter.length()).trim();
349 if (test.startsWith(Integer.toString(number))) {
350 test = test
351 .substring(Integer.toString(number).length())
352 .trim();
353 if (test.isEmpty() || test.startsWith(":")) {
354 return lang;
08fe2e33 355 }
08fe2e33
NR
356 }
357 }
358 }
359 }
360
361 return null;
362 }
363}