reader: sync/async work
[fanfix.git] / src / be / nikiroo / fanfix / reader / BasicReader.java
1 package be.nikiroo.fanfix.reader;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.MalformedURLException;
6 import java.net.URL;
7
8 import be.nikiroo.fanfix.Instance;
9 import be.nikiroo.fanfix.bundles.Config;
10 import be.nikiroo.fanfix.bundles.UiConfig;
11 import be.nikiroo.fanfix.data.MetaData;
12 import be.nikiroo.fanfix.data.Story;
13 import be.nikiroo.fanfix.library.BasicLibrary;
14 import be.nikiroo.fanfix.library.LocalLibrary;
15 import be.nikiroo.fanfix.supported.BasicSupport;
16 import be.nikiroo.utils.Progress;
17 import be.nikiroo.utils.serial.SerialUtils;
18
19 /**
20 * The class that handles the different {@link Story} readers you can use.
21 * <p>
22 * All the readers should be accessed via {@link BasicReader#getReader()}.
23 *
24 * @author niki
25 */
26 public abstract class BasicReader implements Reader {
27 private static BasicLibrary defaultLibrary = Instance.getLibrary();
28 private static ReaderType defaultType = ReaderType.GUI;
29
30 private BasicLibrary lib;
31 private MetaData meta;
32 private Story story;
33 private int chapter;
34
35 /**
36 * Take the default reader type configuration from the config file.
37 */
38 static {
39 String typeString = Instance.getConfig().getString(Config.READER_TYPE);
40 if (typeString != null && !typeString.isEmpty()) {
41 try {
42 ReaderType type = ReaderType.valueOf(typeString.toUpperCase());
43 defaultType = type;
44 } catch (IllegalArgumentException e) {
45 // Do nothing
46 }
47 }
48 }
49
50 @Override
51 public synchronized Story getStory(Progress pg) {
52 if (story == null) {
53 story = getLibrary().getStory(meta.getLuid(), pg);
54 }
55
56 return story;
57 }
58
59 @Override
60 public BasicLibrary getLibrary() {
61 if (lib == null) {
62 lib = defaultLibrary;
63 }
64
65 return lib;
66 }
67
68 @Override
69 public void setLibrary(BasicLibrary lib) {
70 this.lib = lib;
71 }
72
73 @Override
74 public synchronized MetaData getMeta() {
75 return meta;
76 }
77
78 @Override
79 public synchronized void setMeta(MetaData meta) throws IOException {
80 setMeta(meta == null ? null : meta.getLuid()); // must check the library
81 }
82
83 @Override
84 public synchronized void setMeta(String luid) throws IOException {
85 story = null;
86 meta = getLibrary().getInfo(luid);
87
88 if (meta == null) {
89 throw new IOException("Cannot retrieve story from library: " + luid);
90 }
91 }
92
93 @Override
94 public synchronized void setMeta(URL url, Progress pg) throws IOException {
95 BasicSupport support = BasicSupport.getSupport(url);
96 if (support == null) {
97 throw new IOException("URL not supported: " + url.toString());
98 }
99
100 story = support.process(pg);
101 if (story == null) {
102 throw new IOException(
103 "Cannot retrieve story from external source: "
104 + url.toString());
105 }
106
107 meta = story.getMeta();
108 }
109
110 @Override
111 public int getChapter() {
112 return chapter;
113 }
114
115 @Override
116 public void setChapter(int chapter) {
117 this.chapter = chapter;
118 }
119
120 /**
121 * Return a new {@link BasicReader} ready for use if one is configured.
122 * <p>
123 * Can return NULL if none are configured.
124 *
125 * @return a {@link BasicReader}, or NULL if none configured
126 */
127 public static Reader getReader() {
128 try {
129 if (defaultType != null) {
130 return (Reader) SerialUtils.createObject(defaultType
131 .getTypeName());
132 }
133 } catch (Exception e) {
134 Instance.getTraceHandler().error(
135 new Exception("Cannot create a reader of type: "
136 + defaultType + " (Not compiled in?)", e));
137 }
138
139 return null;
140 }
141
142 /**
143 * The default {@link Reader.ReaderType} used when calling
144 * {@link BasicReader#getReader()}.
145 *
146 * @return the default type
147 */
148 public static ReaderType getDefaultReaderType() {
149 return defaultType;
150 }
151
152 /**
153 * The default {@link Reader.ReaderType} used when calling
154 * {@link BasicReader#getReader()}.
155 *
156 * @param defaultType
157 * the new default type
158 */
159 public static void setDefaultReaderType(ReaderType defaultType) {
160 BasicReader.defaultType = defaultType;
161 }
162
163 /**
164 * Change the default {@link LocalLibrary} to open with the
165 * {@link BasicReader}s.
166 *
167 * @param lib
168 * the new {@link LocalLibrary}
169 */
170 public static void setDefaultLibrary(BasicLibrary lib) {
171 BasicReader.defaultLibrary = lib;
172 }
173
174 /**
175 * Return an {@link URL} from this {@link String}, be it a file path or an
176 * actual {@link URL}.
177 *
178 * @param sourceString
179 * the source
180 *
181 * @return the corresponding {@link URL}
182 *
183 * @throws MalformedURLException
184 * if this is neither a file nor a conventional {@link URL}
185 */
186 public static URL getUrl(String sourceString) throws MalformedURLException {
187 if (sourceString == null || sourceString.isEmpty()) {
188 throw new MalformedURLException("Empty url");
189 }
190
191 URL source = null;
192 try {
193 source = new URL(sourceString);
194 } catch (MalformedURLException e) {
195 File sourceFile = new File(sourceString);
196 source = sourceFile.toURI().toURL();
197 }
198
199 return source;
200 }
201
202 /**
203 * Open the {@link Story} with an external reader (the program will be
204 * passed the main file associated with this {@link Story}).
205 *
206 * @param lib
207 * the {@link BasicLibrary} to select the {@link Story} from
208 * @param luid
209 * the {@link Story} LUID
210 * @param sync
211 * execute the process synchronously (wait until it is terminated
212 * before returning)
213 *
214 * @throws IOException
215 * in case of I/O error
216 */
217 @Override
218 public void openExternal(BasicLibrary lib, String luid, boolean sync)
219 throws IOException {
220 MetaData meta = lib.getInfo(luid);
221 File target = lib.getFile(luid, null);
222
223 openExternal(meta, target, sync);
224 }
225
226 /**
227 * Open the {@link Story} with an external reader (the program will be
228 * passed the given target file).
229 *
230 * @param meta
231 * the {@link Story} to load
232 * @param target
233 * the target {@link File}
234 * @param sync
235 * execute the process synchronously (wait until it is terminated
236 * before returning)
237 *
238 * @throws IOException
239 * in case of I/O error
240 */
241 protected void openExternal(MetaData meta, File target, boolean sync)
242 throws IOException {
243 String program = null;
244 if (meta.isImageDocument()) {
245 program = Instance.getUiConfig().getString(
246 UiConfig.IMAGES_DOCUMENT_READER);
247 } else {
248 program = Instance.getUiConfig().getString(
249 UiConfig.NON_IMAGES_DOCUMENT_READER);
250 }
251
252 if (program != null && program.trim().isEmpty()) {
253 program = null;
254 }
255
256 start(target, program, sync);
257 }
258
259 /**
260 * Start a file and open it with the given program if given or the first
261 * default system starter we can find.
262 *
263 * @param target
264 * the target to open
265 * @param program
266 * the program to use or NULL for the default system starter
267 * @param sync
268 * execute the process synchronously (wait until it is terminated
269 * before returning)
270 *
271 * @throws IOException
272 * in case of I/O error
273 */
274 protected void start(File target, String program, boolean sync)
275 throws IOException {
276
277 Process proc = null;
278 if (program == null) {
279 boolean ok = false;
280 for (String starter : new String[] { "xdg-open", "open", "see",
281 "start", "run" }) {
282 try {
283 Instance.getTraceHandler().trace(
284 "starting external program");
285 proc = Runtime.getRuntime().exec(
286 new String[] { starter, target.getAbsolutePath() });
287 ok = true;
288 break;
289 } catch (IOException e) {
290 }
291 }
292 if (!ok) {
293 throw new IOException("Cannot find a program to start the file");
294 }
295 } else {
296 Instance.getTraceHandler().trace("starting external program");
297 proc = Runtime.getRuntime().exec(
298 new String[] { program, target.getAbsolutePath() });
299 }
300
301 if (proc != null && sync) {
302 while (proc.isAlive()) {
303 try {
304 Thread.sleep(100);
305 } catch (InterruptedException e) {
306 }
307 }
308 }
309 }
310 }