1 package be
.nikiroo
.fanfix
;
3 import java
.awt
.image
.BufferedImage
;
5 import java
.io
.IOException
;
7 import java
.util
.ArrayList
;
8 import java
.util
.Collections
;
11 import be
.nikiroo
.fanfix
.data
.MetaData
;
12 import be
.nikiroo
.fanfix
.data
.Story
;
13 import be
.nikiroo
.fanfix
.output
.BasicOutput
;
14 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
15 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
16 import be
.nikiroo
.fanfix
.supported
.BasicSupport
.SupportType
;
17 import be
.nikiroo
.utils
.Progress
;
20 * Manage a library of Stories: import, export, list, modify.
22 * Each {@link Story} object will be associated with a (local to the library)
23 * unique ID, the LUID, which will be used to identify the {@link Story}.
25 * Most of the {@link BasicLibrary} functions work on a partial (cover
26 * <b>MAY</b> not be included) {@link MetaData} object.
30 abstract public class BasicLibrary
{
32 * Retrieve the main {@link File} corresponding to the given {@link Story},
33 * which can be passed to an external reader or instance.
35 * Do <b>NOT</b> alter this file.
38 * the Library UID of the story
40 * @return the corresponding {@link Story}
42 public abstract File
getFile(String luid
);
45 * Return the cover image associated to this story.
48 * the Library UID of the story
50 * @return the cover image
52 public abstract BufferedImage
getCover(String luid
);
55 * Return the list of stories (represented by their {@link MetaData}, which
56 * <b>MAY</b> not have the cover included).
59 * the optional {@link Progress}
61 * @return the list (can be empty but not NULL)
63 protected abstract List
<MetaData
> getMetas(Progress pg
);
66 * Invalidate the {@link Story} cache (when the content should be re-read
67 * because it was changed).
69 protected abstract void clearCache();
72 * Return the next LUID that can be used.
74 * @return the next luid
76 protected abstract int getNextId();
79 * Delete the target {@link Story}.
82 * the LUID of the {@link Story}
85 * in case of I/O error or if the {@link Story} wa not found
87 protected abstract void doDelete(String luid
) throws IOException
;
90 * Actually save the story to the back-end.
93 * the {@link Story} to save
95 * the optional {@link Progress}
97 * @return the saved {@link Story} (which may have changed, especially
98 * regarding the {@link MetaData})
100 * @throws IOException
101 * in case of I/O error
103 protected abstract Story
doSave(Story story
, Progress pg
)
107 * Refresh the {@link BasicLibrary}, that is, make sure all stories are
111 * force the full content of the stories to be loaded, not just
112 * the {@link MetaData}
115 * the optional progress reporter
117 public void refresh(boolean full
, Progress pg
) {
120 List
<MetaData
> metas
= getMetas(pg
);
121 for (MetaData meta
: metas
) {
122 getStory(meta
.getLuid(), null);
130 * List all the known types (sources) of stories.
132 * @return the sources
134 public synchronized List
<String
> getSources() {
135 List
<String
> list
= new ArrayList
<String
>();
136 for (MetaData meta
: getMetas(null)) {
137 String storySource
= meta
.getSource();
138 if (!list
.contains(storySource
)) {
139 list
.add(storySource
);
143 Collections
.sort(list
);
148 * List all the known authors of stories.
150 * @return the authors
152 public synchronized List
<String
> getAuthors() {
153 List
<String
> list
= new ArrayList
<String
>();
154 for (MetaData meta
: getMetas(null)) {
155 String storyAuthor
= meta
.getAuthor();
156 if (!list
.contains(storyAuthor
)) {
157 list
.add(storyAuthor
);
161 Collections
.sort(list
);
166 * List all the stories in the {@link BasicLibrary}.
168 * Cover images not included.
170 * @return the stories
172 public synchronized List
<MetaData
> getList() {
173 return getMetas(null);
177 * List all the stories of the given source type in the {@link BasicLibrary}
178 * , or all the stories if NULL is passed as a type.
180 * Cover images not included.
183 * the type of story to retrieve, or NULL for all
185 * @return the stories
187 public synchronized List
<MetaData
> getListBySource(String type
) {
188 List
<MetaData
> list
= new ArrayList
<MetaData
>();
189 for (MetaData meta
: getMetas(null)) {
190 String storyType
= meta
.getSource();
191 if (type
== null || type
.equalsIgnoreCase(storyType
)) {
196 Collections
.sort(list
);
201 * List all the stories of the given author in the {@link BasicLibrary}, or
202 * all the stories if NULL is passed as an author.
204 * Cover images not included.
207 * the author of the stories to retrieve, or NULL for all
209 * @return the stories
211 public synchronized List
<MetaData
> getListByAuthor(String author
) {
212 List
<MetaData
> list
= new ArrayList
<MetaData
>();
213 for (MetaData meta
: getMetas(null)) {
214 String storyAuthor
= meta
.getAuthor();
215 if (author
== null || author
.equalsIgnoreCase(storyAuthor
)) {
220 Collections
.sort(list
);
225 * Retrieve a {@link MetaData} corresponding to the given {@link Story},
226 * cover image <b>MAY</b> not be included.
229 * the Library UID of the story
231 * @return the corresponding {@link Story}
233 public synchronized MetaData
getInfo(String luid
) {
235 for (MetaData meta
: getMetas(null)) {
236 if (luid
.equals(meta
.getLuid())) {
246 * Retrieve a specific {@link Story}.
249 * the Library UID of the story
251 * the optional progress reporter
253 * @return the corresponding {@link Story} or NULL if not found
255 public synchronized Story
getStory(String luid
, Progress pg
) {
262 for (MetaData meta
: getMetas(null)) {
263 if (meta
.getLuid().equals(luid
)) {
264 File file
= getFile(luid
);
266 SupportType type
= SupportType
.valueOfAllOkUC(meta
268 URL url
= file
.toURI().toURL();
270 story
= BasicSupport
.getSupport(type
).process(url
, pg
);
272 throw new IOException("Unknown type: " + meta
.getType());
274 } catch (IOException e
) {
275 // We should not have not-supported files in the
277 Instance
.syserr(new IOException(
278 "Cannot load file from library: " + file
, e
));
291 * Import the {@link Story} at the given {@link URL} into the
292 * {@link BasicLibrary}.
295 * the {@link URL} to import
297 * the optional progress reporter
299 * @return the imported {@link Story}
301 * @throws IOException
302 * in case of I/O error
304 public Story
imprt(URL url
, Progress pg
) throws IOException
{
305 BasicSupport support
= BasicSupport
.getSupport(url
);
306 if (support
== null) {
307 throw new IOException("URL not supported: " + url
.toString());
310 return save(support
.process(url
, pg
), null);
314 * Export the {@link Story} to the given target in the given format.
317 * the {@link Story} ID
319 * the {@link OutputType} to transform it to
321 * the target to save to
323 * the optional progress reporter
325 * @return the saved resource (the main saved {@link File})
327 * @throws IOException
328 * in case of I/O error
330 public File
export(String luid
, OutputType type
, String target
, Progress pg
)
332 Progress pgGetStory
= new Progress();
333 Progress pgOut
= new Progress();
336 pg
.addProgress(pgGetStory
, 1);
337 pg
.addProgress(pgOut
, 1);
340 BasicOutput out
= BasicOutput
.getOutput(type
, true);
342 throw new IOException("Output type not supported: " + type
);
345 Story story
= getStory(luid
, pgGetStory
);
347 throw new IOException("Cannot find story to export: " + luid
);
350 return out
.process(story
, target
, pgOut
);
354 * Save a {@link Story} to the {@link BasicLibrary}.
357 * the {@link Story} to save
359 * the optional progress reporter
361 * @return the same {@link Story}, whose LUID may have changed
363 * @throws IOException
364 * in case of I/O error
366 public Story
save(Story story
, Progress pg
) throws IOException
{
367 return save(story
, null, pg
);
371 * Save a {@link Story} to the {@link BasicLibrary} -- the LUID <b>must</b>
372 * be correct, or NULL to get the next free one.
374 * Will override any previous {@link Story} with the same LUID.
377 * the {@link Story} to save
379 * the <b>correct</b> LUID or NULL to get the next free one
381 * the optional progress reporter
383 * @return the same {@link Story}, whose LUID may have changed
385 * @throws IOException
386 * in case of I/O error
388 public synchronized Story
save(Story story
, String luid
, Progress pg
)
390 // Do not change the original metadata, but change the original story
391 MetaData meta
= story
.getMeta().clone();
394 if (luid
== null || luid
.isEmpty()) {
395 meta
.setLuid(String
.format("%03d", getNextId()));
400 if (getInfo(luid
) != null) {
411 * Delete the given {@link Story} from this {@link BasicLibrary}.
414 * the LUID of the target {@link Story}
416 * @throws IOException
417 * in case of I/O error
419 public synchronized void delete(String luid
) throws IOException
{
425 * Change the type (source) of the given {@link Story}.
428 * the {@link Story} LUID
432 * the optional progress reporter
434 * @throws IOException
435 * in case of I/O error or if the {@link Story} was not found
437 public synchronized void changeSource(String luid
, String newSource
,
438 Progress pg
) throws IOException
{
439 MetaData meta
= getInfo(luid
);
441 throw new IOException("Story not found: " + luid
);
444 meta
.setSource(newSource
);
449 * Save back the current state of the {@link MetaData} (LUID <b>MUST NOT</b>
450 * change) for this {@link Story}.
452 * By default, delete the old {@link Story} then recreate a new
455 * Note that this behaviour can lead to data loss.
458 * the new {@link MetaData} (LUID <b>MUST NOT</b> change)
460 * the optional {@link Progress}
462 * @throws IOException
463 * in case of I/O error or if the {@link Story} was not found
465 protected synchronized void saveMeta(MetaData meta
, Progress pg
)
471 Progress pgGet
= new Progress();
472 Progress pgSet
= new Progress();
473 pg
.addProgress(pgGet
, 50);
474 pg
.addProgress(pgSet
, 50);
476 Story story
= getStory(meta
.getLuid(), pgGet
);
478 throw new IOException("Story not found: " + meta
.getLuid());
481 delete(meta
.getLuid());
484 save(story
, meta
.getLuid(), pgSet
);