Merge branch 'subtree'
[fanfix.git] / src / be / nikiroo / fanfix / data / MetaData.java
1 package be.nikiroo.fanfix.data;
2
3 import java.io.Serializable;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import be.nikiroo.fanfix.supported.SupportType;
8 import be.nikiroo.utils.Image;
9 import be.nikiroo.utils.StringUtils;
10
11 /**
12 * The meta data associated to a {@link Story} object.
13 * <p>
14 * Note that some earlier version of the program did not save the resume as an
15 * external file; for those stories, the resume is not fetched until the story
16 * is.
17 * <p>
18 * The cover is never fetched until the story is.
19 *
20 * @author niki
21 */
22 public class MetaData implements Cloneable, Comparable<MetaData>, Serializable {
23 private static final long serialVersionUID = 1L;
24
25 private String title;
26 private String author;
27 private String date;
28 private Chapter resume;
29 private List<String> tags;
30 private Image cover;
31 private String subject;
32 private String source;
33 private String url;
34 private String uuid;
35 private String luid;
36 private String lang;
37 private String publisher;
38 private String type;
39 private boolean imageDocument;
40 private long words;
41 private String creationDate;
42 private boolean fakeCover;
43
44 /**
45 * Create an empty {@link MetaData}.
46 */
47 public MetaData() {
48 }
49
50 /**
51 * The title of the story.
52 *
53 * @return the title
54 */
55 public String getTitle() {
56 return title;
57 }
58
59 /**
60 * The title of the story.
61 *
62 * @param title
63 * the title to set
64 */
65 public void setTitle(String title) {
66 this.title = title;
67 }
68
69 /**
70 * The author of the story.
71 *
72 * @return the author
73 */
74 public String getAuthor() {
75 return author;
76 }
77
78 /**
79 * The author of the story.
80 *
81 * @param author
82 * the author to set
83 */
84 public void setAuthor(String author) {
85 this.author = author;
86 }
87
88 /**
89 * The story publication date, we try to use "YYYY-mm-dd" when possible.
90 *
91 * @return the date
92 */
93 public String getDate() {
94 return date;
95 }
96
97 /**
98 * The story publication date, we try to use "YYYY-mm-dd" when possible.
99 *
100 * @param date
101 * the date to set
102 */
103 public void setDate(String date) {
104 this.date = date;
105 }
106
107 /**
108 * The tags associated with this story.
109 *
110 * @return the tags
111 */
112 public List<String> getTags() {
113 return tags;
114 }
115
116 /**
117 * The tags associated with this story.
118 *
119 * @param tags
120 * the tags to set
121 */
122 public void setTags(List<String> tags) {
123 this.tags = tags;
124 }
125
126 /**
127 * The story resume (a.k.a. description).
128 * <p>
129 * This can be NULL if we don't have a resume for this {@link Story}.
130 * <p>
131 * Note that some earlier version of the program did not save the resume as
132 * an external file; for those stories, the resume is not fetched until the
133 * story is.
134 *
135 * @return the resume
136 */
137 public Chapter getResume() {
138 return resume;
139 }
140
141 /**
142 * The story resume (a.k.a. description).
143 * <p>
144 * Note that some earlier version of the program did not save the resume as
145 * an external file; for those stories, the resume is not fetched until the
146 * story is.
147 *
148 * @param resume
149 * the resume to set
150 */
151 public void setResume(Chapter resume) {
152 this.resume = resume;
153 }
154
155 /**
156 * The cover image of the story, if any (can be NULL).
157 * <p>
158 * The cover is not fetched until the story is.
159 *
160 * @return the cover
161 */
162 public Image getCover() {
163 return cover;
164 }
165
166 /**
167 * The cover image of the story, if any (can be NULL).
168 * <p>
169 * The cover is not fetched until the story is.
170 *
171 * @param cover
172 * the cover to set
173 */
174 public void setCover(Image cover) {
175 this.cover = cover;
176 }
177
178 /**
179 * The subject of the story (for instance, if it is a fanfiction, what is the
180 * original work; if it is a technical text, what is the technical
181 * subject...).
182 *
183 * @return the subject
184 */
185 public String getSubject() {
186 return subject;
187 }
188
189 /**
190 * The subject of the story (for instance, if it is a fanfiction, what is
191 * the original work; if it is a technical text, what is the technical
192 * subject...).
193 *
194 * @param subject
195 * the subject to set
196 */
197 public void setSubject(String subject) {
198 this.subject = subject;
199 }
200
201 /**
202 * The source of this story -- a very user-visible piece of data.
203 * <p>
204 * It is initialised with the same value as {@link MetaData#getPublisher()},
205 * but the user is allowed to change it into any value -- this is a sort of
206 * 'category'.
207 *
208 * @return the source
209 */
210 public String getSource() {
211 return source;
212 }
213
214 /**
215 * The source of this story -- a very user-visible piece of data.
216 * <p>
217 * It is initialised with the same value as {@link MetaData#getPublisher()},
218 * but the user is allowed to change it into any value -- this is a sort of
219 * 'category'.
220 *
221 * @param source
222 * the source to set
223 */
224 public void setSource(String source) {
225 this.source = source;
226 }
227
228 /**
229 * The original URL from which this {@link Story} was imported.
230 *
231 * @return the url
232 */
233 public String getUrl() {
234 return url;
235 }
236
237 /**
238 * The original URL from which this {@link Story} was imported.
239 *
240 * @param url
241 * the new url to set
242 */
243 public void setUrl(String url) {
244 this.url = url;
245 }
246
247 /**
248 * A unique value representing the story (it is often a URL).
249 *
250 * @return the uuid
251 */
252 public String getUuid() {
253 return uuid;
254 }
255
256 /**
257 * A unique value representing the story (it is often a URL).
258 *
259 * @param uuid
260 * the uuid to set
261 */
262 public void setUuid(String uuid) {
263 this.uuid = uuid;
264 }
265
266 /**
267 * A unique value representing the story in the local library (usually a
268 * numerical value 0-padded with a minimum size of 3; but this is subject to
269 * change and you can also obviously have more than 1000 stories --
270 * <strong>a luid may potentially be anything else, including non-numeric
271 * characters</strong>).
272 * <p>
273 * A NULL or empty luid represents an incomplete, corrupted or fake
274 * {@link Story}.
275 *
276 * @return the luid
277 */
278 public String getLuid() {
279 return luid;
280 }
281
282 /**
283 * A unique value representing the story in the local library (usually a
284 * numerical value 0-padded with a minimum size of 3; but this is subject to
285 * change and you can also obviously have more than 1000 stories --
286 * <strong>a luid may potentially be anything else, including non-numeric
287 * characters</strong>).
288 * <p>
289 * A NULL or empty luid represents an incomplete, corrupted or fake
290 * {@link Story}.
291 *
292 * @param luid
293 * the luid to set
294 */
295 public void setLuid(String luid) {
296 this.luid = luid;
297 }
298
299 /**
300 * The 2-letter code language of this story.
301 *
302 * @return the lang
303 */
304 public String getLang() {
305 return lang;
306 }
307
308 /**
309 * The 2-letter code language of this story.
310 *
311 * @param lang
312 * the lang to set
313 */
314 public void setLang(String lang) {
315 this.lang = lang;
316 }
317
318 /**
319 * The story publisher -- which is also the user representation of the
320 * output type this {@link Story} is in (see {@link SupportType}).
321 * <p>
322 * It allows you to know where the {@link Story} comes from, and is not
323 * supposed to change, even when re-imported.
324 * <p>
325 * It's the user representation of the enum
326 * ({@link SupportType#getSourceName()}, not
327 * {@link SupportType#toString()}).
328 *
329 * @return the publisher
330 */
331 public String getPublisher() {
332 return publisher;
333 }
334
335 /**
336 * The story publisher -- which is also the user representation of the
337 * output type this {@link Story} is in (see {@link SupportType}).
338 * <p>
339 * It allows you to know where the {@link Story} comes from, and is not
340 * supposed to change, even when re-imported.
341 * <p>
342 * It's the user representation of the enum
343 * ({@link SupportType#getSourceName()}, not
344 * {@link SupportType#toString()}).
345 *
346 * @param publisher
347 * the publisher to set
348 */
349 public void setPublisher(String publisher) {
350 this.publisher = publisher;
351 }
352
353 /**
354 * The output type this {@link Story} is in (see {@link SupportType}).
355 * <p>
356 * It allows you to know where the {@link Story} comes from, and is supposed
357 * to only change when it is imported anew.
358 * <p>
359 * It's the direct representation of the enum
360 * ({@link SupportType#toString()}, not
361 * {@link SupportType#getSourceName()}).
362 *
363 * @return the type the type
364 */
365 public String getType() {
366 return type;
367 }
368
369 /**
370 * The output type this {@link Story} is in (see {@link SupportType}).
371 * <p>
372 * It allows you to know where the {@link Story} comes from, and is supposed
373 * to only change when it is imported anew.
374 * <p>
375 * It's the direct representation of the enum
376 * ({@link SupportType#toString()}, not
377 * {@link SupportType#getSourceName()}).
378 *
379 * @param type
380 * the new type to set
381 */
382 public void setType(String type) {
383 this.type = type;
384 }
385
386 /**
387 * Document catering mostly to image files.
388 * <p>
389 * I.E., this is a comics or a manga, not a textual story with actual words.
390 * <p>
391 * In image documents, all the paragraphs are supposed to be images.
392 *
393 * @return the imageDocument state
394 */
395 public boolean isImageDocument() {
396 return imageDocument;
397 }
398
399 /**
400 * Document catering mostly to image files.
401 * <p>
402 * I.E., this is a comics or a manga, not a textual story with actual words.
403 * <p>
404 * In image documents, all the paragraphs are supposed to be images.
405 *
406 * @param imageDocument
407 * the imageDocument state to set
408 */
409 public void setImageDocument(boolean imageDocument) {
410 this.imageDocument = imageDocument;
411 }
412
413 /**
414 * The number of words (or images if this is an image document -- see
415 * {@link MetaData#isImageDocument()}) in the related {@link Story}.
416 *
417 * @return the number of words/images
418 */
419 public long getWords() {
420 return words;
421 }
422
423 /**
424 * The number of words (or images if this is an image document -- see
425 * {@link MetaData#isImageDocument()}) in the related {@link Story}.
426 *
427 * @param words
428 * the number of words/images to set
429 */
430 public void setWords(long words) {
431 this.words = words;
432 }
433
434 /**
435 * The (Fanfix) {@link Story} creation date, i.e., when the {@link Story}
436 * was fetched via Fanfix.
437 *
438 * @return the creation date
439 */
440 public String getCreationDate() {
441 return creationDate;
442 }
443
444 /**
445 * The (Fanfix) {@link Story} creation date, i.e., when the {@link Story}
446 * was fetched via Fanfix.
447 *
448 * @param creationDate
449 * the creation date to set
450 */
451 public void setCreationDate(String creationDate) {
452 this.creationDate = creationDate;
453 }
454
455 /**
456 * The cover in this {@link MetaData} object is "fake", in the sense that it
457 * comes from the actual content images.
458 *
459 * @return TRUE for a fake cover
460 */
461 public boolean isFakeCover() {
462 return fakeCover;
463 }
464
465 /**
466 * The cover in this {@link MetaData} object is "fake", in the sense that it
467 * comes from the actual content images
468 *
469 * @param fakeCover
470 * TRUE for a fake cover
471 */
472 public void setFakeCover(boolean fakeCover) {
473 this.fakeCover = fakeCover;
474 }
475
476 @Override
477 public int compareTo(MetaData o) {
478 if (o == null) {
479 return 1;
480 }
481
482 String id = (getTitle() == null ? "" : getTitle())
483 + (getUuid() == null ? "" : getUuid())
484 + (getLuid() == null ? "" : getLuid());
485 String oId = (getTitle() == null ? "" : o.getTitle())
486 + (getUuid() == null ? "" : o.getUuid())
487 + (o.getLuid() == null ? "" : o.getLuid());
488
489 return id.compareToIgnoreCase(oId);
490 }
491
492 @Override
493 public boolean equals(Object obj) {
494 if (!(obj instanceof MetaData)) {
495 return false;
496 }
497
498 return compareTo((MetaData) obj) == 0;
499 }
500
501 @Override
502 public int hashCode() {
503 String uuid = getUuid();
504 if (uuid == null) {
505 uuid = "" + title + author + source;
506 }
507
508 return uuid.hashCode();
509 }
510
511 @Override
512 public MetaData clone() {
513 MetaData meta = null;
514 try {
515 meta = (MetaData) super.clone();
516 } catch (CloneNotSupportedException e) {
517 // Did the clones rebel?
518 System.err.println(e);
519 }
520
521 if (tags != null) {
522 meta.tags = new ArrayList<String>(tags);
523 }
524
525 if (resume != null) {
526 meta.resume = resume.clone();
527 }
528
529 return meta;
530 }
531
532 /**
533 * Display a DEBUG {@link String} representation of this object.
534 * <p>
535 * This is not efficient, nor intended to be.
536 */
537 @Override
538 public String toString() {
539 String title = "";
540 if (getTitle() != null) {
541 title = getTitle();
542 }
543
544 StringBuilder tags = new StringBuilder();
545 if (getTags() != null) {
546 for (String tag : getTags()) {
547 if (tags.length() > 0) {
548 tags.append(", ");
549 }
550 tags.append(tag);
551 }
552 }
553
554 String resume = "";
555 if (getResume() != null) {
556 for (Paragraph para : getResume()) {
557 resume += "\n\t";
558 resume += para.toString().substring(0,
559 Math.min(para.toString().length(), 120));
560 }
561 resume += "\n";
562 }
563
564 String cover = "none";
565 if (getCover() != null) {
566 cover = StringUtils.formatNumber(getCover().getSize())
567 + "bytes";
568 }
569
570 return String.format(
571 "Meta %s:\n\tTitle: [%s]\n\tAuthor: [%s]\n\tDate: [%s]\n\tTags: [%s]\n\tWord count: [%s]"
572 + "\n\tResume: [%s]\n\tCover: [%s]",
573 luid, title, getAuthor(), getDate(), tags.toString(),
574 "" + words, resume, cover);
575 }
576 }