Initial commit (working)
[fanfix.git] / src / be / nikiroo / fanfix / supported / Text.java
1 package be.nikiroo.fanfix.supported;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.net.URISyntaxException;
7 import java.net.URL;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.Map.Entry;
11 import java.util.Scanner;
12
13 import be.nikiroo.fanfix.Instance;
14 import be.nikiroo.fanfix.bundles.Config;
15
16 /**
17 * Support class for local stories encoded in textual format, with a few rules:
18 * <ul>
19 * <li>The title must be on the first line</li>
20 * <li>The author (preceded by nothing, "by " or "©") must be on the second
21 * line, possibly with the publication date in parenthesis (i.e., "
22 * <tt>By Unknown (3rd October 1998)</tt>")</li>
23 * <li>Chapters must be declared with "<tt>Chapter x</tt>" or "
24 * <tt>Chapter x: NAME OF THE CHAPTER</tt>", where "<tt>x</tt>" is the chapter
25 * number</li>
26 * <li>A description of the story must be given as chapter number 0</li>
27 * <li>A cover may be present, with the same filename but a PNG, JPEG or JPG
28 * extension</li<
29 * </ul>
30 *
31 * @author niki
32 */
33 class Text extends BasicSupport {
34 @Override
35 protected boolean isHtml() {
36 return false;
37 }
38
39 @Override
40 public String getSourceName() {
41 return "text";
42 }
43
44 @Override
45 protected String getPublisher(URL source, InputStream in)
46 throws IOException {
47 return "";
48 }
49
50 @Override
51 protected String getSubject(URL source, InputStream in) throws IOException {
52 try {
53 File file = new File(source.toURI());
54 return file.getParentFile().getName();
55 } catch (URISyntaxException e) {
56 throw new IOException("Cannot parse the URL to File: "
57 + source.toString(), e);
58 }
59
60 }
61
62 @Override
63 protected String getLang(URL source, InputStream in) throws IOException {
64 @SuppressWarnings("resource")
65 Scanner scan = new Scanner(in, "UTF-8");
66 scan.useDelimiter("\\n");
67 scan.next(); // Title
68 scan.next(); // Author (Date)
69 String chapter0 = scan.next(); // empty or Chapter 0
70 while (chapter0.isEmpty()) {
71 chapter0 = scan.next();
72 }
73
74 String lang = detectChapter(chapter0);
75 if (lang == null) {
76 lang = super.getLang(source, in);
77 } else {
78 lang = lang.toUpperCase();
79 }
80
81 return lang;
82 }
83
84 @Override
85 protected String getTitle(URL source, InputStream in) throws IOException {
86 @SuppressWarnings("resource")
87 Scanner scan = new Scanner(in, "UTF-8");
88 scan.useDelimiter("\\n");
89 return scan.next();
90 }
91
92 @Override
93 protected String getAuthor(URL source, InputStream in) throws IOException {
94 @SuppressWarnings("resource")
95 Scanner scan = new Scanner(in, "UTF-8");
96 scan.useDelimiter("\\n");
97 scan.next();
98 String authorDate = scan.next();
99
100 String author = authorDate;
101 int pos = authorDate.indexOf('(');
102 if (pos >= 0) {
103 author = authorDate.substring(0, pos);
104 }
105
106 return author;
107 }
108
109 @Override
110 protected String getDate(URL source, InputStream in) throws IOException {
111 @SuppressWarnings("resource")
112 Scanner scan = new Scanner(in, "UTF-8");
113 scan.useDelimiter("\\n");
114 scan.next();
115 String authorDate = scan.next();
116
117 String date = "";
118 int pos = authorDate.indexOf('(');
119 if (pos >= 0) {
120 date = authorDate.substring(pos + 1).trim();
121 pos = date.lastIndexOf(')');
122 if (pos >= 0) {
123 date = date.substring(0, pos).trim();
124 }
125 }
126
127 return date;
128 }
129
130 @Override
131 protected String getDesc(URL source, InputStream in) {
132 return getChapterContent(source, in, 0);
133 }
134
135 @Override
136 protected URL getCover(URL source, InputStream in) {
137 String path;
138 try {
139 path = new File(source.toURI()).getPath();
140 } catch (URISyntaxException e) {
141 Instance.syserr(e);
142 path = null;
143 }
144
145 for (String ext : new String[] { ".txt", ".text", ".story" }) {
146 if (path.endsWith(ext)) {
147 path = path.substring(0, path.length() - ext.length());
148 }
149 }
150
151 return getImage(source, path);
152 }
153
154 @Override
155 protected List<Entry<String, URL>> getChapters(URL source, InputStream in) {
156 List<Entry<String, URL>> chaps = new ArrayList<Entry<String, URL>>();
157 @SuppressWarnings("resource")
158 Scanner scan = new Scanner(in, "UTF-8");
159 scan.useDelimiter("\\n");
160 boolean descSkipped = false;
161 boolean prevLineEmpty = false;
162 while (scan.hasNext()) {
163 String line = scan.next();
164 if (prevLineEmpty && detectChapter(line) != null) {
165 if (descSkipped) {
166 String chapName = Integer.toString(chaps.size());
167 int pos = line.indexOf(':');
168 if (pos >= 0 && pos + 1 < line.length()) {
169 chapName = line.substring(pos + 1).trim();
170 }
171 final URL value = source;
172 final String key = chapName;
173 chaps.add(new Entry<String, URL>() {
174 public URL setValue(URL value) {
175 return null;
176 }
177
178 public URL getValue() {
179 return value;
180 }
181
182 public String getKey() {
183 return key;
184 }
185 });
186 } else {
187 descSkipped = true;
188 }
189 }
190
191 prevLineEmpty = line.trim().isEmpty();
192 }
193
194 return chaps;
195 }
196
197 @Override
198 protected String getChapterContent(URL source, InputStream in, int number) {
199 StringBuilder builder = new StringBuilder();
200 @SuppressWarnings("resource")
201 Scanner scan = new Scanner(in, "UTF-8");
202 scan.useDelimiter("\\n");
203 boolean inChap = false;
204 boolean prevLineEmpty = false;
205 while (scan.hasNext()) {
206 String line = scan.next();
207 if (prevLineEmpty) {
208 if (detectChapter(line, number) != null) {
209 inChap = true;
210 } else if (inChap) {
211 if (prevLineEmpty && detectChapter(line) != null) {
212 break;
213 }
214
215 builder.append(line);
216 builder.append("\n");
217 }
218 }
219
220 prevLineEmpty = line.trim().isEmpty();
221 }
222
223 return builder.toString();
224 }
225
226 @Override
227 protected boolean supports(URL url) {
228 if ("file".equals(url.getProtocol())) {
229 File file;
230 try {
231 file = new File(url.toURI());
232 file = new File(file.getPath() + ".info");
233 } catch (URISyntaxException e) {
234 Instance.syserr(e);
235 file = null;
236 }
237
238 return file == null || !file.exists();
239 }
240
241 return false;
242 }
243
244 /**
245 * Check if the given line looks like a starting chapter in a supported
246 * language, and return the language if it does (or NULL if not).
247 *
248 * @param line
249 * the line to check
250 *
251 * @return the language or NULL
252 */
253 private String detectChapter(String line) {
254 return detectChapter(line, null);
255 }
256
257 /**
258 * Check if the given line looks like the given starting chapter in a
259 * supported language, and return the language if it does (or NULL if not).
260 *
261 * @param line
262 * the line to check
263 *
264 * @return the language or NULL
265 */
266 private String detectChapter(String line, Integer number) {
267 line = line.toUpperCase();
268 for (String lang : Instance.getConfig().getString(Config.CHAPTER)
269 .split(",")) {
270 String chapter = Instance.getConfig().getStringX(Config.CHAPTER,
271 lang);
272 if (chapter != null && !chapter.isEmpty()) {
273 chapter = chapter.toUpperCase() + " ";
274 if (line.startsWith(chapter)) {
275 if (number != null) {
276 // We want "[CHAPTER] [number]: [name]", with ": [name]"
277 // optional
278 String test = line.substring(chapter.length()).trim();
279 if (test.startsWith(Integer.toString(number))) {
280 test = test.substring(
281 Integer.toString(number).length()).trim();
282 if (test.isEmpty() || test.startsWith(":")) {
283 return lang;
284 }
285 }
286 } else {
287 return lang;
288 }
289 }
290 }
291 }
292
293 return null;
294 }
295 }