Add more warnings source to 1.6) and fix warnings
[nikiroo-utils.git] / src / be / nikiroo / fanfix / library / LocalLibrary.java
CommitLineData
e42573a0 1package be.nikiroo.fanfix.library;
68e2c6d2
NR
2
3import java.awt.image.BufferedImage;
4import java.io.File;
5import java.io.FileFilter;
6import java.io.IOException;
7import java.util.ArrayList;
8import java.util.HashMap;
9import java.util.List;
10import java.util.Map;
11
e42573a0 12import be.nikiroo.fanfix.Instance;
68e2c6d2
NR
13import be.nikiroo.fanfix.bundles.Config;
14import be.nikiroo.fanfix.data.MetaData;
15import be.nikiroo.fanfix.data.Story;
16import be.nikiroo.fanfix.output.BasicOutput;
17import be.nikiroo.fanfix.output.BasicOutput.OutputType;
18import be.nikiroo.fanfix.output.InfoCover;
19import be.nikiroo.fanfix.supported.InfoReader;
20import be.nikiroo.utils.IOUtils;
21import be.nikiroo.utils.Progress;
22
23/**
24 * This {@link BasicLibrary} will store the stories locally on disk.
25 *
26 * @author niki
27 */
28public class LocalLibrary extends BasicLibrary {
29 private int lastId;
30 private Map<MetaData, File[]> stories; // Files: [ infoFile, TargetFile ]
31
32 private File baseDir;
33 private OutputType text;
34 private OutputType image;
35
36 /**
37 * Create a new {@link LocalLibrary} with the given back-end directory.
38 *
39 * @param baseDir
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
45 */
46 public LocalLibrary(File baseDir, OutputType text, OutputType image) {
47 this.baseDir = baseDir;
48 this.text = text;
49 this.image = image;
50
51 this.lastId = 0;
52 this.stories = null;
53
54 baseDir.mkdirs();
55 }
56
57 @Override
58 protected List<MetaData> getMetas(Progress pg) {
59 return new ArrayList<MetaData>(getStories(pg).keySet());
60 }
61
62 @Override
63 public File getFile(String luid) {
64 File[] files = getStories(null).get(getInfo(luid));
65 if (files != null) {
66 return files[1];
67 }
68
69 return null;
70 }
71
72 @Override
73 public BufferedImage getCover(String luid) {
74 MetaData meta = getInfo(luid);
75 if (meta != null) {
76 File[] files = getStories(null).get(meta);
77 if (files != null) {
78 File infoFile = files[0];
79
80 try {
81 meta = InfoReader.readMeta(infoFile, true);
82 return meta.getCover();
83 } catch (IOException e) {
84 Instance.syserr(e);
85 }
86 }
87 }
88
89 return null;
90 }
91
92 @Override
93 protected void clearCache() {
94 stories = null;
95 }
96
97 @Override
98 protected synchronized int getNextId() {
99 return ++lastId;
100 }
101
102 @Override
103 protected void doDelete(String luid) throws IOException {
104 for (File file : getRelatedFiles(luid)) {
105 // TODO: throw an IOException if we cannot delete the files?
106 IOUtils.deltree(file);
107 }
108 }
109
110 @Override
111 protected Story doSave(Story story, Progress pg) throws IOException {
112 MetaData meta = story.getMeta();
113
114 File expectedTarget = getExpectedFile(meta);
115 expectedTarget.getParentFile().mkdirs();
116
117 BasicOutput it = BasicOutput.getOutput(getOutputType(meta), true);
118 it.process(story, expectedTarget.getPath(), pg);
119
120 return story;
121 }
122
123 @Override
124 protected synchronized void saveMeta(MetaData meta, Progress pg)
125 throws IOException {
126 File newDir = getExpectedDir(meta.getSource());
127 if (!newDir.exists()) {
128 newDir.mkdir();
129 }
130
131 List<File> relatedFiles = getRelatedFiles(meta.getLuid());
132 for (File relatedFile : relatedFiles) {
133 // TODO: this is not safe at all.
134 // We should copy all the files THEN delete them
135 // Maybe also adding some rollback cleanup if possible
136 if (relatedFile.getName().endsWith(".info")) {
137 try {
138 String name = relatedFile.getName().replaceFirst(
139 "\\.info$", "");
140 InfoCover.writeInfo(newDir, name, meta);
141 relatedFile.delete();
142 } catch (IOException e) {
143 Instance.syserr(e);
144 }
145 } else {
146 relatedFile.renameTo(new File(newDir, relatedFile.getName()));
147 }
148 }
149
150 clearCache();
151 }
152
153 /**
154 * Return the {@link OutputType} for this {@link Story}.
155 *
156 * @param meta
157 * the {@link Story} {@link MetaData}
158 *
159 * @return the type
160 */
161 private OutputType getOutputType(MetaData meta) {
162 if (meta != null && meta.isImageDocument()) {
163 return image;
68e2c6d2 164 }
211f7ddb
NR
165
166 return text;
68e2c6d2
NR
167 }
168
169 /**
170 * Get the target {@link File} related to the given <tt>.info</tt>
171 * {@link File} and {@link MetaData}.
172 *
173 * @param meta
174 * the meta
175 * @param infoFile
176 * the <tt>.info</tt> {@link File}
177 *
178 * @return the target {@link File}
179 */
180 private File getTargetFile(MetaData meta, File infoFile) {
181 // Replace .info with whatever is needed:
182 String path = infoFile.getPath();
183 path = path.substring(0, path.length() - ".info".length());
184 String newExt = getOutputType(meta).getDefaultExtension(true);
185
186 return new File(path + newExt);
187 }
188
189 /**
190 * The target (full path) where the {@link Story} related to this
191 * {@link MetaData} should be located on disk for a new {@link Story}.
192 *
193 * @param key
194 * the {@link Story} {@link MetaData}
195 *
196 * @return the target
197 */
198 private File getExpectedFile(MetaData key) {
199 String title = key.getTitle();
200 if (title == null) {
201 title = "";
202 }
203 title = title.replaceAll("[^a-zA-Z0-9._+-]", "_");
204 return new File(getExpectedDir(key.getSource()), key.getLuid() + "_"
205 + title);
206 }
207
208 /**
209 * The directory (full path) where the new {@link Story} related to this
210 * {@link MetaData} should be located on disk.
211 *
212 * @param type
213 * the type (source)
214 *
215 * @return the target directory
216 */
217 private File getExpectedDir(String type) {
218 String source = type.replaceAll("[^a-zA-Z0-9._+-]", "_");
219 return new File(baseDir, source);
220 }
221
222 /**
223 * Return the list of files/directories on disk for this {@link Story}.
224 * <p>
225 * If the {@link Story} is not found, and empty list is returned.
226 *
227 * @param luid
228 * the {@link Story} LUID
229 *
230 * @return the list of {@link File}s
231 *
232 * @throws IOException
233 * if the {@link Story} was not found
234 */
235 private List<File> getRelatedFiles(String luid) throws IOException {
236 List<File> files = new ArrayList<File>();
237
238 MetaData meta = getInfo(luid);
239 if (meta == null) {
240 throw new IOException("Story not found: " + luid);
211f7ddb 241 }
68e2c6d2 242
211f7ddb
NR
243 File infoFile = getStories(null).get(meta)[0];
244 File targetFile = getStories(null).get(meta)[1];
68e2c6d2 245
211f7ddb
NR
246 files.add(infoFile);
247 files.add(targetFile);
68e2c6d2 248
211f7ddb
NR
249 String readerExt = getOutputType(meta).getDefaultExtension(true);
250 String fileExt = getOutputType(meta).getDefaultExtension(false);
68e2c6d2 251
211f7ddb
NR
252 String path = targetFile.getAbsolutePath();
253 if (readerExt != null && !readerExt.equals(fileExt)) {
254 path = path.substring(0, path.length() - readerExt.length())
255 + fileExt;
256 File relatedFile = new File(path);
68e2c6d2 257
211f7ddb
NR
258 if (relatedFile.exists()) {
259 files.add(relatedFile);
68e2c6d2 260 }
211f7ddb 261 }
68e2c6d2 262
211f7ddb
NR
263 String coverExt = "."
264 + Instance.getConfig().getString(Config.IMAGE_FORMAT_COVER);
265 File coverFile = new File(path + coverExt);
266 if (!coverFile.exists()) {
267 coverFile = new File(path.substring(0,
268 path.length() - fileExt.length())
269 + coverExt);
270 }
271
272 if (coverFile.exists()) {
273 files.add(coverFile);
68e2c6d2
NR
274 }
275
276 return files;
277 }
278
279 /**
280 * Fill the list of stories by reading the content of the local directory
281 * {@link LocalLibrary#baseDir}.
282 * <p>
283 * Will use a cached list when possible (see
284 * {@link BasicLibrary#clearCache()}).
285 *
286 * @param pg
287 * the optional {@link Progress}
288 *
289 * @return the list of stories
290 */
291 private synchronized Map<MetaData, File[]> getStories(Progress pg) {
292 if (pg == null) {
293 pg = new Progress();
294 } else {
295 pg.setMinMax(0, 100);
296 }
297
298 if (stories == null) {
299 stories = new HashMap<MetaData, File[]>();
300
301 lastId = 0;
302
303 File[] dirs = baseDir.listFiles(new FileFilter() {
211f7ddb 304 @Override
68e2c6d2
NR
305 public boolean accept(File file) {
306 return file != null && file.isDirectory();
307 }
308 });
309
310 Progress pgDirs = new Progress(0, 100 * dirs.length);
311 pg.addProgress(pgDirs, 100);
312
313 for (File dir : dirs) {
314 File[] infoFiles = dir.listFiles(new FileFilter() {
211f7ddb 315 @Override
68e2c6d2
NR
316 public boolean accept(File file) {
317 return file != null
318 && file.getPath().toLowerCase()
319 .endsWith(".info");
320 }
321 });
322
323 Progress pgFiles = new Progress(0, infoFiles.length);
324 pgDirs.addProgress(pgFiles, 100);
325 pgDirs.setName("Loading from: " + dir.getName());
326
327 for (File infoFile : infoFiles) {
328 pgFiles.setName(infoFile.getName());
329 try {
330 MetaData meta = InfoReader.readMeta(infoFile, false);
331 try {
332 int id = Integer.parseInt(meta.getLuid());
333 if (id > lastId) {
334 lastId = id;
335 }
336
337 stories.put(meta, new File[] { infoFile,
338 getTargetFile(meta, infoFile) });
339 } catch (Exception e) {
340 // not normal!!
341 throw new IOException(
342 "Cannot understand the LUID of "
343 + infoFile
344 + ": "
345 + (meta == null ? "[meta is NULL]"
346 : meta.getLuid()), e);
347 }
348 } catch (IOException e) {
349 // We should not have not-supported files in the
350 // library
351 Instance.syserr(new IOException(
352 "Cannot load file from library: " + infoFile, e));
353 }
354 pgFiles.add(1);
355 }
356
357 pgFiles.setName(null);
358 }
359
360 pgDirs.setName("Loading directories");
361 }
362
363 return stories;
364 }
365}