1 package be
.nikiroo
.fanfix
.library
;
3 import java
.awt
.image
.BufferedImage
;
5 import java
.io
.IOException
;
7 import java
.net
.UnknownHostException
;
8 import java
.util
.ArrayList
;
9 import java
.util
.Collections
;
10 import java
.util
.List
;
12 import be
.nikiroo
.fanfix
.Instance
;
13 import be
.nikiroo
.fanfix
.data
.MetaData
;
14 import be
.nikiroo
.fanfix
.data
.Story
;
15 import be
.nikiroo
.fanfix
.output
.BasicOutput
;
16 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
17 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
18 import be
.nikiroo
.fanfix
.supported
.BasicSupport
.SupportType
;
19 import be
.nikiroo
.utils
.Progress
;
22 * Manage a library of Stories: import, export, list, modify.
24 * Each {@link Story} object will be associated with a (local to the library)
25 * unique ID, the LUID, which will be used to identify the {@link Story}.
27 * Most of the {@link BasicLibrary} functions work on a partial (cover
28 * <b>MAY</b> not be included) {@link MetaData} object.
32 abstract public class BasicLibrary
{
34 * A {@link BasicLibrary} status.
39 /** The library is ready. */
41 /** The library is invalid (not correctly set up). */
43 /** You are not allowed to access this library. */
45 /** The library is currently out of commission. */
50 * Return a name for this library (the UI may display this).
54 * @return the name, or an empty {@link String} if none
56 public String
getLibraryName() {
63 * @return the current status
65 public Status
getStatus() {
70 * Retrieve the main {@link File} corresponding to the given {@link Story},
71 * which can be passed to an external reader or instance.
73 * Do <b>NOT</b> alter this file.
76 * the Library UID of the story
78 * the optional {@link Progress}
80 * @return the corresponding {@link Story}
82 public abstract File
getFile(String luid
, Progress pg
);
85 * Return the cover image associated to this story.
88 * the Library UID of the story
90 * @return the cover image
92 public abstract BufferedImage
getCover(String luid
);
95 * Return the cover image associated to this source.
97 * By default, return the cover of the first story with this source.
102 * @return the cover image or NULL
104 public BufferedImage
getSourceCover(String source
) {
105 List
<MetaData
> metas
= getListBySource(source
);
106 if (metas
.size() > 0) {
107 return getCover(metas
.get(0).getLuid());
114 * Fix the source cover to the given story cover.
117 * the source to change
121 public abstract void setSourceCover(String source
, String luid
);
124 * Return the list of stories (represented by their {@link MetaData}, which
125 * <b>MAY</b> not have the cover included).
128 * the optional {@link Progress}
130 * @return the list (can be empty but not NULL)
132 protected abstract List
<MetaData
> getMetas(Progress pg
);
135 * Invalidate the {@link Story} cache (when the content should be re-read
136 * because it was changed).
138 protected abstract void clearCache();
141 * Return the next LUID that can be used.
143 * @return the next luid
145 protected abstract int getNextId();
148 * Delete the target {@link Story}.
151 * the LUID of the {@link Story}
153 * @throws IOException
154 * in case of I/O error or if the {@link Story} wa not found
156 protected abstract void doDelete(String luid
) throws IOException
;
159 * Actually save the story to the back-end.
162 * the {@link Story} to save
164 * the optional {@link Progress}
166 * @return the saved {@link Story} (which may have changed, especially
167 * regarding the {@link MetaData})
169 * @throws IOException
170 * in case of I/O error
172 protected abstract Story
doSave(Story story
, Progress pg
)
176 * Refresh the {@link BasicLibrary}, that is, make sure all metas are
180 * the optional progress reporter
182 public void refresh(Progress pg
) {
187 * List all the known types (sources) of stories.
189 * @return the sources
191 public synchronized List
<String
> getSources() {
192 List
<String
> list
= new ArrayList
<String
>();
193 for (MetaData meta
: getMetas(null)) {
194 String storySource
= meta
.getSource();
195 if (!list
.contains(storySource
)) {
196 list
.add(storySource
);
200 Collections
.sort(list
);
205 * List all the known authors of stories.
207 * @return the authors
209 public synchronized List
<String
> getAuthors() {
210 List
<String
> list
= new ArrayList
<String
>();
211 for (MetaData meta
: getMetas(null)) {
212 String storyAuthor
= meta
.getAuthor();
213 if (!list
.contains(storyAuthor
)) {
214 list
.add(storyAuthor
);
218 Collections
.sort(list
);
223 * List all the stories in the {@link BasicLibrary}.
225 * Cover images not included.
227 * @return the stories
229 public synchronized List
<MetaData
> getList() {
230 return getMetas(null);
234 * List all the stories of the given source type in the {@link BasicLibrary}
235 * , or all the stories if NULL is passed as a type.
237 * Cover images not included.
240 * the type of story to retrieve, or NULL for all
242 * @return the stories
244 public synchronized List
<MetaData
> getListBySource(String type
) {
245 List
<MetaData
> list
= new ArrayList
<MetaData
>();
246 for (MetaData meta
: getMetas(null)) {
247 String storyType
= meta
.getSource();
248 if (type
== null || type
.equalsIgnoreCase(storyType
)) {
253 Collections
.sort(list
);
258 * List all the stories of the given author in the {@link BasicLibrary}, or
259 * all the stories if NULL is passed as an author.
261 * Cover images not included.
264 * the author of the stories to retrieve, or NULL for all
266 * @return the stories
268 public synchronized List
<MetaData
> getListByAuthor(String author
) {
269 List
<MetaData
> list
= new ArrayList
<MetaData
>();
270 for (MetaData meta
: getMetas(null)) {
271 String storyAuthor
= meta
.getAuthor();
272 if (author
== null || author
.equalsIgnoreCase(storyAuthor
)) {
277 Collections
.sort(list
);
282 * Retrieve a {@link MetaData} corresponding to the given {@link Story},
283 * cover image <b>MAY</b> not be included.
286 * the Library UID of the story
288 * @return the corresponding {@link Story}
290 public synchronized MetaData
getInfo(String luid
) {
292 for (MetaData meta
: getMetas(null)) {
293 if (luid
.equals(meta
.getLuid())) {
303 * Retrieve a specific {@link Story}.
306 * the Library UID of the story
308 * the optional progress reporter
310 * @return the corresponding {@link Story} or NULL if not found
312 public synchronized Story
getStory(String luid
, Progress pg
) {
317 Progress pgGet
= new Progress();
318 Progress pgProcess
= new Progress();
321 pg
.addProgress(pgGet
, 1);
322 pg
.addProgress(pgProcess
, 1);
325 for (MetaData meta
: getMetas(null)) {
326 if (meta
.getLuid().equals(luid
)) {
327 File file
= getFile(luid
, pgGet
);
330 SupportType type
= SupportType
.valueOfAllOkUC(meta
332 URL url
= file
.toURI().toURL();
334 story
= BasicSupport
.getSupport(type
).process(url
,
336 // Because we do not want to clear the meta cache:
337 meta
.setCover(story
.getMeta().getCover());
341 throw new IOException("Unknown type: " + meta
.getType());
343 } catch (IOException e
) {
344 // We should not have not-supported files in the
346 Instance
.getTraceHandler().error(
347 new IOException("Cannot load file from library: "
362 * Import the {@link Story} at the given {@link URL} into the
363 * {@link BasicLibrary}.
366 * the {@link URL} to import
368 * the optional progress reporter
370 * @return the imported {@link Story}
372 * @throws UnknownHostException
373 * if the host is not supported
374 * @throws IOException
375 * in case of I/O error
377 public Story
imprt(URL url
, Progress pg
) throws IOException
{
378 BasicSupport support
= BasicSupport
.getSupport(url
);
379 if (support
== null) {
380 throw new UnknownHostException("" + url
);
383 return save(support
.process(url
, pg
), null);
387 * Import the story from one library to another, and keep the same LUID.
390 * the other library to import from
394 * the optional progress reporter
396 * @throws IOException
397 * in case of I/O error
399 public void imprt(BasicLibrary other
, String luid
, Progress pg
)
401 Progress pgGetStory
= new Progress();
402 Progress pgSave
= new Progress();
408 pg
.addProgress(pgGetStory
, 1);
409 pg
.addProgress(pgSave
, 1);
411 Story story
= other
.getStory(luid
, pgGetStory
);
413 story
= this.save(story
, luid
, pgSave
);
417 throw new IOException("Cannot find story in Library: " + luid
);
422 * Export the {@link Story} to the given target in the given format.
425 * the {@link Story} ID
427 * the {@link OutputType} to transform it to
429 * the target to save to
431 * the optional progress reporter
433 * @return the saved resource (the main saved {@link File})
435 * @throws IOException
436 * in case of I/O error
438 public File
export(String luid
, OutputType type
, String target
, Progress pg
)
440 Progress pgGetStory
= new Progress();
441 Progress pgOut
= new Progress();
444 pg
.addProgress(pgGetStory
, 1);
445 pg
.addProgress(pgOut
, 1);
448 BasicOutput out
= BasicOutput
.getOutput(type
, false, false);
450 throw new IOException("Output type not supported: " + type
);
453 Story story
= getStory(luid
, pgGetStory
);
455 throw new IOException("Cannot find story to export: " + luid
);
458 return out
.process(story
, target
, pgOut
);
462 * Save a {@link Story} to the {@link BasicLibrary}.
465 * the {@link Story} to save
467 * the optional progress reporter
469 * @return the same {@link Story}, whose LUID may have changed
471 * @throws IOException
472 * in case of I/O error
474 public Story
save(Story story
, Progress pg
) throws IOException
{
475 return save(story
, null, pg
);
479 * Save a {@link Story} to the {@link BasicLibrary} -- the LUID <b>must</b>
480 * be correct, or NULL to get the next free one.
482 * Will override any previous {@link Story} with the same LUID.
485 * the {@link Story} to save
487 * the <b>correct</b> LUID or NULL to get the next free one
489 * the optional progress reporter
491 * @return the same {@link Story}, whose LUID may have changed
493 * @throws IOException
494 * in case of I/O error
496 public synchronized Story
save(Story story
, String luid
, Progress pg
)
498 // Do not change the original metadata, but change the original story
499 MetaData meta
= story
.getMeta().clone();
502 if (luid
== null || luid
.isEmpty()) {
503 meta
.setLuid(String
.format("%03d", getNextId()));
508 if (getInfo(luid
) != null) {
520 * Delete the given {@link Story} from this {@link BasicLibrary}.
523 * the LUID of the target {@link Story}
525 * @throws IOException
526 * in case of I/O error
528 public synchronized void delete(String luid
) throws IOException
{
534 * Change the type (source) of the given {@link Story}.
537 * the {@link Story} LUID
541 * the optional progress reporter
543 * @throws IOException
544 * in case of I/O error or if the {@link Story} was not found
546 public synchronized void changeSource(String luid
, String newSource
,
547 Progress pg
) throws IOException
{
548 MetaData meta
= getInfo(luid
);
550 throw new IOException("Story not found: " + luid
);
553 meta
.setSource(newSource
);
558 * Save back the current state of the {@link MetaData} (LUID <b>MUST NOT</b>
559 * change) for this {@link Story}.
561 * By default, delete the old {@link Story} then recreate a new
564 * Note that this behaviour can lead to data loss.
567 * the new {@link MetaData} (LUID <b>MUST NOT</b> change)
569 * the optional {@link Progress}
571 * @throws IOException
572 * in case of I/O error or if the {@link Story} was not found
574 protected synchronized void saveMeta(MetaData meta
, Progress pg
)
580 Progress pgGet
= new Progress();
581 Progress pgSet
= new Progress();
582 pg
.addProgress(pgGet
, 50);
583 pg
.addProgress(pgSet
, 50);
585 Story story
= getStory(meta
.getLuid(), pgGet
);
587 throw new IOException("Story not found: " + meta
.getLuid());
590 delete(meta
.getLuid());
593 save(story
, meta
.getLuid(), pgSet
);