1 package be
.nikiroo
.fanfix
;
4 import java
.io
.IOException
;
6 import java
.util
.ArrayList
;
7 import java
.util
.HashMap
;
10 import java
.util
.Map
.Entry
;
12 import be
.nikiroo
.fanfix
.bundles
.Config
;
13 import be
.nikiroo
.fanfix
.data
.MetaData
;
14 import be
.nikiroo
.fanfix
.data
.Story
;
15 import be
.nikiroo
.fanfix
.output
.BasicOutput
;
16 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
17 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
18 import be
.nikiroo
.fanfix
.supported
.BasicSupport
.SupportType
;
19 import be
.nikiroo
.fanfix
.supported
.InfoReader
;
20 import be
.nikiroo
.utils
.Progress
;
23 * Manage a library of Stories: import, export, list.
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}.
30 public class Library
{
32 private Map
<MetaData
, File
> stories
;
34 private OutputType text
;
35 private OutputType image
;
38 * Create a new {@link Library} with the given backend directory.
41 * the directory where to find the {@link Story} objects
43 * the {@link OutputType} to save the text-focused stories into
45 * the {@link OutputType} to save the images-focused stories into
47 public Library(File dir
, OutputType text
, OutputType image
) {
49 this.stories
= new HashMap
<MetaData
, File
>();
58 * List all the known types of stories.
62 public synchronized List
<String
> getTypes() {
63 List
<String
> list
= new ArrayList
<String
>();
64 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
65 String storyType
= entry
.getValue().getParentFile().getName();
66 if (!list
.contains(storyType
)) {
75 * List all the stories of the given source type in the {@link Library}, or
76 * all the stories if NULL is passed as a type.
79 * the type of story to retrieve, or NULL for all
83 public synchronized List
<MetaData
> getList(String type
) {
84 List
<MetaData
> list
= new ArrayList
<MetaData
>();
85 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
86 String storyType
= entry
.getValue().getParentFile().getName();
87 if (type
== null || type
.equalsIgnoreCase(storyType
)) {
88 list
.add(entry
.getKey());
96 * Retrieve a {@link File} corresponding to the given {@link Story}.
99 * the Library UID of the story
101 * @return the corresponding {@link Story}
103 public synchronized MetaData
getInfo(String luid
) {
105 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
106 if (luid
.equals(entry
.getKey().getLuid())) {
107 return entry
.getKey();
116 * Retrieve a {@link File} corresponding to the given {@link Story}.
119 * the Library UID of the story
121 * @return the corresponding {@link Story}
123 public synchronized File
getFile(String luid
) {
125 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
126 if (luid
.equals(entry
.getKey().getLuid())) {
127 return entry
.getValue();
136 * Retrieve a specific {@link Story}.
139 * the Library UID of the story
141 * the optional progress reporter
143 * @return the corresponding {@link Story} or NULL if not found
145 public synchronized Story
getStory(String luid
, Progress pg
) {
147 for (Entry
<MetaData
, File
> entry
: getStories().entrySet()) {
148 if (luid
.equals(entry
.getKey().getLuid())) {
150 SupportType type
= SupportType
.valueOfAllOkUC(entry
151 .getKey().getType());
152 URL url
= entry
.getValue().toURI().toURL();
154 return BasicSupport
.getSupport(type
).process(url
,
157 throw new IOException("Unknown type: "
158 + entry
.getKey().getType());
160 } catch (IOException e
) {
161 // We should not have not-supported files in the
163 Instance
.syserr(new IOException(
164 "Cannot load file from library: "
165 + entry
.getValue().getPath(), e
));
180 * Import the {@link Story} at the given {@link URL} into the
184 * the {@link URL} to import
186 * the optional progress reporter
188 * @return the imported {@link Story}
190 * @throws IOException
191 * in case of I/O error
193 public Story
imprt(URL url
, Progress pg
) throws IOException
{
194 BasicSupport support
= BasicSupport
.getSupport(url
);
195 if (support
== null) {
196 throw new IOException("URL not supported: " + url
.toString());
199 return save(support
.process(url
, pg
), null);
203 * Export the {@link Story} to the given target in the given format.
206 * the {@link Story} ID
208 * the {@link OutputType} to transform it to
210 * the target to save to
212 * the optional progress reporter
214 * @return the saved resource (the main saved {@link File})
216 * @throws IOException
217 * in case of I/O error
219 public File
export(String luid
, OutputType type
, String target
, Progress pg
)
221 Progress pgGetStory
= new Progress();
222 Progress pgOut
= new Progress();
225 pg
.addProgress(pgGetStory
, 1);
226 pg
.addProgress(pgOut
, 1);
229 BasicOutput out
= BasicOutput
.getOutput(type
, true);
231 throw new IOException("Output type not supported: " + type
);
234 Story story
= getStory(luid
, pgGetStory
);
236 throw new IOException("Cannot find story to export: " + luid
);
239 return out
.process(story
, target
, pgOut
);
243 * Save a {@link Story} to the {@link Library}.
246 * the {@link Story} to save
248 * the optional progress reporter
250 * @return the same {@link Story}, whose LUID may have changed
252 * @throws IOException
253 * in case of I/O error
255 public Story
save(Story story
, Progress pg
) throws IOException
{
256 return save(story
, null, pg
);
260 * Save a {@link Story} to the {@link Library} -- the LUID <b>must</b> be
261 * correct, or NULL to get the next free one.
264 * the {@link Story} to save
266 * the <b>correct</b> LUID or NULL to get the next free one
268 * the optional progress reporter
270 * @return the same {@link Story}, whose LUID may have changed
272 * @throws IOException
273 * in case of I/O error
275 public synchronized Story
save(Story story
, String luid
, Progress pg
)
277 // Do not change the original metadata, but change the original story
278 MetaData key
= story
.getMeta().clone();
281 if (luid
== null || luid
.isEmpty()) {
282 getStories(); // refresh lastId if needed
283 key
.setLuid(String
.format("%03d", (++lastId
)));
288 getDir(key
).mkdirs();
289 if (!getDir(key
).exists()) {
290 throw new IOException("Cannot create library dir");
294 if (key
!= null && key
.isImageDocument()) {
300 BasicOutput it
= BasicOutput
.getOutput(out
, true);
301 it
.process(story
, getFile(key
).getPath(), pg
);
310 * Delete the given {@link Story} from this {@link Library}.
313 * the LUID of the target {@link Story}
315 * @return TRUE if it was deleted
317 public synchronized boolean delete(String luid
) {
320 MetaData meta
= getInfo(luid
);
321 File file
= getStories().get(meta
);
325 String newExt
= getOutputType(meta
).getDefaultExtension(false);
327 String path
= file
.getAbsolutePath();
328 File infoFile
= new File(path
+ ".info");
329 if (!infoFile
.exists()) {
330 infoFile
= new File(path
.substring(0, path
.length()
336 String coverExt
= "."
337 + Instance
.getConfig().getString(
338 Config
.IMAGE_FORMAT_COVER
);
339 File coverFile
= new File(path
+ coverExt
);
340 if (!coverFile
.exists()) {
341 coverFile
= new File(path
.substring(0, path
.length()
357 * The directory (full path) where the {@link Story} related to this
358 * {@link MetaData} should be located on disk.
361 * the {@link Story} {@link MetaData}
363 * @return the target directory
365 private File
getDir(MetaData key
) {
366 String source
= key
.getSource().replaceAll("[^a-zA-Z0-9._+-]", "_");
367 return new File(baseDir
, source
);
371 * The target (full path) where the {@link Story} related to this
372 * {@link MetaData} should be located on disk.
375 * the {@link Story} {@link MetaData}
379 private File
getFile(MetaData key
) {
380 String title
= key
.getTitle().replaceAll("[^a-zA-Z0-9._+-]", "_");
381 return new File(getDir(key
), key
.getLuid() + "_" + title
);
385 * Return all the known stories in this {@link Library} object.
387 * @return the stories
389 private synchronized Map
<MetaData
, File
> getStories() {
390 if (stories
.isEmpty()) {
393 String ext
= ".info";
394 for (File dir
: baseDir
.listFiles()) {
395 if (dir
.isDirectory()) {
396 for (File file
: dir
.listFiles()) {
398 if (file
.getPath().toLowerCase().endsWith(ext
)) {
399 MetaData meta
= InfoReader
.readMeta(file
);
401 int id
= Integer
.parseInt(meta
.getLuid());
406 // Replace .info with whatever is needed:
407 String path
= file
.getPath();
408 path
= path
.substring(0, path
.length()
411 String newExt
= getOutputType(meta
)
412 .getDefaultExtension(true);
414 file
= new File(path
+ newExt
);
417 stories
.put(meta
, file
);
419 } catch (Exception e
) {
421 Instance
.syserr(new IOException(
422 "Cannot understand the LUID of "
423 + file
.getPath() + ": "
424 + meta
.getLuid(), e
));
427 } catch (IOException e
) {
428 // We should not have not-supported files in the
430 Instance
.syserr(new IOException(
431 "Cannot load file from library: "
432 + file
.getPath(), e
));
443 * Return the {@link OutputType} for this {@link Story}.
446 * the {@link Story} {@link MetaData}
450 private OutputType
getOutputType(MetaData meta
) {
451 if (meta
!= null && meta
.isImageDocument()) {