1 package be
.nikiroo
.fanfix
.reader
;
4 import java
.io
.IOException
;
5 import java
.net
.MalformedURLException
;
7 import java
.text
.ParseException
;
8 import java
.text
.SimpleDateFormat
;
11 import java
.util
.TreeMap
;
13 import be
.nikiroo
.fanfix
.Instance
;
14 import be
.nikiroo
.fanfix
.bundles
.Config
;
15 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
16 import be
.nikiroo
.fanfix
.data
.MetaData
;
17 import be
.nikiroo
.fanfix
.data
.Story
;
18 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
19 import be
.nikiroo
.fanfix
.library
.LocalLibrary
;
20 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
21 import be
.nikiroo
.utils
.Progress
;
22 import be
.nikiroo
.utils
.StringUtils
;
23 import be
.nikiroo
.utils
.serial
.SerialUtils
;
26 * The class that handles the different {@link Story} readers you can use.
28 * All the readers should be accessed via {@link BasicReader#getReader()}.
32 public abstract class BasicReader
implements Reader
{
33 private static BasicLibrary defaultLibrary
= Instance
.getInstance().getLibrary();
34 private static ReaderType defaultType
= ReaderType
.GUI
;
36 private BasicLibrary lib
;
37 private MetaData meta
;
42 * Take the default reader type configuration from the config file.
45 String typeString
= Instance
.getInstance().getConfig().getString(Config
.READER_TYPE
);
46 if (typeString
!= null && !typeString
.isEmpty()) {
48 ReaderType type
= ReaderType
.valueOf(typeString
.toUpperCase());
50 } catch (IllegalArgumentException e
) {
57 public synchronized Story
getStory(Progress pg
) throws IOException
{
59 story
= getLibrary().getStory(meta
.getLuid(), pg
);
66 public BasicLibrary
getLibrary() {
75 public void setLibrary(BasicLibrary lib
) {
80 public synchronized MetaData
getMeta() {
85 public synchronized void setMeta(MetaData meta
) throws IOException
{
86 setMeta(meta
== null ?
null : meta
.getLuid()); // must check the library
90 public synchronized void setMeta(String luid
) throws IOException
{
92 meta
= getLibrary().getInfo(luid
);
95 throw new IOException("Cannot retrieve story from library: " + luid
);
100 public synchronized void setMeta(URL url
, Progress pg
) throws IOException
{
101 BasicSupport support
= BasicSupport
.getSupport(url
);
102 if (support
== null) {
103 throw new IOException("URL not supported: " + url
.toString());
106 story
= support
.process(pg
);
108 throw new IOException(
109 "Cannot retrieve story from external source: "
113 meta
= story
.getMeta();
117 public int getChapter() {
122 public void setChapter(int chapter
) {
123 this.chapter
= chapter
;
127 * Return a new {@link BasicReader} ready for use if one is configured.
129 * Can return NULL if none are configured.
131 * @return a {@link BasicReader}, or NULL if none configured
133 public static Reader
getReader() {
135 if (defaultType
!= null) {
136 return (Reader
) SerialUtils
.createObject(defaultType
139 } catch (Exception e
) {
140 Instance
.getInstance().getTraceHandler()
141 .error(new Exception("Cannot create a reader of type: " + defaultType
+ " (Not compiled in?)", e
));
148 * The default {@link Reader.ReaderType} used when calling
149 * {@link BasicReader#getReader()}.
151 * @return the default type
153 public static ReaderType
getDefaultReaderType() {
158 * The default {@link Reader.ReaderType} used when calling
159 * {@link BasicReader#getReader()}.
162 * the new default type
164 public static void setDefaultReaderType(ReaderType defaultType
) {
165 BasicReader
.defaultType
= defaultType
;
169 * Change the default {@link LocalLibrary} to open with the
170 * {@link BasicReader}s.
173 * the new {@link LocalLibrary}
175 public static void setDefaultLibrary(BasicLibrary lib
) {
176 BasicReader
.defaultLibrary
= lib
;
180 * Return an {@link URL} from this {@link String}, be it a file path or an
181 * actual {@link URL}.
183 * @param sourceString
186 * @return the corresponding {@link URL}
188 * @throws MalformedURLException
189 * if this is neither a file nor a conventional {@link URL}
191 public static URL
getUrl(String sourceString
) throws MalformedURLException
{
192 if (sourceString
== null || sourceString
.isEmpty()) {
193 throw new MalformedURLException("Empty url");
198 source
= new URL(sourceString
);
199 } catch (MalformedURLException e
) {
200 File sourceFile
= new File(sourceString
);
201 source
= sourceFile
.toURI().toURL();
208 * Describe a {@link Story} from its {@link MetaData} and return a list of
209 * title/value that represent this {@link Story}.
212 * the {@link MetaData} to represent
214 * @return the information
216 public static Map
<String
, String
> getMetaDesc(MetaData meta
) {
217 Map
<String
, String
> metaDesc
= new TreeMap
<String
, String
>();
221 StringBuilder tags
= new StringBuilder();
222 for (String tag
: meta
.getTags()) {
223 if (tags
.length() > 0) {
230 metaDesc
.put("Author", meta
.getAuthor());
231 metaDesc
.put("Publication date", formatDate(meta
.getDate()));
232 metaDesc
.put("Published on", meta
.getPublisher());
233 metaDesc
.put("URL", meta
.getUrl());
235 if (meta
.getWords() > 0) {
236 count
= StringUtils
.formatNumber(meta
.getWords());
238 if (meta
.isImageDocument()) {
239 metaDesc
.put("Number of images", count
);
241 metaDesc
.put("Number of words", count
);
243 metaDesc
.put("Source", meta
.getSource());
244 metaDesc
.put("Subject", meta
.getSubject());
245 metaDesc
.put("Language", meta
.getLang());
246 metaDesc
.put("Tags", tags
.toString());
252 * Open the {@link Story} with an external reader (the program will be
253 * passed the main file associated with this {@link Story}).
256 * the {@link BasicLibrary} to select the {@link Story} from
258 * the {@link Story} LUID
260 * execute the process synchronously (wait until it is terminated
263 * @throws IOException
264 * in case of I/O error
267 public void openExternal(BasicLibrary lib
, String luid
, boolean sync
)
269 MetaData meta
= lib
.getInfo(luid
);
270 File target
= lib
.getFile(luid
, null);
272 openExternal(meta
, target
, sync
);
276 * Open the {@link Story} with an external reader (the program will be
277 * passed the given target file).
280 * the {@link Story} to load
282 * the target {@link File}
284 * execute the process synchronously (wait until it is terminated
287 * @throws IOException
288 * in case of I/O error
290 protected void openExternal(MetaData meta
, File target
, boolean sync
)
292 String program
= null;
293 if (meta
.isImageDocument()) {
294 program
= Instance
.getInstance().getUiConfig().getString(UiConfig
.IMAGES_DOCUMENT_READER
);
296 program
= Instance
.getInstance().getUiConfig().getString(UiConfig
.NON_IMAGES_DOCUMENT_READER
);
299 if (program
!= null && program
.trim().isEmpty()) {
303 start(target
, program
, sync
);
307 * Start a file and open it with the given program if given or the first
308 * default system starter we can find.
313 * the program to use or NULL for the default system starter
315 * execute the process synchronously (wait until it is terminated
318 * @throws IOException
319 * in case of I/O error
321 protected void start(File target
, String program
, boolean sync
)
325 if (program
== null) {
327 for (String starter
: new String
[] { "xdg-open", "open", "see",
330 Instance
.getInstance().getTraceHandler().trace("starting external program");
331 proc
= Runtime
.getRuntime().exec(new String
[] { starter
, target
.getAbsolutePath() });
334 } catch (IOException e
) {
338 throw new IOException("Cannot find a program to start the file");
341 Instance
.getInstance().getTraceHandler().trace("starting external program");
342 proc
= Runtime
.getRuntime().exec(
343 new String
[] { program
, target
.getAbsolutePath() });
346 if (proc
!= null && sync
) {
349 } catch (InterruptedException e
) {
354 static private String
formatDate(String date
) {
357 if (date
!= null && !date
.isEmpty()) {
359 ms
= StringUtils
.toTime(date
);
360 } catch (ParseException e
) {
364 SimpleDateFormat sdf
= new SimpleDateFormat(
365 "yyyy-MM-dd'T'HH:mm:ssSSS");
367 ms
= sdf
.parse(date
).getTime();
368 } catch (ParseException e
) {
373 SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
374 return sdf
.format(new Date(ms
));