Version 1.1.0
[fanfix.git] / src / be / nikiroo / fanfix / Library.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix;
2
3import java.io.File;
4import java.io.IOException;
5import java.net.URL;
6import java.util.ArrayList;
7import java.util.HashMap;
8import java.util.List;
9import java.util.Map;
10import java.util.Map.Entry;
11
08fe2e33
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;
92fb0719 17import be.nikiroo.utils.ui.Progress;
08fe2e33 18import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
68686a37 19import be.nikiroo.fanfix.supported.InfoReader;
08fe2e33
NR
20
21/**
22 * Manage a library of Stories: import, export, list.
23 * <p>
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}.
26 *
27 * @author niki
28 */
29public class Library {
30 private File baseDir;
31 private Map<MetaData, File> stories;
08fe2e33 32 private int lastId;
2206ef66
NR
33 private OutputType text;
34 private OutputType image;
08fe2e33
NR
35
36 /**
37 * Create a new {@link Library} with the given backend directory.
38 *
39 * @param dir
2206ef66
NR
40 * the directory where to find the {@link Story} objects
41 * @param text
42 * the {@link OutputType} to save the text-focused stories into
43 * @param image
44 * the {@link OutputType} to save the images-focused stories into
08fe2e33 45 */
2206ef66 46 public Library(File dir, OutputType text, OutputType image) {
08fe2e33
NR
47 this.baseDir = dir;
48 this.stories = new HashMap<MetaData, File>();
49 this.lastId = 0;
2206ef66
NR
50 this.text = text;
51 this.image = image;
08fe2e33
NR
52
53 dir.mkdirs();
54 }
55
333f0e7b
NR
56 /**
57 * List all the known types of stories.
58 *
59 * @return the types
60 */
61 public List<String> getTypes() {
62 List<String> list = new ArrayList<String>();
63 for (Entry<MetaData, File> entry : getStories().entrySet()) {
64 String storyType = entry.getValue().getParentFile().getName();
65 if (!list.contains(storyType)) {
66 list.add(storyType);
67 }
68 }
69
70 return list;
71 }
72
08fe2e33
NR
73 /**
74 * List all the stories of the given source type in the {@link Library}, or
75 * all the stories if NULL is passed as a type.
76 *
77 * @param type
78 * the type of story to retrieve, or NULL for all
79 *
80 * @return the stories
81 */
333f0e7b 82 public List<MetaData> getList(String type) {
08fe2e33
NR
83 List<MetaData> list = new ArrayList<MetaData>();
84 for (Entry<MetaData, File> entry : getStories().entrySet()) {
85 String storyType = entry.getValue().getParentFile().getName();
333f0e7b 86 if (type == null || type.equalsIgnoreCase(storyType)) {
08fe2e33
NR
87 list.add(entry.getKey());
88 }
89 }
90
91 return list;
92 }
93
301791d3
NR
94 /**
95 * Retrieve a {@link File} corresponding to the given {@link Story}.
96 *
97 * @param luid
98 * the Library UID of the story
99 *
100 * @return the corresponding {@link Story}
101 */
102 public MetaData getInfo(String luid) {
103 if (luid != null) {
104 for (Entry<MetaData, File> entry : getStories().entrySet()) {
105 if (luid.equals(entry.getKey().getLuid())) {
106 return entry.getKey();
107 }
108 }
109 }
110
111 return null;
112 }
113
2206ef66
NR
114 /**
115 * Retrieve a {@link File} corresponding to the given {@link Story}.
116 *
117 * @param luid
118 * the Library UID of the story
119 *
120 * @return the corresponding {@link Story}
121 */
122 public File getFile(String luid) {
123 if (luid != null) {
124 for (Entry<MetaData, File> entry : getStories().entrySet()) {
125 if (luid.equals(entry.getKey().getLuid())) {
126 return entry.getValue();
127 }
128 }
129 }
130
131 return null;
132 }
133
08fe2e33
NR
134 /**
135 * Retrieve a specific {@link Story}.
136 *
137 * @param luid
138 * the Library UID of the story
92fb0719
NR
139 * @param pg
140 * the optional progress reporter
08fe2e33 141 *
3d247bc3 142 * @return the corresponding {@link Story} or NULL if not found
08fe2e33 143 */
92fb0719 144 public Story getStory(String luid, Progress pg) {
08fe2e33
NR
145 if (luid != null) {
146 for (Entry<MetaData, File> entry : getStories().entrySet()) {
147 if (luid.equals(entry.getKey().getLuid())) {
148 try {
fe999aa4
NR
149 SupportType type = SupportType.valueOfAllOkUC(entry
150 .getKey().getType());
151 URL url = entry.getValue().toURI().toURL();
152 if (type != null) {
92fb0719
NR
153 return BasicSupport.getSupport(type).process(url,
154 pg);
fe999aa4
NR
155 } else {
156 throw new IOException("Unknown type: "
157 + entry.getKey().getType());
158 }
08fe2e33
NR
159 } catch (IOException e) {
160 // We should not have not-supported files in the
161 // library
162 Instance.syserr(new IOException(
163 "Cannot load file from library: "
164 + entry.getValue().getPath(), e));
165 }
166 }
167 }
168 }
169
92fb0719
NR
170 if (pg != null) {
171 pg.setMinMax(0, 1);
172 pg.setProgress(1);
173 }
174
08fe2e33
NR
175 return null;
176 }
177
178 /**
179 * Import the {@link Story} at the given {@link URL} into the
180 * {@link Library}.
181 *
182 * @param url
183 * the {@link URL} to import
92fb0719
NR
184 * @param pg
185 * the optional progress reporter
08fe2e33
NR
186 *
187 * @return the imported {@link Story}
188 *
189 * @throws IOException
190 * in case of I/O error
191 */
92fb0719 192 public Story imprt(URL url, Progress pg) throws IOException {
08fe2e33
NR
193 BasicSupport support = BasicSupport.getSupport(url);
194 if (support == null) {
195 throw new IOException("URL not supported: " + url.toString());
196 }
197
92fb0719 198 return save(support.process(url, pg), null);
08fe2e33
NR
199 }
200
201 /**
202 * Export the {@link Story} to the given target in the given format.
203 *
204 * @param luid
205 * the {@link Story} ID
206 * @param type
207 * the {@link OutputType} to transform it to
208 * @param target
209 * the target to save to
92fb0719
NR
210 * @param pg
211 * the optional progress reporter
08fe2e33
NR
212 *
213 * @return the saved resource (the main saved {@link File})
214 *
215 * @throws IOException
216 * in case of I/O error
217 */
92fb0719 218 public File export(String luid, OutputType type, String target, Progress pg)
08fe2e33
NR
219 throws IOException {
220 BasicOutput out = BasicOutput.getOutput(type, true);
221 if (out == null) {
222 throw new IOException("Output type not supported: " + type);
223 }
224
92fb0719 225 Story story = getStory(luid, pg);
73ce17ef
NR
226 if (story == null) {
227 throw new IOException("Cannot find story to export: " + luid);
228 }
229
92fb0719 230 return out.process(story, target);
08fe2e33
NR
231 }
232
233 /**
2206ef66
NR
234 * Save a {@link Story} to the {@link Library}.
235 *
236 * @param story
237 * the {@link Story} to save
238 *
239 * @return the same {@link Story}, whose LUID may have changed
240 *
241 * @throws IOException
242 * in case of I/O error
243 */
244 public Story save(Story story) throws IOException {
245 return save(story, null);
246 }
247
248 /**
249 * Save a {@link Story} to the {@link Library} -- the LUID <b>must</b> be
250 * correct, or NULL to get the next free one.
08fe2e33
NR
251 *
252 * @param story
253 * the {@link Story} to save
2206ef66
NR
254 * @param luid
255 * the <b>correct</b> LUID or NULL to get the next free one
256 *
257 * @return the same {@link Story}, whose LUID may have changed
08fe2e33
NR
258 *
259 * @throws IOException
260 * in case of I/O error
261 */
b4dc6ab5 262 public Story save(Story story, String luid) throws IOException {
a7d266e6 263 // Do not change the original metadata, but change the original story
301791d3 264 MetaData key = story.getMeta().clone();
a7d266e6 265 story.setMeta(key);
08fe2e33 266
2206ef66
NR
267 if (luid == null || luid.isEmpty()) {
268 getStories(); // refresh lastId if needed
269 key.setLuid(String.format("%03d", (++lastId)));
270 } else {
271 key.setLuid(luid);
272 }
273
08fe2e33
NR
274 getDir(key).mkdirs();
275 if (!getDir(key).exists()) {
276 throw new IOException("Cannot create library dir");
277 }
278
279 OutputType out;
08fe2e33 280 if (key != null && key.isImageDocument()) {
2206ef66 281 out = image;
08fe2e33 282 } else {
2206ef66 283 out = text;
08fe2e33 284 }
2206ef66 285
08fe2e33
NR
286 BasicOutput it = BasicOutput.getOutput(out, true);
287 File file = it.process(story, getFile(key).getPath());
2206ef66
NR
288 getStories().put(story.getMeta(), file);
289
290 return story;
08fe2e33
NR
291 }
292
293 /**
294 * The directory (full path) where the {@link Story} related to this
295 * {@link MetaData} should be located on disk.
296 *
297 * @param key
298 * the {@link Story} {@link MetaData}
299 *
300 * @return the target directory
301 */
302 private File getDir(MetaData key) {
303 String source = key.getSource().replaceAll("[^a-zA-Z0-9._+-]", "_");
304 return new File(baseDir, source);
305 }
306
307 /**
308 * The target (full path) where the {@link Story} related to this
309 * {@link MetaData} should be located on disk.
310 *
311 * @param key
312 * the {@link Story} {@link MetaData}
313 *
314 * @return the target
315 */
316 private File getFile(MetaData key) {
317 String title = key.getTitle().replaceAll("[^a-zA-Z0-9._+-]", "_");
318 return new File(getDir(key), key.getLuid() + "_" + title);
319 }
320
321 /**
322 * Return all the known stories in this {@link Library} object.
323 *
324 * @return the stories
325 */
326 private Map<MetaData, File> getStories() {
327 if (stories.isEmpty()) {
328 lastId = 0;
68686a37 329
2206ef66 330 String ext = ".info";
08fe2e33
NR
331 for (File dir : baseDir.listFiles()) {
332 if (dir.isDirectory()) {
333 for (File file : dir.listFiles()) {
334 try {
2206ef66 335 if (file.getPath().toLowerCase().endsWith(ext)) {
68686a37
NR
336 MetaData meta = InfoReader.readMeta(file);
337 try {
338 int id = Integer.parseInt(meta.getLuid());
339 if (id > lastId) {
340 lastId = id;
08fe2e33 341 }
68686a37 342
2206ef66
NR
343 // Replace .info with whatever is needed:
344 String path = file.getPath();
345 path = path.substring(0, path.length()
346 - ext.length());
347
348 String newExt = getOutputType(meta)
349 .getDefaultExtension();
350
351 file = new File(path + newExt);
352 //
353
68686a37
NR
354 stories.put(meta, file);
355
356 } catch (Exception e) {
08fe2e33
NR
357 // not normal!!
358 Instance.syserr(new IOException(
68686a37
NR
359 "Cannot understand the LUID of "
360 + file.getPath() + ": "
361 + meta.getLuid(), e));
08fe2e33
NR
362 }
363 }
364 } catch (IOException e) {
365 // We should not have not-supported files in the
366 // library
367 Instance.syserr(new IOException(
368 "Cannot load file from library: "
369 + file.getPath(), e));
370 }
371 }
372 }
373 }
374 }
375
376 return stories;
377 }
2206ef66
NR
378
379 /**
380 * Return the {@link OutputType} for this {@link Story}.
381 *
382 * @param meta
383 * the {@link Story} {@link MetaData}
384 *
385 * @return the type
386 */
387 private OutputType getOutputType(MetaData meta) {
388 if (meta != null && meta.isImageDocument()) {
389 return image;
390 } else {
391 return text;
392 }
393 }
08fe2e33 394}