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