weblib: fix delete and imprt
[fanfix.git] / src / be / nikiroo / fanfix / library / BasicLibrary.java
CommitLineData
e42573a0 1package be.nikiroo.fanfix.library;
68e2c6d2 2
68e2c6d2
NR
3import java.io.File;
4import java.io.IOException;
5import java.net.URL;
fd1d31c2 6import java.net.UnknownHostException;
599b05c7 7import java.util.LinkedHashMap;
68e2c6d2 8import java.util.List;
c1b93db3 9import java.util.Map;
68e2c6d2 10
e42573a0 11import be.nikiroo.fanfix.Instance;
68e2c6d2
NR
12import be.nikiroo.fanfix.data.MetaData;
13import be.nikiroo.fanfix.data.Story;
14import be.nikiroo.fanfix.output.BasicOutput;
15import be.nikiroo.fanfix.output.BasicOutput.OutputType;
16import be.nikiroo.fanfix.supported.BasicSupport;
0ffa4754 17import be.nikiroo.fanfix.supported.SupportType;
16a81ef7 18import be.nikiroo.utils.Image;
68e2c6d2 19import be.nikiroo.utils.Progress;
5f42f329 20import be.nikiroo.utils.StringUtils;
68e2c6d2
NR
21
22/**
23 * Manage a library of Stories: import, export, list, modify.
24 * <p>
25 * Each {@link Story} object will be associated with a (local to the library)
26 * unique ID, the LUID, which will be used to identify the {@link Story}.
27 * <p>
28 * Most of the {@link BasicLibrary} functions work on a partial (cover
29 * <b>MAY</b> not be included) {@link MetaData} object.
30 *
31 * @author niki
32 */
33abstract public class BasicLibrary {
e6249b0f
NR
34 /**
35 * A {@link BasicLibrary} status.
36 *
37 * @author niki
38 */
39 public enum Status {
0bb51c9c
NR
40 /** The library is ready and r/w. */
41 READ_WRITE,
42 /** The library is ready, but read-only. */
43 READ_ONLY,
e6249b0f 44 /** You are not allowed to access this library. */
99206a39 45 UNAUTHORIZED,
33d40b9f
NR
46 /** The library is invalid, and will never work as is. */
47 INVALID,
48 /** The library is currently out of commission, but may work later. */
0bb51c9c
NR
49 UNAVAILABLE;
50
51 /**
52 * The library is available (you can query it).
53 * <p>
54 * It does <b>not</b> specify if it is read-only or not.
55 *
56 * @return TRUE if it is
57 */
58 public boolean isReady() {
59 return (this == READ_WRITE || this == READ_ONLY);
60 }
61
62 /**
63 * This library can be modified (= you are allowed to modify it).
64 *
65 * @return TRUE if it is
66 */
67 public boolean isWritable() {
68 return (this == READ_WRITE);
69 }
e6249b0f
NR
70 }
71
99ccbdf6
NR
72 /**
73 * Return a name for this library (the UI may display this).
74 * <p>
75 * Must not be NULL.
76 *
77 * @return the name, or an empty {@link String} if none
78 */
79 public String getLibraryName() {
80 return "";
81 }
82
e6249b0f
NR
83 /**
84 * The library status.
85 *
86 * @return the current status
87 */
88 public Status getStatus() {
0bb51c9c 89 return Status.READ_WRITE;
e6249b0f
NR
90 }
91
68e2c6d2
NR
92 /**
93 * Retrieve the main {@link File} corresponding to the given {@link Story},
94 * which can be passed to an external reader or instance.
95 * <p>
96 * Do <b>NOT</b> alter this file.
97 *
98 * @param luid
dc919036 99 * the Library UID of the story, can be NULL
ff05b828
NR
100 * @param pg
101 * the optional {@link Progress}
68e2c6d2
NR
102 *
103 * @return the corresponding {@link Story}
0bb51c9c
NR
104 *
105 * @throws IOException
106 * in case of IOException
68e2c6d2 107 */
0bb51c9c 108 public abstract File getFile(String luid, Progress pg) throws IOException;
68e2c6d2
NR
109
110 /**
111 * Return the cover image associated to this story.
112 *
113 * @param luid
114 * the Library UID of the story
115 *
116 * @return the cover image
0bb51c9c
NR
117 *
118 * @throws IOException
119 * in case of IOException
68e2c6d2 120 */
0bb51c9c 121 public abstract Image getCover(String luid) throws IOException;
68e2c6d2 122
599b05c7
NR
123 /**
124 * Retrieve the list of {@link MetaData} known by this {@link BasicLibrary}
125 * in a easy-to-filter version.
126 *
127 * @param pg
128 * the optional {@link Progress}
129 * @return the list of {@link MetaData} as a {@link MetaResultList} you can
130 * query
131 * @throws IOException
132 * in case of I/O eror
133 */
dc919036 134 public MetaResultList getList(Progress pg) throws IOException {
599b05c7
NR
135 // TODO: ensure it is the main used interface
136
1f2a7d5f
NR
137 return new MetaResultList(getMetas(pg));
138 }
dc919036 139
599b05c7 140 // TODO: make something for (normal and custom) non-story covers
dc919036 141
14b57448
NR
142 /**
143 * Return the cover image associated to this source.
144 * <p>
e1de8087
NR
145 * By default, return the custom cover if any, and if not, return the cover
146 * of the first story with this source.
14b57448
NR
147 *
148 * @param source
149 * the source
150 *
151 * @return the cover image or NULL
0bb51c9c
NR
152 *
153 * @throws IOException
154 * in case of IOException
14b57448 155 */
0bb51c9c 156 public Image getSourceCover(String source) throws IOException {
e1de8087
NR
157 Image custom = getCustomSourceCover(source);
158 if (custom != null) {
159 return custom;
160 }
161
ef98466f 162 List<MetaData> metas = getList().filter(source, null, null);
14b57448
NR
163 if (metas.size() > 0) {
164 return getCover(metas.get(0).getLuid());
165 }
166
167 return null;
168 }
169
3989dfc5
NR
170 /**
171 * Return the cover image associated to this author.
172 * <p>
173 * By default, return the custom cover if any, and if not, return the cover
174 * of the first story with this author.
175 *
176 * @param author
177 * the author
178 *
179 * @return the cover image or NULL
0bb51c9c
NR
180 *
181 * @throws IOException
182 * in case of IOException
3989dfc5 183 */
0bb51c9c 184 public Image getAuthorCover(String author) throws IOException {
3989dfc5
NR
185 Image custom = getCustomAuthorCover(author);
186 if (custom != null) {
187 return custom;
188 }
189
ef98466f 190 List<MetaData> metas = getList().filter(null, author, null);
3989dfc5
NR
191 if (metas.size() > 0) {
192 return getCover(metas.get(0).getLuid());
193 }
194
195 return null;
196 }
197
e1de8087
NR
198 /**
199 * Return the custom cover image associated to this source.
200 * <p>
201 * By default, return NULL.
202 *
203 * @param source
204 * the source to look for
205 *
206 * @return the custom cover or NULL if none
0bb51c9c
NR
207 *
208 * @throws IOException
209 * in case of IOException
e1de8087 210 */
0c7a6637
NR
211 @SuppressWarnings("unused")
212 public Image getCustomSourceCover(String source) throws IOException {
e1de8087
NR
213 return null;
214 }
215
14b57448 216 /**
3989dfc5
NR
217 * Return the custom cover image associated to this author.
218 * <p>
219 * By default, return NULL.
220 *
221 * @param author
222 * the author to look for
223 *
224 * @return the custom cover or NULL if none
0bb51c9c
NR
225 *
226 * @throws IOException
227 * in case of IOException
3989dfc5 228 */
0c7a6637
NR
229 @SuppressWarnings("unused")
230 public Image getCustomAuthorCover(String author) throws IOException {
3989dfc5
NR
231 return null;
232 }
233
234 /**
235 * Set the source cover to the given story cover.
14b57448
NR
236 *
237 * @param source
238 * the source to change
239 * @param luid
240 * the story LUID
0bb51c9c
NR
241 *
242 * @throws IOException
243 * in case of IOException
14b57448 244 */
0bb51c9c
NR
245 public abstract void setSourceCover(String source, String luid)
246 throws IOException;
14b57448 247
3989dfc5
NR
248 /**
249 * Set the author cover to the given story cover.
250 *
d4449e96 251 * @param author
3989dfc5
NR
252 * the author to change
253 * @param luid
254 * the story LUID
0bb51c9c
NR
255 *
256 * @throws IOException
257 * in case of IOException
3989dfc5 258 */
0bb51c9c
NR
259 public abstract void setAuthorCover(String author, String luid)
260 throws IOException;
3989dfc5 261
68e2c6d2
NR
262 /**
263 * Return the list of stories (represented by their {@link MetaData}, which
264 * <b>MAY</b> not have the cover included).
1f2a7d5f
NR
265 * <p>
266 * The returned list <b>MUST</b> be a copy, not the original one.
68e2c6d2
NR
267 *
268 * @param pg
269 * the optional {@link Progress}
270 *
271 * @return the list (can be empty but not NULL)
0bb51c9c
NR
272 *
273 * @throws IOException
274 * in case of IOException
68e2c6d2 275 */
0bb51c9c 276 protected abstract List<MetaData> getMetas(Progress pg) throws IOException;
68e2c6d2
NR
277
278 /**
279 * Invalidate the {@link Story} cache (when the content should be re-read
280 * because it was changed).
281 */
c8d48938
NR
282 protected void invalidateInfo() {
283 invalidateInfo(null);
e272f05f
NR
284 }
285
286 /**
efa3c511
NR
287 * Invalidate the {@link Story} cache (when the content is removed).
288 * <p>
289 * All the cache can be deleted if NULL is passed as meta.
e272f05f
NR
290 *
291 * @param luid
efa3c511 292 * the LUID of the {@link Story} to clear from the cache, or NULL
e272f05f
NR
293 * for all stories
294 */
c8d48938 295 protected abstract void invalidateInfo(String luid);
efa3c511
NR
296
297 /**
298 * Invalidate the {@link Story} cache (when the content has changed, but we
299 * already have it) with the new given meta.
300 *
301 * @param meta
302 * the {@link Story} to clear from the cache
0bb51c9c
NR
303 *
304 * @throws IOException
305 * in case of IOException
efa3c511 306 */
0bb51c9c 307 protected abstract void updateInfo(MetaData meta) throws IOException;
68e2c6d2
NR
308
309 /**
310 * Return the next LUID that can be used.
311 *
312 * @return the next luid
313 */
f70bcacf 314 protected abstract String getNextId();
68e2c6d2
NR
315
316 /**
317 * Delete the target {@link Story}.
318 *
319 * @param luid
320 * the LUID of the {@link Story}
321 *
322 * @throws IOException
323 * in case of I/O error or if the {@link Story} wa not found
324 */
325 protected abstract void doDelete(String luid) throws IOException;
326
327 /**
328 * Actually save the story to the back-end.
329 *
330 * @param story
331 * the {@link Story} to save
332 * @param pg
333 * the optional {@link Progress}
334 *
335 * @return the saved {@link Story} (which may have changed, especially
336 * regarding the {@link MetaData})
337 *
338 * @throws IOException
339 * in case of I/O error
340 */
341 protected abstract Story doSave(Story story, Progress pg)
342 throws IOException;
343
344 /**
e6249b0f 345 * Refresh the {@link BasicLibrary}, that is, make sure all metas are
68e2c6d2
NR
346 * loaded.
347 *
68e2c6d2
NR
348 * @param pg
349 * the optional progress reporter
350 */
dc919036 351 public void refresh(Progress pg) {
0bb51c9c
NR
352 try {
353 getMetas(pg);
354 } catch (IOException e) {
355 // We will let it fail later
356 }
68e2c6d2 357 }
dc919036 358
4452446c
NR
359 /**
360 * Check if the {@link Story} denoted by this Library UID is present in the
541f433a 361 * cache (if we have no cache, we default to </tt>true</tt>).
4452446c
NR
362 *
363 * @param luid
364 * the Library UID
365 *
366 * @return TRUE if it is
367 */
541f433a 368 public boolean isCached(@SuppressWarnings("unused") String luid) {
4452446c
NR
369 // By default, everything is cached
370 return true;
371 }
dc919036 372
4452446c
NR
373 /**
374 * Clear the {@link Story} from the cache, if needed.
375 * <p>
376 * The next time we try to retrieve the {@link Story}, it may be required to
377 * cache it again.
378 *
379 * @param luid
380 * the story to clear
381 *
382 * @throws IOException
383 * in case of I/O error
384 */
541f433a 385 @SuppressWarnings("unused")
4452446c
NR
386 public void clearFromCache(String luid) throws IOException {
387 // By default, this is a noop.
388 }
68e2c6d2
NR
389
390 /**
599b05c7
NR
391 * @return the same as getList()
392 * @throws IOException
393 * in case of I/O error
59819600
NR
394 * @deprecated please use {@link BasicLibrary#getList()} and
395 * {@link MetaResultList#getSources()} instead.
68e2c6d2 396 */
59819600 397 @Deprecated
dc919036 398 public List<String> getSources() throws IOException {
59819600 399 return getList().getSources();
68e2c6d2
NR
400 }
401
c1b93db3 402 /**
599b05c7
NR
403 * @return the same as getList()
404 * @throws IOException
405 * in case of I/O error
59819600
NR
406 * @deprecated please use {@link BasicLibrary#getList()} and
407 * {@link MetaResultList#getSourcesGrouped()} instead.
c1b93db3 408 */
59819600 409 @Deprecated
dc919036 410 public Map<String, List<String>> getSourcesGrouped() throws IOException {
59819600 411 return getList().getSourcesGrouped();
c1b93db3
NR
412 }
413
68e2c6d2 414 /**
599b05c7
NR
415 * @return the same as getList()
416 * @throws IOException
417 * in case of I/O error
59819600
NR
418 * @deprecated please use {@link BasicLibrary#getList()} and
419 * {@link MetaResultList#getAuthors()} instead.
68e2c6d2 420 */
59819600 421 @Deprecated
dc919036 422 public List<String> getAuthors() throws IOException {
59819600 423 return getList().getAuthors();
68e2c6d2
NR
424 }
425
5f42f329 426 /**
599b05c7
NR
427 * @return the same as getList()
428 * @throws IOException
429 * in case of I/O error
59819600
NR
430 * @deprecated please use {@link BasicLibrary#getList()} and
431 * {@link MetaResultList#getAuthorsGrouped()} instead.
5f42f329 432 */
599b05c7 433 @Deprecated
0bb51c9c 434 public Map<String, List<String>> getAuthorsGrouped() throws IOException {
59819600 435 return getList().getAuthorsGrouped();
5f42f329
NR
436 }
437
68e2c6d2
NR
438 /**
439 * List all the stories in the {@link BasicLibrary}.
440 * <p>
3a0605e6 441 * Cover images <b>MAYBE</b> not included.
68e2c6d2
NR
442 *
443 * @return the stories
0bb51c9c
NR
444 *
445 * @throws IOException
446 * in case of IOException
68e2c6d2 447 */
ef98466f
NR
448 public MetaResultList getList() throws IOException {
449 return getList(null);
68e2c6d2
NR
450 }
451
452 /**
453 * Retrieve a {@link MetaData} corresponding to the given {@link Story},
454 * cover image <b>MAY</b> not be included.
455 *
456 * @param luid
dc919036 457 * the Library UID of the story, can be NULL
68e2c6d2 458 *
dc919036 459 * @return the corresponding {@link Story} or NULL if not found
0bb51c9c
NR
460 *
461 * @throws IOException
462 * in case of IOException
68e2c6d2 463 */
dc919036 464 public MetaData getInfo(String luid) throws IOException {
68e2c6d2
NR
465 if (luid != null) {
466 for (MetaData meta : getMetas(null)) {
467 if (luid.equals(meta.getLuid())) {
468 return meta;
469 }
470 }
471 }
472
473 return null;
474 }
475
476 /**
477 * Retrieve a specific {@link Story}.
478 *
479 * @param luid
480 * the Library UID of the story
481 * @param pg
482 * the optional progress reporter
483 *
484 * @return the corresponding {@link Story} or NULL if not found
0bb51c9c
NR
485 *
486 * @throws IOException
487 * in case of IOException
68e2c6d2 488 */
59819600 489 public Story getStory(String luid, Progress pg) throws IOException {
60f72311
NR
490 Progress pgMetas = new Progress();
491 Progress pgStory = new Progress();
492 if (pg != null) {
493 pg.setMinMax(0, 100);
494 pg.addProgress(pgMetas, 10);
495 pg.addProgress(pgStory, 90);
496 }
497
498 MetaData meta = null;
499 for (MetaData oneMeta : getMetas(pgMetas)) {
500 if (oneMeta.getLuid().equals(luid)) {
501 meta = oneMeta;
502 break;
503 }
504 }
505
506 pgMetas.done();
507
508 Story story = getStory(luid, meta, pgStory);
509 pgStory.done();
510
511 return story;
512 }
513
514 /**
515 * Retrieve a specific {@link Story}.
516 *
517 * @param luid
dc919036
NR
518 * the LUID of the story
519 * @param meta
60f72311
NR
520 * the meta of the story
521 * @param pg
522 * the optional progress reporter
523 *
524 * @return the corresponding {@link Story} or NULL if not found
0bb51c9c
NR
525 *
526 * @throws IOException
527 * in case of IOException
60f72311 528 */
dc919036 529 public synchronized Story getStory(String luid, MetaData meta, Progress pg)
0bb51c9c 530 throws IOException {
60f72311 531
68e2c6d2
NR
532 if (pg == null) {
533 pg = new Progress();
534 }
535
ff05b828
NR
536 Progress pgGet = new Progress();
537 Progress pgProcess = new Progress();
538
539 pg.setMinMax(0, 2);
540 pg.addProgress(pgGet, 1);
541 pg.addProgress(pgProcess, 1);
542
68e2c6d2 543 Story story = null;
dc919036
NR
544 File file = null;
545
546 if (luid != null && meta != null) {
547 file = getFile(luid, pgGet);
548 }
549
60f72311
NR
550 pgGet.done();
551 try {
dc919036
NR
552 if (file != null) {
553 SupportType type = SupportType.valueOfAllOkUC(meta.getType());
554 if (type == null) {
555 throw new IOException("Unknown type: " + meta.getType());
556 }
557
558 URL url = file.toURI().toURL();
60f72311
NR
559 story = BasicSupport.getSupport(type, url) //
560 .process(pgProcess);
561
562 // Because we do not want to clear the meta cache:
563 meta.setCover(story.getMeta().getCover());
564 meta.setResume(story.getMeta().getResume());
565 story.setMeta(meta);
68e2c6d2 566 }
60f72311 567 } catch (IOException e) {
dc919036
NR
568 // We should not have not-supported files in the library
569 Instance.getInstance().getTraceHandler()
570 .error(new IOException(String.format(
571 "Cannot load file of type '%s' from library: %s",
572 meta.getType(), file), e));
60f72311
NR
573 } finally {
574 pgProcess.done();
575 pg.done();
68e2c6d2
NR
576 }
577
578 return story;
579 }
580
581 /**
582 * Import the {@link Story} at the given {@link URL} into the
583 * {@link BasicLibrary}.
584 *
585 * @param url
586 * the {@link URL} to import
587 * @param pg
588 * the optional progress reporter
589 *
b6b65795 590 * @return the imported Story {@link MetaData}
68e2c6d2 591 *
fd1d31c2
NR
592 * @throws UnknownHostException
593 * if the host is not supported
68e2c6d2
NR
594 * @throws IOException
595 * in case of I/O error
596 */
b6b65795 597 public MetaData imprt(URL url, Progress pg) throws IOException {
acbec0d2
NR
598 return imprt(url, null, pg);
599 }
600
601 /**
602 * Import the {@link Story} at the given {@link URL} into the
603 * {@link BasicLibrary}.
604 *
605 * @param url
606 * the {@link URL} to import
607 * @param luid
608 * the LUID to use
609 * @param pg
610 * the optional progress reporter
611 *
612 * @return the imported Story {@link MetaData}
613 *
614 * @throws UnknownHostException
615 * if the host is not supported
616 * @throws IOException
617 * in case of I/O error
618 */
619 MetaData imprt(URL url, String luid, Progress pg) throws IOException {
9b863b20
NR
620 if (pg == null)
621 pg = new Progress();
622
623 pg.setMinMax(0, 1000);
624 Progress pgProcess = new Progress();
625 Progress pgSave = new Progress();
626 pg.addProgress(pgProcess, 800);
627 pg.addProgress(pgSave, 200);
628
68e2c6d2
NR
629 BasicSupport support = BasicSupport.getSupport(url);
630 if (support == null) {
fd1d31c2 631 throw new UnknownHostException("" + url);
68e2c6d2
NR
632 }
633
acbec0d2 634 Story story = save(support.process(pgProcess), luid, pgSave);
9b863b20
NR
635 pg.done();
636
b6b65795 637 return story.getMeta();
68e2c6d2
NR
638 }
639
b89dfb6e
NR
640 /**
641 * Import the story from one library to another, and keep the same LUID.
642 *
643 * @param other
644 * the other library to import from
645 * @param luid
646 * the Library UID
647 * @param pg
648 * the optional progress reporter
649 *
650 * @throws IOException
651 * in case of I/O error
652 */
653 public void imprt(BasicLibrary other, String luid, Progress pg)
654 throws IOException {
655 Progress pgGetStory = new Progress();
656 Progress pgSave = new Progress();
657 if (pg == null) {
658 pg = new Progress();
659 }
660
661 pg.setMinMax(0, 2);
662 pg.addProgress(pgGetStory, 1);
663 pg.addProgress(pgSave, 1);
664
665 Story story = other.getStory(luid, pgGetStory);
666 if (story != null) {
667 story = this.save(story, luid, pgSave);
668 pg.done();
669 } else {
670 pg.done();
671 throw new IOException("Cannot find story in Library: " + luid);
672 }
673 }
674
68e2c6d2
NR
675 /**
676 * Export the {@link Story} to the given target in the given format.
677 *
678 * @param luid
679 * the {@link Story} ID
680 * @param type
681 * the {@link OutputType} to transform it to
682 * @param target
683 * the target to save to
684 * @param pg
685 * the optional progress reporter
686 *
687 * @return the saved resource (the main saved {@link File})
688 *
689 * @throws IOException
690 * in case of I/O error
691 */
692 public File export(String luid, OutputType type, String target, Progress pg)
693 throws IOException {
694 Progress pgGetStory = new Progress();
695 Progress pgOut = new Progress();
696 if (pg != null) {
697 pg.setMax(2);
698 pg.addProgress(pgGetStory, 1);
699 pg.addProgress(pgOut, 1);
700 }
701
925298fd 702 BasicOutput out = BasicOutput.getOutput(type, false, false);
68e2c6d2
NR
703 if (out == null) {
704 throw new IOException("Output type not supported: " + type);
705 }
706
707 Story story = getStory(luid, pgGetStory);
708 if (story == null) {
709 throw new IOException("Cannot find story to export: " + luid);
710 }
711
712 return out.process(story, target, pgOut);
713 }
714
715 /**
716 * Save a {@link Story} to the {@link BasicLibrary}.
717 *
718 * @param story
719 * the {@link Story} to save
720 * @param pg
721 * the optional progress reporter
722 *
723 * @return the same {@link Story}, whose LUID may have changed
724 *
725 * @throws IOException
726 * in case of I/O error
727 */
728 public Story save(Story story, Progress pg) throws IOException {
729 return save(story, null, pg);
730 }
731
732 /**
733 * Save a {@link Story} to the {@link BasicLibrary} -- the LUID <b>must</b>
734 * be correct, or NULL to get the next free one.
735 * <p>
736 * Will override any previous {@link Story} with the same LUID.
737 *
738 * @param story
739 * the {@link Story} to save
740 * @param luid
741 * the <b>correct</b> LUID or NULL to get the next free one
742 * @param pg
743 * the optional progress reporter
744 *
745 * @return the same {@link Story}, whose LUID may have changed
746 *
747 * @throws IOException
748 * in case of I/O error
749 */
750 public synchronized Story save(Story story, String luid, Progress pg)
751 throws IOException {
3b039231
NR
752 if (pg == null) {
753 pg = new Progress();
754 }
dc919036
NR
755
756 Instance.getInstance().getTraceHandler().trace(
757 this.getClass().getSimpleName() + ": saving story " + luid);
9e2fad36 758
68e2c6d2
NR
759 // Do not change the original metadata, but change the original story
760 MetaData meta = story.getMeta().clone();
761 story.setMeta(meta);
762
3b039231 763 pg.setName("Saving story");
dc919036 764
68e2c6d2 765 if (luid == null || luid.isEmpty()) {
f70bcacf 766 meta.setLuid(getNextId());
68e2c6d2
NR
767 } else {
768 meta.setLuid(luid);
769 }
770
e272f05f 771 if (luid != null && getInfo(luid) != null) {
68e2c6d2
NR
772 delete(luid);
773 }
14b57448 774
efa3c511 775 story = doSave(story, pg);
0bf8264e 776
efa3c511 777 updateInfo(story.getMeta());
68e2c6d2 778
d66deb8d 779 Instance.getInstance().getTraceHandler()
dc919036
NR
780 .trace(this.getClass().getSimpleName() + ": story saved ("
781 + luid + ")");
9e2fad36 782
3b039231
NR
783 pg.setName(meta.getTitle());
784 pg.done();
68e2c6d2
NR
785 return story;
786 }
787
788 /**
789 * Delete the given {@link Story} from this {@link BasicLibrary}.
790 *
791 * @param luid
792 * the LUID of the target {@link Story}
793 *
794 * @throws IOException
795 * in case of I/O error
796 */
797 public synchronized void delete(String luid) throws IOException {
dc919036
NR
798 Instance.getInstance().getTraceHandler().trace(
799 this.getClass().getSimpleName() + ": deleting story " + luid);
9e2fad36 800
68e2c6d2 801 doDelete(luid);
c8d48938 802 invalidateInfo(luid);
9e2fad36 803
d66deb8d 804 Instance.getInstance().getTraceHandler()
dc919036
NR
805 .trace(this.getClass().getSimpleName() + ": story deleted ("
806 + luid + ")");
68e2c6d2
NR
807 }
808
809 /**
810 * Change the type (source) of the given {@link Story}.
811 *
812 * @param luid
813 * the {@link Story} LUID
814 * @param newSource
815 * the new source
816 * @param pg
817 * the optional progress reporter
818 *
819 * @throws IOException
820 * in case of I/O error or if the {@link Story} was not found
821 */
822 public synchronized void changeSource(String luid, String newSource,
823 Progress pg) throws IOException {
824 MetaData meta = getInfo(luid);
825 if (meta == null) {
826 throw new IOException("Story not found: " + luid);
827 }
828
c8d48938
NR
829 changeSTA(luid, newSource, meta.getTitle(), meta.getAuthor(), pg);
830 }
831
832 /**
833 * Change the title (name) of the given {@link Story}.
834 *
835 * @param luid
836 * the {@link Story} LUID
837 * @param newTitle
838 * the new title
839 * @param pg
840 * the optional progress reporter
841 *
842 * @throws IOException
843 * in case of I/O error or if the {@link Story} was not found
844 */
845 public synchronized void changeTitle(String luid, String newTitle,
846 Progress pg) throws IOException {
847 MetaData meta = getInfo(luid);
848 if (meta == null) {
849 throw new IOException("Story not found: " + luid);
850 }
851
852 changeSTA(luid, meta.getSource(), newTitle, meta.getAuthor(), pg);
853 }
854
855 /**
856 * Change the author of the given {@link Story}.
857 *
858 * @param luid
859 * the {@link Story} LUID
860 * @param newAuthor
861 * the new author
862 * @param pg
863 * the optional progress reporter
864 *
865 * @throws IOException
866 * in case of I/O error or if the {@link Story} was not found
867 */
868 public synchronized void changeAuthor(String luid, String newAuthor,
869 Progress pg) throws IOException {
870 MetaData meta = getInfo(luid);
871 if (meta == null) {
872 throw new IOException("Story not found: " + luid);
873 }
874
875 changeSTA(luid, meta.getSource(), meta.getTitle(), newAuthor, pg);
876 }
877
878 /**
879 * Change the Source, Title and Author of the {@link Story} in one single
880 * go.
881 *
882 * @param luid
883 * the {@link Story} LUID
884 * @param newSource
885 * the new source
886 * @param newTitle
887 * the new title
888 * @param newAuthor
889 * the new author
890 * @param pg
891 * the optional progress reporter
892 *
893 * @throws IOException
894 * in case of I/O error or if the {@link Story} was not found
895 */
896 protected synchronized void changeSTA(String luid, String newSource,
897 String newTitle, String newAuthor, Progress pg) throws IOException {
898 MetaData meta = getInfo(luid);
899 if (meta == null) {
900 throw new IOException("Story not found: " + luid);
901 }
902
68e2c6d2 903 meta.setSource(newSource);
c8d48938
NR
904 meta.setTitle(newTitle);
905 meta.setAuthor(newAuthor);
68e2c6d2
NR
906 saveMeta(meta, pg);
907 }
908
909 /**
910 * Save back the current state of the {@link MetaData} (LUID <b>MUST NOT</b>
911 * change) for this {@link Story}.
912 * <p>
913 * By default, delete the old {@link Story} then recreate a new
914 * {@link Story}.
915 * <p>
c8d48938 916 * Note that this behaviour can lead to data loss in case of problems!
68e2c6d2
NR
917 *
918 * @param meta
919 * the new {@link MetaData} (LUID <b>MUST NOT</b> change)
920 * @param pg
921 * the optional {@link Progress}
922 *
923 * @throws IOException
924 * in case of I/O error or if the {@link Story} was not found
925 */
926 protected synchronized void saveMeta(MetaData meta, Progress pg)
927 throws IOException {
928 if (pg == null) {
929 pg = new Progress();
930 }
931
932 Progress pgGet = new Progress();
933 Progress pgSet = new Progress();
934 pg.addProgress(pgGet, 50);
935 pg.addProgress(pgSet, 50);
936
937 Story story = getStory(meta.getLuid(), pgGet);
938 if (story == null) {
939 throw new IOException("Story not found: " + meta.getLuid());
940 }
941
c8d48938 942 // TODO: this is not safe!
68e2c6d2 943 delete(meta.getLuid());
68e2c6d2
NR
944 story.setMeta(meta);
945 save(story, meta.getLuid(), pgSet);
946
947 pg.done();
948 }
599b05c7
NR
949
950 /**
951 * Describe a {@link Story} from its {@link MetaData} and return a list of
952 * title/value that represent this {@link Story}.
953 *
954 * @param meta
955 * the {@link MetaData} to represent
956 *
957 * @return the information, translated and sorted
958 */
959 static public Map<String, String> getMetaDesc(MetaData meta) {
960 Map<String, String> metaDesc = new LinkedHashMap<String, String>();
961
962 // TODO: i18n
963
964 StringBuilder tags = new StringBuilder();
965 for (String tag : meta.getTags()) {
966 if (tags.length() > 0) {
967 tags.append(", ");
968 }
969 tags.append(tag);
970 }
971
972 // TODO: i18n
973 metaDesc.put("Author", meta.getAuthor());
974 metaDesc.put("Published on", meta.getPublisher());
975 metaDesc.put("Publication date", meta.getDate());
976 metaDesc.put("Creation date", meta.getCreationDate());
977 String count = "";
978 if (meta.getWords() > 0) {
979 count = StringUtils.formatNumber(meta.getWords());
980 }
981 if (meta.isImageDocument()) {
982 metaDesc.put("Number of images", count);
983 } else {
984 metaDesc.put("Number of words", count);
985 }
986 metaDesc.put("Source", meta.getSource());
987 metaDesc.put("Subject", meta.getSubject());
988 metaDesc.put("Language", meta.getLang());
989 metaDesc.put("Tags", tags.toString());
990 metaDesc.put("URL", meta.getUrl());
991
992 return metaDesc;
993 }
68e2c6d2 994}