oops, forgot a file
[fanfix.git] / src / be / nikiroo / fanfix / reader / BasicReader.java
CommitLineData
89cb07a6
NR
1package be.nikiroo.fanfix.reader;
2
3b2b638f 3import java.io.File;
89cb07a6 4import java.io.IOException;
3b2b638f 5import java.net.MalformedURLException;
89cb07a6 6import java.net.URL;
df6e2d88
NR
7import java.text.ParseException;
8import java.text.SimpleDateFormat;
9import java.util.AbstractMap.SimpleEntry;
10import java.util.ArrayList;
11import java.util.Date;
12import java.util.List;
13import java.util.Map.Entry;
89cb07a6
NR
14
15import be.nikiroo.fanfix.Instance;
d0114000 16import be.nikiroo.fanfix.bundles.Config;
c1873e56
NR
17import be.nikiroo.fanfix.bundles.UiConfig;
18import be.nikiroo.fanfix.data.MetaData;
89cb07a6 19import be.nikiroo.fanfix.data.Story;
e42573a0
NR
20import be.nikiroo.fanfix.library.BasicLibrary;
21import be.nikiroo.fanfix.library.LocalLibrary;
89cb07a6 22import be.nikiroo.fanfix.supported.BasicSupport;
3b2b638f 23import be.nikiroo.utils.Progress;
df6e2d88 24import be.nikiroo.utils.StringUtils;
9119671d 25import be.nikiroo.utils.serial.SerialUtils;
89cb07a6
NR
26
27/**
dd56a893 28 * The class that handles the different {@link Story} readers you can use.
89cb07a6 29 * <p>
dd56a893 30 * All the readers should be accessed via {@link BasicReader#getReader()}.
89cb07a6
NR
31 *
32 * @author niki
33 */
e42573a0 34public abstract class BasicReader implements Reader {
68e2c6d2 35 private static BasicLibrary defaultLibrary = Instance.getLibrary();
c1873e56 36 private static ReaderType defaultType = ReaderType.GUI;
b0e88ebd 37
68e2c6d2 38 private BasicLibrary lib;
bc2ea776 39 private MetaData meta;
89cb07a6 40 private Story story;
bc2ea776 41 private int chapter;
3727aae2 42
d0114000
NR
43 /**
44 * Take the default reader type configuration from the config file.
45 */
3727aae2 46 static {
d0114000
NR
47 String typeString = Instance.getConfig().getString(Config.READER_TYPE);
48 if (typeString != null && !typeString.isEmpty()) {
49 try {
50 ReaderType type = ReaderType.valueOf(typeString.toUpperCase());
51 defaultType = type;
52 } catch (IllegalArgumentException e) {
53 // Do nothing
54 }
55 }
3727aae2
NR
56 }
57
211f7ddb 58 @Override
bc2ea776
NR
59 public synchronized Story getStory(Progress pg) {
60 if (story == null) {
61 story = getLibrary().getStory(meta.getLuid(), pg);
62 }
63
89cb07a6
NR
64 return story;
65 }
66
211f7ddb 67 @Override
68e2c6d2 68 public BasicLibrary getLibrary() {
b0e88ebd
NR
69 if (lib == null) {
70 lib = defaultLibrary;
71 }
72
73 return lib;
74 }
75
211f7ddb 76 @Override
bc2ea776 77 public void setLibrary(BasicLibrary lib) {
b0e88ebd
NR
78 this.lib = lib;
79 }
80
211f7ddb 81 @Override
9fe3f177 82 public synchronized MetaData getMeta() {
bc2ea776
NR
83 return meta;
84 }
85
211f7ddb 86 @Override
bc2ea776
NR
87 public synchronized void setMeta(MetaData meta) throws IOException {
88 setMeta(meta == null ? null : meta.getLuid()); // must check the library
89 }
90
211f7ddb 91 @Override
bc2ea776
NR
92 public synchronized void setMeta(String luid) throws IOException {
93 story = null;
94 meta = getLibrary().getInfo(luid);
95
96 if (meta == null) {
92fb0719 97 throw new IOException("Cannot retrieve story from library: " + luid);
89cb07a6
NR
98 }
99 }
100
211f7ddb 101 @Override
350bc060 102 public synchronized void setMeta(URL url, Progress pg) throws IOException {
e0fb1417 103 BasicSupport support = BasicSupport.getSupport(url);
89cb07a6 104 if (support == null) {
e0fb1417 105 throw new IOException("URL not supported: " + url.toString());
89cb07a6
NR
106 }
107
0ffa4754 108 story = support.process(pg);
89cb07a6
NR
109 if (story == null) {
110 throw new IOException(
111 "Cannot retrieve story from external source: "
e0fb1417 112 + url.toString());
89cb07a6 113 }
bc2ea776
NR
114
115 meta = story.getMeta();
116 }
117
211f7ddb 118 @Override
bc2ea776
NR
119 public int getChapter() {
120 return chapter;
89cb07a6
NR
121 }
122
211f7ddb 123 @Override
bc2ea776
NR
124 public void setChapter(int chapter) {
125 this.chapter = chapter;
6322ab64
NR
126 }
127
3727aae2 128 /**
d0114000
NR
129 * Return a new {@link BasicReader} ready for use if one is configured.
130 * <p>
131 * Can return NULL if none are configured.
3727aae2 132 *
d0114000 133 * @return a {@link BasicReader}, or NULL if none configured
3727aae2 134 */
e42573a0 135 public static Reader getReader() {
333f0e7b
NR
136 try {
137 if (defaultType != null) {
e42573a0
NR
138 return (Reader) SerialUtils.createObject(defaultType
139 .getTypeName());
d0114000 140 }
9119671d 141 } catch (Exception e) {
16a81ef7
NR
142 Instance.getTraceHandler().error(
143 new Exception("Cannot create a reader of type: "
144 + defaultType + " (Not compiled in?)", e));
3727aae2
NR
145 }
146
147 return null;
148 }
149
150 /**
bc2ea776 151 * The default {@link Reader.ReaderType} used when calling
3727aae2
NR
152 * {@link BasicReader#getReader()}.
153 *
154 * @return the default type
155 */
156 public static ReaderType getDefaultReaderType() {
157 return defaultType;
158 }
159
160 /**
bc2ea776 161 * The default {@link Reader.ReaderType} used when calling
3727aae2
NR
162 * {@link BasicReader#getReader()}.
163 *
164 * @param defaultType
165 * the new default type
166 */
167 public static void setDefaultReaderType(ReaderType defaultType) {
168 BasicReader.defaultType = defaultType;
169 }
3b2b638f 170
b0e88ebd 171 /**
68e2c6d2
NR
172 * Change the default {@link LocalLibrary} to open with the
173 * {@link BasicReader}s.
b0e88ebd
NR
174 *
175 * @param lib
68e2c6d2 176 * the new {@link LocalLibrary}
b0e88ebd 177 */
68e2c6d2 178 public static void setDefaultLibrary(BasicLibrary lib) {
b0e88ebd
NR
179 BasicReader.defaultLibrary = lib;
180 }
181
3b2b638f
NR
182 /**
183 * Return an {@link URL} from this {@link String}, be it a file path or an
184 * actual {@link URL}.
185 *
186 * @param sourceString
187 * the source
188 *
189 * @return the corresponding {@link URL}
190 *
191 * @throws MalformedURLException
192 * if this is neither a file nor a conventional {@link URL}
193 */
194 public static URL getUrl(String sourceString) throws MalformedURLException {
195 if (sourceString == null || sourceString.isEmpty()) {
196 throw new MalformedURLException("Empty url");
197 }
198
199 URL source = null;
200 try {
201 source = new URL(sourceString);
202 } catch (MalformedURLException e) {
203 File sourceFile = new File(sourceString);
204 source = sourceFile.toURI().toURL();
205 }
206
207 return source;
208 }
c1873e56 209
df6e2d88
NR
210 /**
211 * Describe a {@link Story} from its {@link MetaData} and return a list of
212 * title/value that represent this {@link Story}.
213 *
214 * @param meta
215 * the {@link MetaData} to represent
216 *
217 * @return the information
218 */
219 public static List<Entry<String, String>> getMetaDesc(MetaData meta) {
220 List<Entry<String, String>> metaDesc = new ArrayList<Entry<String, String>>();
221
222 // TODO: i18n
223
224 StringBuilder tags = new StringBuilder();
225 for (String tag : meta.getTags()) {
226 if (tags.length() > 0) {
227 tags.append(", ");
228 }
229 tags.append(tag);
230 }
231
232 metaDesc.add(new SimpleEntry<String, String>("Author", meta.getAuthor()));
233 metaDesc.add(new SimpleEntry<String, String>("Publication date",
234 formatDate(meta.getDate())));
235 metaDesc.add(new SimpleEntry<String, String>("Published on", meta
236 .getPublisher()));
237 metaDesc.add(new SimpleEntry<String, String>("URL", meta.getUrl()));
238 metaDesc.add(new SimpleEntry<String, String>("Word count", format(meta
239 .getWords())));
240 metaDesc.add(new SimpleEntry<String, String>("Source", meta.getSource()));
241 metaDesc.add(new SimpleEntry<String, String>("Subject", meta
242 .getSubject()));
243 metaDesc.add(new SimpleEntry<String, String>("Language", meta.getLang()));
244 metaDesc.add(new SimpleEntry<String, String>("Tags", tags.toString()));
245
246 return metaDesc;
247 }
248
5dd985cf
NR
249 /**
250 * Open the {@link Story} with an external reader (the program will be
251 * passed the main file associated with this {@link Story}).
252 *
253 * @param lib
254 * the {@link BasicLibrary} to select the {@link Story} from
255 * @param luid
256 * the {@link Story} LUID
350bc060
NR
257 * @param sync
258 * execute the process synchronously (wait until it is terminated
259 * before returning)
5dd985cf
NR
260 *
261 * @throws IOException
262 * in case of I/O error
263 */
16a81ef7 264 @Override
350bc060
NR
265 public void openExternal(BasicLibrary lib, String luid, boolean sync)
266 throws IOException {
b0e88ebd 267 MetaData meta = lib.getInfo(luid);
ff05b828 268 File target = lib.getFile(luid, null);
c1873e56 269
350bc060 270 openExternal(meta, target, sync);
c1873e56
NR
271 }
272
5dd985cf
NR
273 /**
274 * Open the {@link Story} with an external reader (the program will be
275 * passed the given target file).
276 *
277 * @param meta
278 * the {@link Story} to load
279 * @param target
280 * the target {@link File}
350bc060
NR
281 * @param sync
282 * execute the process synchronously (wait until it is terminated
283 * before returning)
5dd985cf
NR
284 *
285 * @throws IOException
286 * in case of I/O error
287 */
350bc060
NR
288 protected void openExternal(MetaData meta, File target, boolean sync)
289 throws IOException {
c1873e56
NR
290 String program = null;
291 if (meta.isImageDocument()) {
292 program = Instance.getUiConfig().getString(
293 UiConfig.IMAGES_DOCUMENT_READER);
294 } else {
295 program = Instance.getUiConfig().getString(
296 UiConfig.NON_IMAGES_DOCUMENT_READER);
297 }
298
299 if (program != null && program.trim().isEmpty()) {
300 program = null;
301 }
302
350bc060 303 start(target, program, sync);
16a81ef7 304 }
c1873e56 305
16a81ef7
NR
306 /**
307 * Start a file and open it with the given program if given or the first
308 * default system starter we can find.
309 *
310 * @param target
311 * the target to open
312 * @param program
313 * the program to use or NULL for the default system starter
350bc060
NR
314 * @param sync
315 * execute the process synchronously (wait until it is terminated
316 * before returning)
16a81ef7
NR
317 *
318 * @throws IOException
319 * in case of I/O error
320 */
350bc060
NR
321 protected void start(File target, String program, boolean sync)
322 throws IOException {
323
324 Process proc = null;
16a81ef7
NR
325 if (program == null) {
326 boolean ok = false;
b4f9071c
NR
327 for (String starter : new String[] { "xdg-open", "open", "see",
328 "start", "run" }) {
16a81ef7 329 try {
9e2fad36
NR
330 Instance.getTraceHandler().trace(
331 "starting external program");
350bc060 332 proc = Runtime.getRuntime().exec(
16a81ef7
NR
333 new String[] { starter, target.getAbsolutePath() });
334 ok = true;
335 break;
336 } catch (IOException e) {
337 }
338 }
339 if (!ok) {
340 throw new IOException("Cannot find a program to start the file");
c1873e56
NR
341 }
342 } else {
9e2fad36 343 Instance.getTraceHandler().trace("starting external program");
350bc060 344 proc = Runtime.getRuntime().exec(
c1873e56
NR
345 new String[] { program, target.getAbsolutePath() });
346 }
350bc060
NR
347
348 if (proc != null && sync) {
349 while (proc.isAlive()) {
350 try {
351 Thread.sleep(100);
352 } catch (InterruptedException e) {
353 }
354 }
355 }
c1873e56 356 }
df6e2d88
NR
357
358 static private String format(long value) {
359 String display = "";
360
361 while (value > 0) {
362 if (!display.isEmpty()) {
363 display = "." + display;
364 }
365 display = (value % 1000) + display;
366 value = value / 1000;
367 }
368
369 return display;
370 }
371
372 static private String formatDate(String date) {
373 long ms = 0;
374
375 try {
376 ms = StringUtils.toTime(date);
377 } catch (ParseException e) {
378 }
379
380 if (ms <= 0) {
381 SimpleDateFormat sdf = new SimpleDateFormat(
382 "yyyy-MM-dd'T'HH:mm:ssXXX");
383 try {
384 ms = sdf.parse(date).getTime();
385 } catch (ParseException e) {
386 }
387 }
388
389 if (ms > 0) {
390 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
391 return sdf.format(new Date(ms));
392 }
393
394 // :(
395 return date;
396 }
89cb07a6 397}